jac*_*ack 6 fortran linker-errors unresolved-external undefined-reference
我正在尝试构建 Fortran 程序,但收到有关未定义引用或未解析的外部符号的错误。我看到了有关这些错误的另一个问题,但那里的答案大多特定于 C++。
使用 Fortran 编写时出现这些错误的常见原因是什么?如何修复/预防它们?
这是构建 Fortran 程序时出现的一系列错误的典型问题。如果您被推荐到这里,或者您的问题作为与此问题重复的问题而被关闭,您可能需要阅读几个答案中的一个或多个。从这个答案开始,它充当所提供解决方案的目录。
像这些消息这样的链接时错误的原因可能与链接器的更一般用途相同,而不仅仅是编译了 Fortran 程序。其中一些内容包含在有关 C++ 链接的链接问题和此处的另一个答案中:未能指定库,或以错误的顺序提供它们。
\n然而,编写 Fortran 程序时存在一些常见错误,可能会导致链接错误。
\n如果子例程引用旨在引用内部子例程,那么如果编译器未提供该子例程内部函数,则可能会导致链接时错误:它被视为外部子例程。
\n implicit none\n call unsupported_intrinsic\nend\nRun Code Online (Sandbox Code Playgroud)\n如果unsupported_intrinsic编译器没有提供,我们可能会看到类似的链接错误消息
undefined reference to `unsupported_intrinsic_\'\nRun Code Online (Sandbox Code Playgroud)\n如果我们使用非标准或不常见实现的内在函数,我们可以通过多种方式帮助编译器报告这一点:
\n implicit none\n intrinsic :: my_intrinsic\n call my_intrinsic\nend program\nRun Code Online (Sandbox Code Playgroud)\n如果my_intrinsic不支持内在的,那么编译器会抱怨一条有用的消息:
Error: \xe2\x80\x98my_intrinsic\xe2\x80\x99 declared INTRINSIC at (1) does not exist\nRun Code Online (Sandbox Code Playgroud)\n我们的内在函数不存在这个问题,因为我们使用的是implicit none:
implicit none\n print *, my_intrinsic()\nend\nRun Code Online (Sandbox Code Playgroud)\nError: Function \xe2\x80\x98my_intrinsic\xe2\x80\x99 at (1) has no IMPLICIT type\nRun Code Online (Sandbox Code Playgroud)\n对于某些编译器,我们可以使用 Fortran 2018implicit语句对子例程执行相同的操作
implicit none (external)\n call my_intrinsic\nend\nRun Code Online (Sandbox Code Playgroud)\nError: Procedure \xe2\x80\x98my_intrinsic\xe2\x80\x99 called at (1) is not explicitly declared\nRun Code Online (Sandbox Code Playgroud)\n请注意,在编译时可能需要指定编译器选项,以请求编译器支持非标准内在函数(例如 gfortran\'s -fdec-math)。同样,如果您请求符合特定语言修订版,但使用后续修订版中引入的内在函数,则可能需要更改一致性请求。例如,编译
intrinsic move_alloc\nend\nRun Code Online (Sandbox Code Playgroud)\n使用 gfortran 和-std=f95:
\n intrinsic move_alloc\n 1\nError: The intrinsic \xe2\x80\x98move_alloc\xe2\x80\x99 declared INTRINSIC at (1) is not available in the current standard settings but new in Fortran 2003. Use an appropriate \xe2\x80\x98-std=*\xe2\x80\x99 option or enable \xe2\x80\x98-fall-intrinsics\xe2\x80\x99 in order to use it.\nRun Code Online (Sandbox Code Playgroud)\n正如我们可以尝试在程序中使用模块过程,但忘记将定义它的对象提供给链接器一样,我们可能会意外地告诉编译器使用外部过程(具有不同的链接符号名称)而不是模块过程:
\nmodule mod\n implicit none\ncontains\n integer function sub()\n sub = 1\n end function\nend module\n\n use mod, only :\n implicit none\n\n integer :: sub\n\n print *, sub()\n\nend\nRun Code Online (Sandbox Code Playgroud)\n或者我们可能根本忘记使用该模块。同样,当我们错误地引用外部过程而不是同级模块过程时,我们经常会看到这种情况。
\n当我们忘记使用模块时, Usingimplicit none (external)可以帮助我们,但这不会捕获我们显式声明该函数为外部函数的情况。我们必须小心,但是如果我们看到像这样的链接错误
undefined reference to `sub_\'\nRun Code Online (Sandbox Code Playgroud)\n那么我们应该认为我们引用了外部过程sub而不是模块过程:不存在任何“模块命名空间”的名称修改。这是我们应该寻找的强烈暗示。
如果我们与 C 进行互操作,那么我们很容易错误地指定符号的链接名称。当不使用标准互操作性工具时,这非常容易,我不会费心指出这一点。如果您看到与 C 函数相关的链接错误,请仔细检查。
\n如果使用标准设施,仍然有可能出错。区分大小写是一种方法:链接符号名称区分大小写,但如果不是全部小写,则必须告知 Fortran 编译器这种情况:
\n interface\n function F() bind(c)\n use, intrinsic :: iso_c_binding, only : c_int\n integer(c_int) :: f\n end function f\n end interface\n\n print *, F()\nend\nRun Code Online (Sandbox Code Playgroud)\n告诉 Fortran 编译器向链接器询问符号f,即使我们F在这里调用了它。如果该符号确实被称为F,我们需要明确地说:
interface\n function F() bind(c, name=\'F\')\n use, intrinsic :: iso_c_binding, only : c_int\n integer(c_int) :: f\n end function f\n end interface\n\n print *, F()\nend\nRun Code Online (Sandbox Code Playgroud)\n如果您看到因情况而异的链接错误,请检查您的绑定标签。
\n这同样适用于具有绑定标签的数据对象,并且还要确保具有链接关联的任何数据对象在任何 C 定义和链接对象中具有匹配的名称。
\n同样,忘记指定 C 互操作性bind(c)意味着链接器可能会查找带有一个或两个尾部下划线的损坏名称(取决于编译器及其选项)。如果您尝试链接 C 函数cfunc但链接器抱怨cfunc_,请检查您是否说过bind(c)。
除非另有说明,编译器通常会假设它正在编译主程序以便(使用链接器)生成可执行文件。如果我们不编译主程序,那不是我们想要的。也就是说,如果我们正在编译一个模块或外部子程序,以供以后使用:
\nmodule mod\n implicit none\ncontains\n integer function f()\n f = 1\n end function f\nend module\n\nsubroutine s()\nend subroutine s\nRun Code Online (Sandbox Code Playgroud)\n我们可能会收到这样的消息
\nundefined reference to `main\'\nRun Code Online (Sandbox Code Playgroud)\n这意味着我们需要告诉编译器我们没有提供 Fortran 主程序。这通常与-c标志一起使用,但如果尝试构建库对象,则会有不同的选项。在这种情况下,编译器文档将提供适当的选项。
undefined reference/错误的最常见原因unresolved external symbol是无法链接提供符号的库(通常是函数或子例程)。
例如,当DGEMM使用来自 BLAS 库的子例程时,必须在链接步骤中使用提供该子例程的库。
在最简单的用例中,链接与编译相结合:
gfortran my_source.f90 -lblas
Run Code Online (Sandbox Code Playgroud)
告诉-lblas链接器(这里由编译器调用)链接库libblas。它可以是动态库(.so、.dll)或静态库(.a、.lib)。
在许多情况下,有必要在请求子例程的对象之后提供定义子例程的库对象。因此,上面的链接可能会成功,而切换命令行选项 ( gfortran -lblas my_source.f90) 可能会失败。
请注意,库的名称可能不同,因为 BLAS 有多种实现(MKL、OpenBLAS、GotoBLAS,...)。但它总是会被缩短为lib...到 ,l...如liopenblas.so和中那样-lopenblas。
如果库位于链接器看不到的位置,您可以使用该-L标志显式添加链接器要考虑的目录,例如:
gfortran -L/usr/local/lib -lopenblas
Run Code Online (Sandbox Code Playgroud)
您还可以尝试将路径添加到链接器搜索的某些环境变量中,例如LIBRARY_PATH,例如:
export LIBRARY_PATH=$LIBRARY_PATH:/usr/local/lib
Run Code Online (Sandbox Code Playgroud)
当链接和编译分开时,库在链接步骤中链接:
gfortran -c my_source.f90 -o my_source.o
gfortran my_source.o -lblas
Run Code Online (Sandbox Code Playgroud)