【问题标题】:Issues passing string parameters from C to a Fortran subroutine将字符串参数从 C 传递到 Fortran 子例程的问题
【发布时间】:2018-01-26 16:15:51
【问题描述】:

我正在开发一个通过命令行接收一些用户参数的Linux程序。

用户界面是用 C 语言制作的,数据处理是用 Fortran 语言制作的,所以 C 语言的 main 函数将一些参数传递给 Fortran 子程序。其中一些参数是字符串,我遇到了问题。 以下是部分代码:

C 主函数:

extern void anaconv_(char *FileName,int *n,char *PlanName,int *m,char *TipoDados,char *FrontName,int *l,char *Dirdat, int *h, bool *kvuge, bool *ch_serie, bool *ch_shuntb, bool *simulacao);
void ajuda_anaconv(void);

int main (int argc, char **argv)
{
  int  i;
  int  n,m,l,h;
  bool flagk     = 0;
  bool flags     = 0;
  bool flagp     = 0;
  bool flagsimul = 0;
  char *dirpwf     = malloc(sizeof(char)*80);
  char *dirbar     = malloc(sizeof(char)*80);
  char *dirfro     = malloc(sizeof(char)*80);
  char *dirdat     = malloc(sizeof(char)*500);
  char *tpdados    = malloc(sizeof(char));
  ...
  dirpwf  = argv[i+1];
  i++;
  ...
  dirbar  = argv[i+1];
  i++;
  ...
  dirfro  = argv[i+1];
  i++;
  ...
  dirdat  = argv[i+1];
  i++;
  ...
  tpdados = argv[i+1];
  i++;
  ...
  n = strlen(dirpwf)
  m = strlen(dirbar)
  l = strlen(dirfro)
  h = strlen(dirdat)
  ...
  anaconv_(dirpwf,&n,dirbar,&m,tpdados,dirfro,&l,dirdat,&h,&flagk,&flags,&flagp,&flagsimul);
  ...
  free(dirpwf);
  free(dirbar);
  free(dirfro);
  free(dirdat);
  free(dirdatcomp);
  free(tpdados);
  ...
  return 1;
}

而他们,Fortran 子程序类似于:

SUBROUTINE ANACONV
 +(FileName,FileNameSize,PlanName,PlanNameSize,TpData,FrontName,FrontNameSize,Dirdat,DirdatSize,kVUGE,CHSERIE,CHSHUNTB,SIMUL)

    IMPLICIT NONE
    CHARACTER*80  FileName,PlanName,FrontName
    CHARACTER*500 Dirdat        
    INTEGER*4     FileNameSize,PlanNameSize,FrontNameSize,DirdatSize
    CHARACTER*1   TpData
    LOGICAL*1     kVUGE,CHSERIE,CHSHUNTB,SIMUL
    ...
END SUBROUTINE ANACONV

编译器是 gcc 版本 4.8.5 20150623,我在 CentOS Linux 版本 7.3.1611 中运行该程序。

虽然代码编译没有问题,但当我运行程序传递一些参数时,我收到以下消息:

Fortran runtime error: Actual string length is shorter than the declared one for dummy argument 'filename' (-136420716/80)

我试图用gdb调试找到问题,但我找不到错误。 FileName 字符串从 C 中 main 函数的 dirpwf 指针获取正确的值,并且存储在 FileNameSize 变量中的字符串大小也是正确的,而 Fortran 理解接收到的大小(显然是 -136420716)是低于预期 (80 )。

我该如何解决这个问题?

【问题讨论】:

  • FileName 的长度为 80,与 FileNameSize 的值无关。
  • 您是否可以随意更改 Fortran 子例程,或者您是否需要使用它并让 C 很好地发挥作用?如果您可以更改 Fortran,那么 C 互操作性工具将解决您的问题(可能)。 fortran-iso-c-binding wiki 中有一些细节。
  • 您的 Fortran 子程序源可能是固定格式的,因此您需要注意这里的格式。
  • 例如可以看到this question
  • 如果char *dirdat = malloc(sizeof(char)*500); 中的malloc() 后面跟着dirdat = argv[i+1];,则它没有意义。你所做的只是泄漏内存。而sizeof(char) 根据定义始终是一。

标签: c linux string fortran


【解决方案1】:

Fortran 和 C 以不同的方式跟踪字符串长度。 C 在末尾使用 NUL 字符作为终止符,而 Fortran 单独保留长度。 Fortran 如何做到这一点取决于实现。对于调用 Fortran 过程,大多数编译器在命名参数之后查找作为附加参数传递的长度,作为按值传递的地址大小的整数。但这不是通用的,也不是便携的。您已经传递了这些长度,但是在字符串地址之后立即这样做了 - 这不是 gfortran 想要它们的地方。

Fortran 2003 引入了 C 互操作性功能,允许您以可移植的方式混合 Fortran 和 C。 Fortran 2008 对此进行了一定程度的扩展,而 Fortran 2018 对其进行了更多扩展。不幸的是,将字符串从 C 传递到 Fortran 仍然很混乱,即使在 F2018 中也是如此。

人们可能会认为简单的解决方案是将BIND(C) 添加到 Fortran 子例程标头 - 这告诉 Fortran 不使用隐藏参数等。但是,您使用 CHARACTER*80 等是不允许的。

最简单的解决方法是在 C 调用中将 n、m、l 和 h 移动到参数列表的末尾,删除大小的 Fortran 参数,并使 n、m、l 和 h 为 size_t 而不是 int。另外,不要使用 & 来传递它们。这仍然是不可移植的,但它会让你现在就开始。

【讨论】:

  • 非常感谢。显然这有效,但还有另一个小问题。 FORTRAN 中的函数继续抱怨传递的参数的大小。不同之处在于它现在抱怨但使用了正确大小的 sting,例如:Fortran 运行时错误:当前字符串长度比为虚拟参数“文件名”(52/80)声明的长度短。 "FileName" 的大小实际上是 52,而 80 是 FORTRAN 中这个变量的最大大小。我正在考虑使用动态分配来解决这个问题。或者你知道我可以如何以不同的方式解决它吗?
  • 只需将声明的字符串长度从80500更改为(*),以便使用传递的字符串长度。
  • 谢谢!问题解决了!刚刚使用了“CHARACTER(LEN=*)”
  • 字符虚拟参数的显式长度绝不是一个好主意。
  • 顺便说一句,目前所有发布的 GFortran 版本都使用 int 作为隐藏字符串长度参数。在即将发布的 GCC 8 中,这已更改为 size_t。
猜你喜欢
  • 1970-01-01
  • 2013-07-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-07-08
  • 1970-01-01
  • 2017-06-11
相关资源
最近更新 更多