"结束程序中的分段错误"在Fortran的声明

时间:2017-11-12 23:40:47

标签: segmentation-fault fortran

我有一些Fortran代码,包含一个子程序和一个调用它。它应该使用定义的窗口大小来计算矩阵中元素的平均值。例如,使用winsize = 2在(10,10)数组上调用子例程将返回一个(5,5)数组。

代码如下所示:

SUBROUTINE avgwin(ts, sizelat,sizelon,winsize,size2,size3,ts_new)
implicit none
double precision, dimension(10,sizelat,sizelon) :: ts
double precision, dimension(winsize,winsize) :: store
double precision, dimension(10,size2,size3) :: ts_new
double precision :: par,ave
integer :: sizelat, sizelon,i,j,k,winsize,size2,size3
integer :: A, B,p,m,numb
A=0
B=0
par = 11 !Hypothetical value to be excluded
do i=1,10 !Looping through time
    do j=1,sizelat !Looping through latitude
        if ((j+winsize) > sizelat) then !Checks if it will exceed bounds
            exit !If it'll exceed, discard remaining cells
        end if
        do k=1,sizelon !Looping through longitude
            if ((k+winsize)>sizelon) then
               exit
            end if
            store = ts(i,j:j+winsize,k:k+winsize) !Gets the values for that window
            where (store == par) store = -99 !Replaces masked with -99
            ave = 0
            numb = 0 !Variable to count 
            do p=1,winsize
                do m=1,winsize
                    if (store(p,m)==-99) then !Evaluates if it's masked, i.e., =-99
                        ave = ave
                    else
                        ave = ave + store(p,m) !Sum of existent values
                        numb = numb +1 !Updates counting variable
                    end if
                end do
            end do
            ave = ave/numb !Calculates the mean
            ts_new(i,A,B) = ave 
            B=B+1
        end do
        B=0
        A=A+1
    end do
A=0
B=0
end do
END SUBROUTINE

program testefor
implicit none
double precision, dimension(10,10,10) :: teste
double precision, dimension(10,5,5) :: oi
integer :: i,j,k

do i=1,10
   do j=1,10
      do k=1,10
         teste(i,j,k)=i
      end do
   end do
 end do

CALL avgwin(teste,10,10,2,5,5,oi)
print*, oi(1,5,5)

end program testefor

但是,当我运行它时,我会收到分段错误。我尝试用GDB调试它,令我惊讶的是,它返回正确的结果但退出程序时会出现段错误。我从gdb得到的内容可以在下面找到:

Breakpoint 1, testefor () at testefor.f90:56
56      do i=1,10
(gdb) cont
Continuing.
   1.0000000000000000     

Program received signal SIGSEGV, Segmentation fault.
0x0000000000400f73 in testefor () at testefor.f90:67
67  end program testefor

所以程序返回正确的(1,5,5)元素= 1.0,但在其他地方发生错误。

有人可以帮我识别问题吗?感谢

2 个答案:

答案 0 :(得分:3)

可以帮助的一件事是在调试模式下编译程序,并激活许多调试开关。

在我的情况下,我使用gfortran进行编译,并使用了:

  1. -g包含调试符号
  2. -fbacktrace以获得更好的堆栈跟踪
  3. -Wall启用所有编译器警告
  4. -fcheck=all启用运行时检查(使程序变慢,但在调试过程中非常有用)。
  5. 这些运行时检查立即发现错误:

    At line 21 of file teste.F
    Fortran runtime error: Array bound mismatch for dimension 1 of array 'store' (2/3)
    

    store的大小为(winsize, winsize),但您将数组复制到大小为(windsize+1, winsize+1)的数组中。如果在Fortran中对数组进行切片,则它包括开始和结束索引:(1:10)从1到10.如果有(1:1+10),则从1到11,这意味着它的大小为11

    没有运行时数组边界检查(在这种情况下由-fcheck=all激活),这很难调试,并且可能导致各种意外行为。

    如果使用与gfortran不同的编译器,则需要了解如何在编译器上打开此类测试,因为交换机未标准化。

答案 1 :(得分:2)

这不是你问题的答案(我已经回答了),但有一些提示可以帮助你。

  1. 如果循环遍历一个多维数组,你应该总是在第一个索引上有最里面的循环,依此类推,所以

    ! Inefficient way to do it:
    do i = 1, 10
        do j = 1, 10
            do k = 1, 10
                a(i, j, k) = i*j+k
            end do
        end do
    end do
    
    !Efficient way to do it:
    do k = 1, 10                   !  <-+
        do j = 1, 10               !    | swapped
            do i = 1, 10           !  <-+
                a(i, j, k) = i*j+k
            end do
        end do
    end do
    

    Fortran存储多维数组的方式是第一个索引变化最快。因此,有效的方法会读取连续的元素,而效率低下的方法需要在内存中大量跳转。

  2. 您可以轻松使用SUMCOUNT来代替这个错综复杂的循环:

    • COUNT计算数组中.TRUE.的实例,因此numb的值可以轻松计算为numb=COUNT(store/=-99)
    • SUM具有可选参数MASK,可用于省略某些值。要总结所有非-99的值,您可以使用:s = SUM(store, MASK=(store /= -99))

    所以你可以用以下代码替换大约15行代码:

    ave = sum(store, MASK=(store/=par)) / count(store/=par)
    
  3. storepar都是double precision,比较浮点变量的绝对相等是tricky