检测未初始化的数组
Detecting uninitialized arrays
在下面的程序中,set_int_array
和 set_real_array
这两个例程为虚拟数组定义了一些值,但是假设 a(:)
和 r(:)
的初始化(行1 和 2) 被无意中遗漏了(即,在正确的程序中,这两行都应该取消注释)。我的问题是如何使用某些编译器选项自动检测这些错误。
module mymod
implicit none
contains
subroutine set_int_array ( a )
integer, intent(out) :: a(:)
integer k
! a(:) = 10 !! (1)
do k = 1, size(a)
a(k) = a(k) + k
enddo
end subroutine
subroutine set_real_array ( r )
real, intent(out) :: r(:)
integer k
! r(:) = 10.0 !! (2)
do k = 1, size(r)
r(k) = r(k) + k
enddo
end subroutine
end module
program main
use mymod
implicit none
integer :: a(5)
real :: r(5)
call set_int_array ( a ) ; print *, a(:)
call set_real_array ( r ) ; print *, r(:)
end program
为此,我首先尝试了 gfortran4.8 的以下选项(在 Linux x86_64 上):
gfortran -Wall -fcheck=all test.f90
但这未能检测到丢失的初始化并给出了不正确的结果
1 2 268435459 32730 207
1.0000000 2.0000000 3.0000000 4.0000000 5.0000000
所以我尝试了以下选项来初始化 r(:)
和 NaN
:
gfortran -finit-real=snan -ffpe-trap=invalid test.f90
成功捕获了 set_real_array
的错误,但未捕获 set_int_array
的错误
-1098847551 59 -1034862589 32608 7941
Program received signal 8 (SIGFPE): Floating-point exception.
Backtrace for this error:
#0 0x00000039becac5f4 in wait () from /lib64/libc.so.6
#1 0x00000039c501400d in ?? () from /usr/lib64/libgfortran.so.3
#2 0x00000039c501582e in ?? () from /usr/lib64/libgfortran.so.3
#3 0x00000039c50146ca in ?? () from /usr/lib64/libgfortran.so.3
#4 <signal handler called>
#5 0x0000000000400bd3 in __mymod_MOD_set_real_array ()
#6 0x0000000000400e69 in MAIN__ ()
#7 0x0000000000400f52 in main ()
因此,我也尝试了带有以下选项的 ifort14.0
ifort -check all test.f90
ifort -check uninit test.f90
但两者都未能检测到错误。事实上,check uninit
选项的手册页说
Only local scalar variables of intrinsic type INTEGER, REAL, COMPLEX, and LOGICAL without the SAVE attribute are checked.
那么没有检测到错误可能是自然的。所以我的问题是:是否有任何方法可以自动捕获 a(:)
和 r(:)
的错误?
[ 编辑 ] 我提出这个问题的动机来自 在 SO 上,OP 很难找到错误。原来问题出在evaluate
模块中curve_derivs_alg1()
中使用了一个未初始化的数组,但我也很难找到错误的确切位置(尽管它可能会出现一旦被发现就很简单了!)。 -finit-real-snan -ffpe-trap=invalid
选项在这种情况下有效,但如果整数数组是问题所在,它应该变得更加困难......所以我想知道是否有一些方便的选项或方法来跟踪使用未初始化的第一个位置数组。
如果您在 Linux,您可以使用 valgrind
完成此任务。如果你发出
valgrind --track-origins=yes ./a.out
你会得到很多输出 ;-) 第一个警告实际上指向未初始化的变量:
==4426== Memcheck, a memory error detector
==4426== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==4426== Using Valgrind-3.11.0 and LibVEX; rerun with -h for copyright info
==4426== Command: ./a.out
==4426==
==4426== Conditional jump or move depends on uninitialised value(s)
==4426== at 0x4F29774: ??? (in /usr/lib64/libgfortran.so.3.0.0)
==4426== by 0x4F2B2DE: ??? (in /usr/lib64/libgfortran.so.3.0.0)
==4426== by 0x4F1F126: _gfortran_transfer_array (in /usr/lib64/libgfortran.so.3.0.0)
==4426== by 0x40098B: MAIN__ (test.F90:34)
==4426== by 0x400A75: main (test.F90:28)
==4426== Uninitialised value was created by a stack allocation
==4426== at 0x4008CD: MAIN__ (test.F90:27)
为了更好地分析输出,我更喜欢每行一个语句,所以我将程序的最后几行更改为
call set_int_array ( a )
print *, a(:)
call set_real_array ( r )
print *, r(:)
然后,test.F90:34
指向
print *, a(:)
稍后,您会发现以下输出指向第二次使用未初始化变量:
==4426== Conditional jump or move depends on uninitialised value(s)
==4426== at 0x4F27BC1: ??? (in /usr/lib64/libgfortran.so.3.0.0)
==4426== by 0x4F2A6E4: ??? (in /usr/lib64/libgfortran.so.3.0.0)
==4426== by 0x4F2B29E: ??? (in /usr/lib64/libgfortran.so.3.0.0)
==4426== by 0x4F1F126: _gfortran_transfer_array (in /usr/lib64/libgfortran.so.3.0.0)
==4426== by 0x400A44: MAIN__ (test.F90:36)
==4426== by 0x400A75: main (test.F90:28)
==4426== Uninitialised value was created by a stack allocation
==4426== at 0x4008CD: MAIN__ (test.F90:27)
请注意,您需要使用调试选项编译代码以获取行号。使用 valgrind
从第一个错误开始,一个接一个地更正错误也是有益的。这是因为未定义的行为可能会导致后续的 errors/warnings 在您解决问题的原因后消失。
如果检测此类错误对您的工作很重要,您可能需要购买 NAG 编译器,它可以在运行时报告此类错误。例如,如果我将您的原始代码保存在 'test.f90' 中并编译它:
nagfor test.f90 -C=undefined -o test.exe
在运行时我得到以下信息:
Runtime Error: test.f90, line 11: Reference to undefined variable A(K)
Program terminated by fatal error
Aborted (core dumped)
如果我取消注释并修复第一个错误,也会报告第二个错误:
11 12 13 14 15
Runtime Error: test.f90, line 21: Reference to undefined variable R(K)
Program terminated by fatal error
Aborted (core dumped)
编译器通常不会执行这种检查,因为它可能很昂贵。也有一些限制。如需更多信息,请访问 http://www.nag.co.uk/nagware/np/r60_doc/nagfor.html#UNDEF
请注意,答案来自 NAG 人员。以上 link 给出了关于使用 '-C=undefined' 标志的限制的信息,这与这个问题没有直接关系,但可能有用。
在下面的程序中,set_int_array
和 set_real_array
这两个例程为虚拟数组定义了一些值,但是假设 a(:)
和 r(:)
的初始化(行1 和 2) 被无意中遗漏了(即,在正确的程序中,这两行都应该取消注释)。我的问题是如何使用某些编译器选项自动检测这些错误。
module mymod
implicit none
contains
subroutine set_int_array ( a )
integer, intent(out) :: a(:)
integer k
! a(:) = 10 !! (1)
do k = 1, size(a)
a(k) = a(k) + k
enddo
end subroutine
subroutine set_real_array ( r )
real, intent(out) :: r(:)
integer k
! r(:) = 10.0 !! (2)
do k = 1, size(r)
r(k) = r(k) + k
enddo
end subroutine
end module
program main
use mymod
implicit none
integer :: a(5)
real :: r(5)
call set_int_array ( a ) ; print *, a(:)
call set_real_array ( r ) ; print *, r(:)
end program
为此,我首先尝试了 gfortran4.8 的以下选项(在 Linux x86_64 上):
gfortran -Wall -fcheck=all test.f90
但这未能检测到丢失的初始化并给出了不正确的结果
1 2 268435459 32730 207
1.0000000 2.0000000 3.0000000 4.0000000 5.0000000
所以我尝试了以下选项来初始化 r(:)
和 NaN
:
gfortran -finit-real=snan -ffpe-trap=invalid test.f90
成功捕获了 set_real_array
的错误,但未捕获 set_int_array
-1098847551 59 -1034862589 32608 7941
Program received signal 8 (SIGFPE): Floating-point exception.
Backtrace for this error:
#0 0x00000039becac5f4 in wait () from /lib64/libc.so.6
#1 0x00000039c501400d in ?? () from /usr/lib64/libgfortran.so.3
#2 0x00000039c501582e in ?? () from /usr/lib64/libgfortran.so.3
#3 0x00000039c50146ca in ?? () from /usr/lib64/libgfortran.so.3
#4 <signal handler called>
#5 0x0000000000400bd3 in __mymod_MOD_set_real_array ()
#6 0x0000000000400e69 in MAIN__ ()
#7 0x0000000000400f52 in main ()
因此,我也尝试了带有以下选项的 ifort14.0
ifort -check all test.f90
ifort -check uninit test.f90
但两者都未能检测到错误。事实上,check uninit
选项的手册页说
Only local scalar variables of intrinsic type INTEGER, REAL, COMPLEX, and LOGICAL without the SAVE attribute are checked.
那么没有检测到错误可能是自然的。所以我的问题是:是否有任何方法可以自动捕获 a(:)
和 r(:)
的错误?
[ 编辑 ] 我提出这个问题的动机来自 evaluate
模块中curve_derivs_alg1()
中使用了一个未初始化的数组,但我也很难找到错误的确切位置(尽管它可能会出现一旦被发现就很简单了!)。 -finit-real-snan -ffpe-trap=invalid
选项在这种情况下有效,但如果整数数组是问题所在,它应该变得更加困难......所以我想知道是否有一些方便的选项或方法来跟踪使用未初始化的第一个位置数组。
如果您在 Linux,您可以使用 valgrind
完成此任务。如果你发出
valgrind --track-origins=yes ./a.out
你会得到很多输出 ;-) 第一个警告实际上指向未初始化的变量:
==4426== Memcheck, a memory error detector
==4426== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==4426== Using Valgrind-3.11.0 and LibVEX; rerun with -h for copyright info
==4426== Command: ./a.out
==4426==
==4426== Conditional jump or move depends on uninitialised value(s)
==4426== at 0x4F29774: ??? (in /usr/lib64/libgfortran.so.3.0.0)
==4426== by 0x4F2B2DE: ??? (in /usr/lib64/libgfortran.so.3.0.0)
==4426== by 0x4F1F126: _gfortran_transfer_array (in /usr/lib64/libgfortran.so.3.0.0)
==4426== by 0x40098B: MAIN__ (test.F90:34)
==4426== by 0x400A75: main (test.F90:28)
==4426== Uninitialised value was created by a stack allocation
==4426== at 0x4008CD: MAIN__ (test.F90:27)
为了更好地分析输出,我更喜欢每行一个语句,所以我将程序的最后几行更改为
call set_int_array ( a )
print *, a(:)
call set_real_array ( r )
print *, r(:)
然后,test.F90:34
指向
print *, a(:)
稍后,您会发现以下输出指向第二次使用未初始化变量:
==4426== Conditional jump or move depends on uninitialised value(s)
==4426== at 0x4F27BC1: ??? (in /usr/lib64/libgfortran.so.3.0.0)
==4426== by 0x4F2A6E4: ??? (in /usr/lib64/libgfortran.so.3.0.0)
==4426== by 0x4F2B29E: ??? (in /usr/lib64/libgfortran.so.3.0.0)
==4426== by 0x4F1F126: _gfortran_transfer_array (in /usr/lib64/libgfortran.so.3.0.0)
==4426== by 0x400A44: MAIN__ (test.F90:36)
==4426== by 0x400A75: main (test.F90:28)
==4426== Uninitialised value was created by a stack allocation
==4426== at 0x4008CD: MAIN__ (test.F90:27)
请注意,您需要使用调试选项编译代码以获取行号。使用 valgrind
从第一个错误开始,一个接一个地更正错误也是有益的。这是因为未定义的行为可能会导致后续的 errors/warnings 在您解决问题的原因后消失。
如果检测此类错误对您的工作很重要,您可能需要购买 NAG 编译器,它可以在运行时报告此类错误。例如,如果我将您的原始代码保存在 'test.f90' 中并编译它:
nagfor test.f90 -C=undefined -o test.exe
在运行时我得到以下信息:
Runtime Error: test.f90, line 11: Reference to undefined variable A(K)
Program terminated by fatal error
Aborted (core dumped)
如果我取消注释并修复第一个错误,也会报告第二个错误:
11 12 13 14 15
Runtime Error: test.f90, line 21: Reference to undefined variable R(K)
Program terminated by fatal error
Aborted (core dumped)
编译器通常不会执行这种检查,因为它可能很昂贵。也有一些限制。如需更多信息,请访问 http://www.nag.co.uk/nagware/np/r60_doc/nagfor.html#UNDEF
请注意,答案来自 NAG 人员。以上 link 给出了关于使用 '-C=undefined' 标志的限制的信息,这与这个问题没有直接关系,但可能有用。