简体   繁体   English

与C函数链接时Fortran变量被覆盖

[英]Fortran variable getting overwritten when linked with C function

I've added a data collection routine to some legacy fortran. 我已将数据收集例程添加到一些旧版fortran中。 For ease of use, I wrote the file i/o routines in C. 为了易于使用,我用C语言编写了文件I / O例程。

I'm using gcc and gfortran. 我正在使用gcc和gfortran。

Problem: Some fortran variable names are being over written during what appears to be an innocuous call to a C function. 问题:在似乎无害地调用C函数的过程中,某些fortran变量名被覆盖。

C functions are all type void, names are all lower case, all arguments are pointers, function names all contain a trailing "_", and are called from Fortran as subroutines. C函数全部为void类型,名称全部为小写字母,所有参数均为指针,函数名称均包含尾随“ _”,并从Fortran中作为子例程调用。 I've done this before. 我以前做过 gfortran forces all Fortran symbols to lower case, and all entry points have an appended "_" to distinguish from same named C entry points. gfortran强制所有Fortran符号小写,并且所有入口点都有一个附加的“ _”以区别于相同的命名C入口点。

Here's a fragment of the C file: 这是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;
}

Here's the declaration of the Fortran variable that's getting stepped on: 这是不断加深的Fortran变量的声明:

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

Note: I originally used the volatile declaration qualifier in place of the save qualifier. 注意:我最初使用volatile声明限定符代替save限定符。 No difference. 没有不同。

Here's the call: 这是电话:

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...................

The problem occurs when I execute the call writefirstStr ('time', lunit) line. 当我执行call writefirstStr ('time', lunit)行时call writefirstStr ('time', lunit)会发生问题。

atime is a character*5 that is datatized to 'time' and explicitly null terminated by: time(5:) = char(0) . atime是一个character*5 ,它被数据化为'time',并且显式地以null终止,其终止time(5:) = char(0)为: time(5:) = char(0) Stepping through writefirststr_() shows no problems, and the correct information is written to the file. 单步执行writefirststr_()不会出现问题,并且正确的信息已写入文件。

If I jump (via gdb) to the return statement (in the fortran routine that contains the code fragment above) after the call openWrite (filename, lunit) , there is no problem. 如果在call openWrite (filename, lunit)之后跳转(通过gdb)跳转到return语句(在包含以上代码片段的fortran例程中call openWrite (filename, lunit) ,则没有问题。

Calling writeFirstSt r is what is overwriting the fortran variable DISMAT . 调用writeFirstSt r会覆盖fortran变量DISMAT I should also note that DISMAT is not in the routine making the C language call above. 我还应注意, DISMAT不在上面的C语言调用例程中。

What I have not yet tried is using the save qualifier on all Fortran variables - logistical problem due to the amount of legacy code. 我还没有尝试过在所有Fortran变量上使用save限定符-由于遗留代码量大而导致的后勤问题。

Anyone have any thoughts? 有人有什么想法吗?

Looks like a problem with the calling conventions to me. 在我看来,调用约定有问题。 You are passing a character variable. 您正在传递一个字符变量。 Fortran usually uses a hidden variable for the string length. Fortran通常使用隐藏变量作为字符串长度。

Your void writefirststr_ doesn't have such a parameter. 您的void writefirststr_没有这样的参数。

For arguments of CHARACTER type, the character length is passed as hidden argument. 对于CHARACTER类型的参数,字符长度作为隐藏参数传递。 For deferred-length strings, the value is passed by reference, otherwise by value. 对于延迟长度的字符串,该值按引用传递,否则按值传递。 The character length has the type INTEGER(kind=4). 字符长度的类型为INTEGER(kind = 4)。 Note with C binding, CHARACTER(len=1) result variables are returned according to the platform ABI and no hidden length argument is used for dummy arguments; 注意,使用C绑定时,将根据平台ABI返回CHARACTER(len = 1)结果变量,并且没有将隐藏长度参数用于伪参数; with VALUE, those variables are passed by value. 如果使用VALUE,则这些变量将按值传递。

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

For any new Fortran code I always recommend to use the modern Fortran interoperability with C ( bind(C) ) and the iso_c_binding intrinsic module. 对于任何新的Fortran代码,我始终建议使用带有C( bind(C) )和iso_c_binding内部模块的现代Fortran互操作性。

I think, that there are several problems in your code: 我认为您的代码中存在几个问题:

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

The format string should be "%*d" because you can only pass one integer as precision for an Integer. 格式字符串应为“%* d”,因为您只能传递一个整数作为Integer的精度。

The next problem is an bad documented "Feature" of Fortran. 下一个问题是记录不好的Fortran“功能”。 Fortran passes the size of a string as hidden argument at the end of all variable (and this time by value and not by pointer). Fortran在所有变量的末尾传递字符串的大小作为隐藏参数(这次是通过值而不是通过指针)。

your function should look like: 您的函数应如下所示:

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

The order of the length variables is in the same as the strings are passed. 长度变量的顺序与传递字符串的顺序相同。 The data type of the length variable depends on the compiler and the OS. length变量的数据类型取决于编译器和OS。 Pay attention in Fortran 90 and explicit interfaces. 在Fortran 90和显式接口中要注意。 It may be possible, that an explicit interface suppress this additional argument. 显式接口可能会取消此附加参数。

The next problem can occur, if you try to close a unit, without opening before. 如果您尝试关闭一个单元而不先打开,则会出现下一个问题。 You do not initialize the global array (or you did not post the code ;-) ) 您没有初始化全局数组(或者您没有发布代码;-))

Deleted the rest of this entry (thanks to Vladimir F). 删除了该条目的其余部分(感谢Vladimir F)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM