Fortran'call'声明:它实际上可以调用什么?

时间:2012-11-01 14:25:28

标签: fortran fortran90 subroutine fortran77

我继承了大约400行非常古怪的Fortran 77代码,我正在逐步分析它,以便在我的脑海中清楚地表达它。

无论如何,我有一个类似头文件(实际上是一个.h,但其中的代码是在fortran而不是C / C ++中),其中有两个语句,名为getarg.h

character*80 serie
integer ln

然后我有另一个名为.f的fortran文件(getserie.h),其中包含此代码:

subroutine getserie(serie, ln)
include 'getarg.h'
call getarg(1, serie)
ln = index(serie, ' ') - 1
return
end

我的问题是:我可以call一个只包含变量声明的外部文件吗?这样做的效果是什么?

3 个答案:

答案 0 :(得分:6)

不,你只能调用子程序。这意味着指定为subroutine的子程序。但是,子例程的定义不必在源文件中。它只需在链接时提供。

getarg子例程可能是编译器gets the command line arguments的内在子例程。这意味着编译器会自动向链接器提供子例程的代码。

不会以任何方式调用文件getarg.h。它的内容只是直接复制到include语句的位置。

在某些情况下,您需要使用被调用子例程的(显式)接口,但在以后的Fortran版本中,90及更高版本。在这些现代版本中,您通常将子例程和函数放在模块中,以便编译器可以检查您是否正确调用它们。

答案 1 :(得分:1)

大多数FORTRAN 实现的一个特殊功能CALL几乎可以调用任何外部符号。当然,只有调用SUBROUTINE才有意义。这是可能的,因为众所周知的FORTRAN调用约定 - 所有实际参数都是通过地址传递的,即所有实际传递给子例程的参数都是相同的类型 - 它们都是指针。甚至常量也通过地址传递 - 包含常量值的内存位置的地址。这允许编译器生成对任何子例程的调用,而无需提供显式原型。它只是将所有参数指针推送到堆栈上(在具有基于堆栈的调用约定的平台上)或将它们加载到相应的寄存器中(在具有寄存器调用约定的平台上)并向符号的地址发出汇编call指令,用作CALL语句中的子例程名称。如果在链接时存在这样的符号,链接器将生成可执行文件,否则它将抱怨未解析的符号引用,并且不会生成可执行文件。

函数调用也是如此。编译器用于从数组索引操作中告诉函数调用的唯一特性是(非内部)函数声明为EXTERNAL。特殊EXTERNAL指定是必要的,因为调用函数和访问数组元素的语法是相同的。还有一些特殊的预定义函数,称为内部函数,它们由编译器内部识别(例如SIN)。还有一些内在的子程序,但大多数只是库子程序。

因此,每当遇到CALL foo(...)语句并且您不知道foo是什么时,您应该查阅编译器手册以查看是否存在此名称的内在函数。如果没有,它可能在代码中的某处定义 - 寻找SUBROUTINE foo。如果没有,它应该是来自外部库的导出子例程,链接到程序中,或者只是语法错误。

这一切都非常容易出错,因为人们可以很容易地提供与子程序所期望的完全不同类型的实际参数,或者甚至可以提供不同数量的实际参数,编译器仍然可以愉快地编译码。当然,这样的程序通常在运行时崩溃或产生无意义的结果。这就是为什么开发像flint这样的特殊程序,以便检查编译器无法检测到的问题的源代码(例如参数类型不匹配,实际参数的数量错误等)Fortran 90修复此问题通过引入接口。接口在某些情况下是必需的,但通常可用于提供编译时参数检查。

答案 2 :(得分:0)

如上所述,这里唯一奇怪的是包含文件名称的可疑选择。 (.h扩展名和内在符号名称的无关使用)。 (是的,我知道getarg不是迂腐标准的f77,而是延伸)

将变量声明放在包含文件中是(/是)常见做法,以确保程序单元之间的声明一致。毫无疑问,该文件也被“包含”在调用程序中,并确保您的字符串在任何地方都以相同的长度声明。

在这个例子中说这肯定是不必要的。您可以将字符串声明为假定长度,因此请替换include:

character serie*(*)
integer ln

以及主程序中的正常声明并摆脱包含文件的混乱。

(现在有人会告诉我上个世纪早期的一些编译器不支持假设的长度字符串声明)..

相关问题