And*_*eak 0 fortran gfortran segmentation-fault intel-fortran
过去两天我在一个大型 Fortran 项目中调试了一个看似无意义的段错误。当我将代码移到自己的计算机上时,问题就开始了,段错误出现在代码的一部分中,而该部分代码多年来在其他几个系统上都运行良好。我最终找到了段错误的根源,但它是如此令人惊讶的意外(并且依赖于编译器),所以我决定将其发布在这里。
考虑以下 MWE:
program dafuq
implicit none
integer :: a=1
integer, parameter :: b=2
call foo(a,b)
end program dafuq
subroutine foo(a,b)
implicit none
integer, intent(inout) :: a, b
a=b !OK
b=a !causes segfault
end subroutine foo
Run Code Online (Sandbox Code Playgroud)
我可以访问两个 HPC 集群,它们与我的笔记本电脑一起允许我检查这些(有时有点旧)编译器:
事实证明,所有四个编译器都会对上述代码产生段错误,因为变量b
被声明为parameter
. 因此,在子例程中更改其值是违规的。intent
我的问题是,只有最新的 gfortran 在编译期间显示警告(即使使用 -Wall),如果我省略子例程中的规范,该警告也会消失。我怀疑在 C++ 中使用const
变量的相同设置会引发一个巨大的危险信号。
现在,为了使其更加晦涩,请考虑以下代码,其中使用数组而不是标量:
program dafuq_array
implicit none
integer :: a(2)=(/1,1/)
integer, parameter :: b(2)=(/2,2/)
call foo(a,b)
end program dafuq_array
subroutine foo(a,b)
implicit none
integer, intent(inout) :: a(2), b(2)
a=b !OK
b=a !might cause segfault
end subroutine foo
Run Code Online (Sandbox Code Playgroud)
现在,在这种情况下,最新的 gfortran 会产生段错误,而其他三个编译器则不会!(实际上,这就是为什么我之前没有遇到这个问题的原因:列表中最新的 gfortran 就是我自己计算机上的那个。) 在所有情况下,我基本上没有使用编译开关,即ifort -o mwe mwe.f
gfortran 也是如此。
尽管我找到了段错误的原因并且我有点理解它,但仍然有一些事情让我烦恼(没有双关语)。
一般来说,Fortran 函数参数仅在模块内部时才会进行类型检查。例如,如果将子例程放入模块中:
module m
public
contains
subroutine foo(a,b)
implicit none
integer, intent(inout) :: a,b
a = b
b = a
end subroutine
end module
program p
use m
implicit none
integer :: a
integer, parameter :: b = 2
a = 1
call foo(a,b)
end program
Run Code Online (Sandbox Code Playgroud)
编译出现错误:
gfortran 4.6.4
:
test.f90:35.15:
call foo(a,b)
1
Error: Non-variable expression in variable definition context (actual argument to INTENT = OUT/INOUT) at (1)
Run Code Online (Sandbox Code Playgroud)
ifort 13.0.1
:
test.f90(35): error #6638: An actual argument is an expression or constant; this is not valid since the associated dummy argument has the explicit INTENT(OUT) or INTENT(INOUT) attribute. [2]
call foo(a,b)
---------------^
compilation aborted for test.f90 (code 1)
Run Code Online (Sandbox Code Playgroud)
如果您无法将模块添加到代码中,您还可以考虑启用自动接口和警告(-gen-interfaces -warn all
在 ifort 中),以启用对不在模块中的函数的参数检查。