像这些消息这样的链接时错误的原因可能与链接器的更一般用途相同,而不仅仅是编译了 Fortran 程序。其中一些已包含在链接问题 https://stackoverflow.com/q/12573816/3157076关于 C++ 链接和另一个答案 https://stackoverflow.com/a/66860223/3157076这里:未能指定库,或以错误的顺序提供它们。
然而,编写 Fortran 程序时存在一些常见错误,可能会导致链接错误。
不支持的内在函数
如果子例程引用旨在引用内部子例程,那么如果编译器未提供该子例程内部函数,则可能会导致链接时错误:它被视为外部子例程。
implicit none
call unsupported_intrinsic
end
With unsupported_intrinsic
编译器不提供我们可能会看到链接错误消息,例如
undefined reference to `unsupported_intrinsic_'
如果我们使用非标准或不常见实现的内在函数,我们可以通过多种方式帮助编译器报告这一点:
implicit none
intrinsic :: my_intrinsic
call my_intrinsic
end program
If my_intrinsic
不是受支持的内在函数,那么编译器会抱怨一条有用的消息:
Error: ‘my_intrinsic’ declared INTRINSIC at (1) does not exist
我们本质上不存在这个问题功能因为我们正在使用implicit none
:
implicit none
print *, my_intrinsic()
end
Error: Function ‘my_intrinsic’ at (1) has no IMPLICIT type
通过某些编译器,我们可以使用 Fortran 2018implicit
对子例程执行相同操作的语句
implicit none (external)
call my_intrinsic
end
Error: Procedure ‘my_intrinsic’ called at (1) is not explicitly declared
注意,编译时可能需要指定编译器选项,以请求编译器支持非标准内在函数(如 gfortran 的-fdec-math
)。同样,如果您请求符合特定语言修订版,但使用后续修订版中引入的内在函数,则可能需要更改一致性请求。例如,编译
intrinsic move_alloc
end
与 gfortran 和-std=f95
:
intrinsic move_alloc
1
Error: The intrinsic ‘move_alloc’ declared INTRINSIC at (1) is not available in the current standard settings but new in Fortran 2003. Use an appropriate ‘-std=*’ option or enable ‘-fall-intrinsics’ in order to use it.
外部过程代替模块过程
正如我们可以尝试在程序中使用模块过程,但忘记将定义它的对象提供给链接器一样,我们可能会意外地告诉编译器使用外部过程(具有不同的链接符号名称)而不是模块过程:
module mod
implicit none
contains
integer function sub()
sub = 1
end function
end module
use mod, only :
implicit none
integer :: sub
print *, sub()
end
或者我们可能根本忘记使用该模块。同样,当我们错误地引用外部过程而不是引用外部过程时,我们经常会看到这种情况兄弟模块过程 https://stackoverflow.com/q/3970798/3157076.
Using implicit none (external)
当我们忘记使用模块时可以帮助我们,但这不会捕获我们显式声明该函数为外部函数的情况。我们必须小心,但是如果我们看到像这样的链接错误
undefined reference to `sub_'
那么我们应该认为我们已经引用了一个外部过程sub
而不是模块过程:没有任何“模块名称空间”的名称修改。这是我们应该寻找的强烈暗示。
错误指定的绑定标签
如果我们与 C 进行互操作,那么我们很容易错误地指定符号的链接名称。当不使用标准互操作性工具时,这非常容易,我不会费心指出这一点。如果您看到与 C 函数相关的链接错误,请仔细检查。
如果使用标准设施,仍然有可能出错。区分大小写是一种方法:链接符号名称区分大小写,但如果不是全部较小,则必须告知 Fortran 编译器这种情况:
interface
function F() bind(c)
use, intrinsic :: iso_c_binding, only : c_int
integer(c_int) :: f
end function f
end interface
print *, F()
end
告诉 Fortran 编译器向链接器询问符号f
,即使我们称之为F
这里。如果该符号确实被称为F
,我们需要明确地说:
interface
function F() bind(c, name='F')
use, intrinsic :: iso_c_binding, only : c_int
integer(c_int) :: f
end function f
end interface
print *, F()
end
如果您看到因情况而异的链接错误,请检查您的绑定标签。
这同样适用于具有绑定标签的数据对象,并且还要确保具有链接关联的任何数据对象在任何 C 定义和链接对象中具有匹配的名称。
同样,忘记指定 C 的互操作性bind(c)
意味着链接器可能会查找带有一个或两个尾部下划线的损坏名称(取决于编译器及其选项)。如果您尝试链接 C 函数cfunc
但链接器抱怨cfunc_
,检查你是否说过bind(c)
.
不提供主程序
除非另有说明,编译器通常会假设它正在编译主程序以便(使用链接器)生成可执行文件。如果我们不编译主程序,那不是我们想要的。也就是说,如果我们正在编译一个模块或外部子程序,以供以后使用:
module mod
implicit none
contains
integer function f()
f = 1
end function f
end module
subroutine s()
end subroutine s
我们可能会收到这样的消息
undefined reference to `main'
这意味着我们需要告诉编译器我们没有提供 Fortran 主程序。这通常会与-c
标志,但如果尝试构建库对象,将会有不同的选项。在这种情况下,编译器文档将提供适当的选项。