通过"参数"传递分段错误到子程序

时间:2015-06-30 23:57:45

标签: segmentation-fault fortran gfortran 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

我可以访问两个HPC群集,它与我的笔记本电脑一起让我可以检查这些(有时候有些旧)编译器:

  • ifort 11.1
  • gfortran 4.1.2
  • gfortran 4.4.7
  • gfortran 4.8.4(Ubuntu 14.04回购中的最新版本)

事实证明,所有四个编译器都使用上面的代码产生段错误,因为变量b被声明为parameter。因此,在子例程中更改其值是违规的。我的问题是,只有最新的gfortran在编译期间显示警告(即使使用-Wall),如果省略子例程中的intent规范,那也会消失。我怀疑使用const变量在C ++中使用相同的设置会引发一个巨大的红旗。

现在,为了使其更加模糊,请考虑以下代码,使用数组而不是标量:

  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

现在,在这种情况下,最新的gfortran产生了一个段错误,而其他三个编译器都没有! (实际上这就是为什么我之前没有遇到过这个问题的原因:列表中最新的gfortran就是我自己计算机上的那个。)在所有情况下,我基本上都没有使用编译开关,即ifort -o mwe mwe.f和gfortran也一样。

即使我找到了段错误的原因并且我理解它,仍然有一些事情让我感到烦恼(没有双关语意)。

  1. 在这种情况下期待编译错误/警告我错了吗?或至少超出"无效内存引用"。
  2. 的运行时错误
  3. 使用数组是否有意为某些编译器避免此错误?
  4. 我是否认为在不同系统上遇到的不同行为是由于编译器的差异,还是因为系统特定的更微妙?

2 个答案:

答案 0 :(得分:4)

一般情况下,只有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

编译会给出错误:

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)

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)

如果您无法在代码中添加模块,您还可以考虑启用自动接口和警告(ifort中的-gen-interfaces -warn all)以启用不在模块中的函数的参数检查。

答案 1 :(得分:2)

希望发生错误并不是错误的(尽管你不能指望这种情况)并且GNU Fortran 5.1.0会为你的第二个测试用例提供警告。

dafuq.f90:6:13:

   call foo(a,b)
             1
Warning: Named constant ‘b’ in variable definition context (actual argument to INTENT = OUT/INOUT) at (1)

另请注意,虽然有些编译器工作正常,但有些编译器没有编译,有些段错误,有些编译工作的数组不能使用标量,这些都是合理的结果。一旦违反标准,您的代码就不再是Fortran,未定义的行为意味着任何结果都是正确的。