在CentOS7上安装了PGI编译器,但是调试过程中遇到的“段错误(吐核)”一直让人很头疼。
通常采用在程序中增加屏幕输出代码的方式来追踪和定位出错的变量,比如下面这个样例程序就在第16行和第18行增加了两句输出:
1 real*8, allocatable :: mlat(:),mlon(:) 2 integer cnti,LenMLAT,LenMLON 3 LenMLAT=50 4 LenMLON=30 5 if (.not.allocated(mlat)) allocate(mlat(LenMLAT)) 6 if (.not.allocated(mlon)) allocate(mlon(LenMLON)) 7 do cnti=0,LenMLAT-1 8 mlat(cnti) = dble(cnti) 9 enddo 10 ! do cnti=1,LenMLAT 11 ! write(*,'("mlat(",I2,"): ",F5.1)') cnti,mlat(cnti) 12 ! enddo 13 14 !...... 15 16 write(*,*) "@1338" 17 if (allocated(mlat)) deallocate(mlat) 18 write(*,*) "@1340" 19 if (allocated(mlon)) deallocate(mlon)
如果屏幕的输出内容停留在“@1338”,则表明它后面一句没有被成功执行,即: if (allocated(mlat)) deallocate(mlat) 失败。
接下来,去程序中检查 $mlat 变量的定义和预分配部分,它必须具有可分配属性并且已经分配了一定的空间,才能在这里被成功释放掉。
经查,变量 $mlat 既具有可分配属性,并且已经分配了一定的空间,但是为什么还是无法正常被释放掉呢?
原来出错的原因是对 $mlat 变量的索引引用超出了它的范围:
(1) allocate(mlat(LenMLAT)) 分配的索引默认从1开始,也即 $mlat 的有效索引范围为 [1, LenMLAT];
(2)deallocate(mlat) 也默认从1开始释放,这样也需要引用 $mlat 在 [1, LenMLAT] 之间的值。
可是,程序中仅仅对 mlat(0:LenMLAT-1) 的部分作了初始化,而 mlat(LenMLAT) 的位置并没有初始化,也就无法释放掉。
感兴趣的同学可以测试下第10-12句的注释部分,看它是否也一样报错。
也正因为 Fortran 关于数组可自定义索引范围的特点,在初始化、引用和释放数组变量时需要特别检查索引的起止范围,保证全文一致,以免出现难以查找原因的“吐核”错误。
核吃多了,容易噎着……