所以,我想帮助我的研究人员调试Fortran程序,为了演示目的,我创建了一个故意导致段错误的程序。
这是来源:
program segfault
implicit none
integer :: n(10), i
integer :: ios, u
open(newunit=u, file='data.txt', status='old', action='read', iostat=ios)
if (ios /= 0) STOP "error opening file"
i = 0
do
i = i + 1
read(u, *, iostat=ios) n(i)
if (ios /= 0) exit
end do
close(u)
print*, sum(n)
end program segfault
data.txt
文件包含100个随机数:
for i in {1..100}; do
echo $RANDOM >> data.txt;
done
当我用
编译这个程序时gfortran -O3 -o segfault.exe segfault.f90
生成的可执行文件尽职尽责地崩溃。但是当我启用调试编译时:
gfortran -O0 -g -o segfault.exe segfault.f90
然后它只读取前10个值,并打印它们的总和。对于它的价值,-O2
会导致所需的段错误,-O1
不会。{/ p>
我非常关注这一点。毕竟,如果在启用调试符号的情况下编译错误,我该如何正确调试?
有人可以解释这种行为吗?
我正在使用GNU Fortran (MacPorts gcc5 5.3.0_1) 5.3.0
答案 0 :(得分:3)
段错误是undefined behaviour。该程序不符合Fortran标准,因此您不能指望任何特定结果。它可以做任何事情。你不能指出发生了一个段错误,对它没有发生时就不那么深切了。
有编译器检查(fcheck=
)和清理(-fsanitize=
)可用的原因。等待段错误不能保证有效。不是在Fortran中,不是在C中,不是在任何类似的语言中。
不合格程序的结果可能取决于许多因素,例如在内存或寄存器中放置变量。对齐内存中的变量,堆栈帧的位置......你根本无法计算任何东西。这些细节显然取决于优化级别。
如果程序访问数组越界,但内存中的地址恰好是仍属于进程的内存的一部分,则可能不会发生段错误。它只是存储器中的一些字节,允许进程读取或写入(或两者)。您可能正在覆盖其他一些变量,您可能正在从一些旧的堆栈框架中读取一些垃圾,您可能会覆盖malloc
的内部簿记数据并破坏堆。崩溃可能等待在其他地方发生,或者只是程序的数字结果会略有错误。任何事情都可能发生。