为什么此函数在模块内部声明,然后在链接器看不到的同一模块中的其他地方使用?

Jos*_*ose 3 fortran gfortran

我在看起来像这样的模块中有一个函数(以防有人感兴趣,就是这个函数

MODULE MYMODULE

    IMPLICIT NONE
    ! Some random stuff
    CONTAINS

        CHARACTER*255 FUNCTION strtok ( source_string, delimiters )
         [...]
        END FUNCTION strtok

        SUBROUTINE DO_SOMETHING ( )
           CHARACTER(LEN=255) :: strtok
           [...] ! 
        END SUBROUTINE DO_SOMETHING

END MODULE MYMODULE
Run Code Online (Sandbox Code Playgroud)

strtok函数是C的字符串标记器的一个版本,我将在DO_SOMETHING子例程中使用此函数。我需要定义strtok,否则gfortran抱怨它未定义。但是,如果我这样做了,然后编译我的代码并将其链接到主程序,则链接器会抱怨未定义对的引用strtok_。我不知道为什么会这样,因为它们都在同一个模块中并且应该可见。同一模块中的其他函数和子例程不存在此问题。这与返回字符*的功能有关吗?

F'x*_*F'x 5

在下面的内容中,我将使用下面的完整示例进行说明(您可以编译并链接该示例):

module mymodule
contains
  integer function foo ()
    foo = 1
  end function

  integer function bar ()
    integer :: foo
    bar = foo()
  end function
end module

program test
  use mymodule
  print *, bar()
end
Run Code Online (Sandbox Code Playgroud)

在函数代码中bar,声明integer :: foo严格等于:

integer, external :: foo
Run Code Online (Sandbox Code Playgroud)

因此,在的代码中bar,您明确声明:

“您可能已经有了名称的符号 foo,但是从现在开始,当我使用它时,我的意思是它是该名称的外部功能”

因此,这是有效的代码,编译器只希望您提供external名为的函数foo。因为您不这样做(模块功能不是外部的),所以它无法链接。您可以foo通过添加以下代码来提供外部功能(不在模块中,仅在同一文件的末尾):

integer function foo ()
  foo = 42
end function
Run Code Online (Sandbox Code Playgroud)

如果添加此函数主体,则代码将被编译,并且输出将为42(由于调用了外部函数,而不是模块函数)。

同样值得注意的是,如果您integer :: foo在的代码中将行注释掉bar,则symbol foo将解析为模块函数,无论您是否提供名为的外部函数,该函数都会被调用foo(因此,输出为1)。

结论:不是编译器错误,而是滥用语言的旧功能(外部声明)。老实说,我认为最好将external声明明确标记为这样,这样至少可以在此处突出显示该问题。