1

我在一些旧版 fortran 中添加了一个数据收集例程。为了便于使用,我用 C 编写了文件 i/o 例程。

我正在使用 gcc 和 gfortran。

问题:在看似无害的 C 函数调用过程中,一些 fortran 变量名称被覆盖。

C 函数都是 void 类型,名称都是小写,所有参数都是指针,函数名称都包含一个尾随“_”,并且作为子例程从 Fortran 调用。我以前做过。gfortran 强制所有 Fortran 符号为小写,并且所有入口点都附加一个“_”以区别于同名 C 入口点。

这是 C 文件的片段:

#define MAXFILES 20
FILE *outfile[MAXFILES];

/* int2char_ generates a left zero padded string (*theChar) from an int
   (*theInt), that is *numChar characters long.  E.g, called from fortran:

   string *5 arun
   integer nrun
   integer nnchar

   nrun = 231
   nchar = 4
   call int2char (nrun, arun, nnchar)
c...   returns '0231' in arun
*/
void int2char_ (int *theInt, char *theChar, int *numChar) {

  int nchar;

  nchar = *numChar;

  if (nchar > 9) nchar = 9;
  if (nchar < 1) nchar = 1;

  sprintf(theChar, "%*.*d", nchar, nchar, *theInt);

  return;
}  // end of int2char


void openwrite_ (char *filename, int *unit) {
  outfile[*unit] = fopen (filename, "w");
  return;
}  /* end of openWrite */

void closefile_ (int *unit) {
  int closed;
  if (outfile[*unit]) {
    closed = fclose (outfile[*unit]);
  }
  return;
}

void writefirststr_ (char *string, int *unit) {
  int printed;
  printed = fprintf (outfile[*unit], "%s", string);
//  printed = fputs (string, outfile[*unit]);
  return;
}

这是被踩到的 Fortran 变量的声明:

c...................
c"Display the mass matrix when DISMAT is set TRUE "
      LOGICAL, save :: DISMAT
c...................

注意:我最初使用volatile声明限定符代替save限定符。没有不同。

这是电话:

c...................
c...  build file name
          numchar = 4
          call int2char (nrun, filenumber, numchar)
          begin = 1
          end = len_trim(fileprefix)
          filename(begin:end) = fileprefix

          begin = end + 1
          end = begin + 3
          filename(begin:end) = filenumber

          begin = end + 1
          end = begin + 3
          filename(begin:end) = fileext

          begin = end + 1
          filename(begin:begin) = char(0)


c...  close open file
          call closefile (lunit)

c...  open file
          call openWrite (filename, lunit)

c...  write header(s)
          call writeFirstStr (atime', lunit)
c...................

当我执行该call writefirstStr ('time', lunit)行时会出现问题。

atime是一个character*5被数据化为 'time' 并显式地以 null 终止的:time(5:) = char(0)。单步writefirststr_()执行显示没有问题,并且正确的信息被写入文件。

如果我(通过 gdb)在 之后跳转到 return 语句(在包含上述代码片段的 fortran 例程中)call openWrite (filename, lunit),则没有问题。

调用writeFirstStr 是覆盖 fortran 变量的原因DISMAT。我还应该注意,这DISMAT不在上面进行 C 语言调用的例程中。

我还没有尝试save在所有 Fortran 变量上使用限定符——由于遗留代码的数量导致的后勤问题。

有人有什么想法吗?

4

2 回答 2

2

对我来说,调用约定似乎有问题。您正在传递一个字符变量。Fortran 通常使用隐藏变量作为字符串长度。

void writefirststr_没有这样的参数。

对于 CHARACTER 类型的参数,字符长度作为隐藏参数传递。对于延迟长度的字符串,值通过引用传递,否则通过值传递。字符长度的类型为 INTEGER(kind=4)。注意 C 绑定,CHARACTER(len=1) 结果变量根据平台 ABI 返回,并且没有隐藏长度参数用于虚拟参数;使用 VALUE,这些变量按值传递。

(来自http://gcc.gnu.org/onlinedocs/gfortran/Argument-passing-conventions.html#Argument-passing-conventions

对于任何新的 Fortran 代码,我始终建议使用现代 Fortran 与 C ( bind(C)) 和iso_c_binding内部模块的互操作性。

于 2014-02-24T19:05:22.310 回答
0

我认为,您的代码中有几个问题:

sprintf(theChar, "%*.*d", nchar, nchar, *theInt);

格式字符串应为“%*d”,因为您只能传递一个整数作为整数的精度。

下一个问题是 Fortran 的错误记录“功能”。Fortran 将字符串的大小作为隐藏参数传递到所有变量的末尾(这次是通过值而不是指针)。

您的函数应如下所示:

void int2char_ (int *theInt, char *theChar, int *numChar, long len_of_the_char);
void writefirststr_ (char *string, int *unit, long len_of_string);

长度变量的顺序与传递字符串的顺序相同。长度变量的数据类型取决于编译器和操作系统。注意 Fortran 90 和显式接口。显式接口可能会抑制此附加参数。

如果您尝试关闭一个单元,而之前没有打开,则可能会出现下一个问题。您没有初始化全局数组(或者您没有发布代码;-))

删除了该条目的其余部分(感谢 Vladimir F)。

于 2014-02-24T19:10:51.957 回答