【问题标题】:Why no runtime error when clearly writing over array bounds?为什么在明确写入数组边界时没有运行时错误?
【发布时间】:2012-03-23 14:12:24
【问题描述】:

我有一个程序,它分配了一个超出其边界的数组,我预计会抛出一个运行时错误。然而根本没有引发错误,程序继续写入未声明的内存。是否有一些编译器选项可以防止这种情况?通过显示的内存转储,很明显这种越界是真实的。有没有办法声明变量或参数规范来捕捉这个?显然,这是一个明显的案例,但是当负责维护数千行 F77 派生代码时,(对我而言)并不总是清楚是否会发生这种情况。

PROGRAM TEST_CODE
IMPLICIT NONE

INTEGER*4 :: R(5)           ! Array of 5

    CALL R_TEST(R, 10)

END PROGRAM

SUBROUTINE R_TEST(R, J)
IMPLICIT NONE

INTEGER*4, INTENT(INOUT) :: R(1)    ! Dummy is array of 1
INTEGER*4, INTENT(IN) :: J
INTEGER*4 :: K

DO K=J-5,J+5            ! K=5..15
    R(K) = K            ! No Runtime Error
END DO

END SUBROUTINE

编译器是 Intel Fortran 2011 XE,是的,我正在使用字节规范 INTEGER*4,因为我知道我得到了什么。

这里是运行时检查的编译器选项。

【问题讨论】:

    标签: fortran runtime-error range-checking


    【解决方案1】:

    intel 编译器在检查指针和可分配数组的边界方面做得非常好。如果您稍微修改您的代码(如下)并使用以下内容进行编译:

    $ ifort -O0 -debug -traceback -check -ftrapuv TEST_CODE.f90

    您将收到运行时错误。但是对于假定大小的数组,英特尔编译器无法检查边界。尤其是隐式类型的F77代码等,不容易发现内存泄漏。另一件小事,在 Fortran 中,您的程序必须做一些有意义的事情;否则编译器会忽略你的代码,因为它什么都不做!这就是我最后添加打印的原因。

    R(:) 有个小问题是编译器不能假设它在内存中是连续的;因此它不能进行一些编译器优化。那么最好使用可分配数组或使用连续属性(F2008 标准)。

    PROGRAM TEST_CODE
    IMPLICIT NONE
    
    INTEGER*4 :: R(5)           ! Array of 5
    
        CALL R_TEST(R, 10)
        print *,R
    
    END PROGRAM
    
    SUBROUTINE R_TEST(R, J)
    IMPLICIT NONE
    
    INTEGER*4, INTENT(INOUT) :: R(:)    ! Dummy is array of 1
    INTEGER*4, INTENT(IN) :: J
    INTEGER*4 :: K
    
    DO K=J-5,J+5            ! K=5..15
        R(K) = K            ! No Runtime Error
    END DO
    
    END SUBROUTINE
    

    【讨论】:

    • 感谢您的回答。如果编译器不能假定连续内存,它如何处理数组索引。不是每个数组都与前一个数组相距固定距离吗,就像 C 数组一样?
    • 不,不保证像 R(:) 这样的假定形状数组是连续的。 C 没有数组(不计算指针!)。不过,C 中有一些方法可以创建连续的数组指针(请参阅 stackoverflow.com/questions/5196318/… )。在 Fortran 中,您可以简单地对此进行测试。我进行了测试,当我使用可分配属性时,我得到了 10-15% 的性能提升。在进行混合 C/Fortran 编程时,可以将 R(:) 映射到 C 类型的指针数组。
    【解决方案2】:

    有趣。 gfortran 4.6 发现运行时下标错误:

    At line 18 of file test_code.f90
    Fortran runtime error: Index '5' of dimension 1 of array 'r' above upper bound of 1 
    

    但 ifort XE 12.1.1.246 没有。

    编辑:这是来自英特尔编译器文档的答案:“对于作为虚拟参数的数组,仅检查下限以查找上限指定为 * 或上限和下限均为 1 的维度。 "而当声明改为R(2)时,ifort也发现下标错误。

    原因是很多旧代码使用值“1”作为虚拟参数数组的大小来指示未知大小。如果您只是将参数视为地址,则此方法有效,但当然会使任何下标检查变得不可能,因为编译器不知道虚拟参数的大小。这种技术不应该在新代码中使用。 Fortran 90 提供了更好的选择,例如假定形状的数组(冒号声明)。

    所以“如果发生这种情况,(对我来说)并不总是很清楚”的答案,即当你的遗留代码没有被 ifort 检查时——寻找声明为 (1) 或 (*) 的过程参数或对于多个维度中的一个或多个维度相同。

    【讨论】:

    • 如何强制 gfortran 进行 ranfe 检查?我总是必须使用 valgrind 或 Solaris Studio。我多次查看调试选项。
    • 我使用的 gfortran 调试编译器选项是 Unix/Mac 风格的: -O2 -fimplicit-none -Wall -Wline-truncation -Wcharacter-truncation -Wsurprising -Waliasing -Wimplicit-interface -未使用的参数 -fwhole-file -fcheck=all -std=f2008 -pedantic -fbacktrace。这些可能会使用旧代码产生大量警告,因此您可能需要删除一些。
    • ranfe 可能是范围。 gfortran -fcheck=all 包含 -fcheck=bounds。
    • 谢谢,很有趣,我使用-fbacktrace -ggdb -pedantic -Wall -fcheck=all -ffpe-trap=zero,overflow,它适用于我现在尝试的简单测试用例,但我真的记得它不适用于我的大型程序。
    猜你喜欢
    • 1970-01-01
    • 2023-02-08
    • 1970-01-01
    • 2012-08-11
    • 1970-01-01
    • 2022-01-24
    • 1970-01-01
    • 2022-12-11
    • 2022-06-20
    相关资源
    最近更新 更多