【问题标题】:Segmentation fault in "end program" statement in FortranFortran 中“结束程序”语句中的分段错误
【发布时间】:2017-11-12 23:40:47
【问题描述】:

我有一些 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 但在其他地方出错。

有人可以帮我找出问题吗?谢谢

【问题讨论】:

    标签: segmentation-fault fortran


    【解决方案1】:

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

    在我的例子中,我使用gfortran 编译,我使用了:

    1. -g 包含调试符号
    2. -fbacktrace 以获得更好的堆栈跟踪
    3. -Wall 启用所有编译器警告
    4. -fcheck=all 启用运行时检查(使程序变慢,但在调试期间非常有用)。

    这些运行时检查立即发现了错误:

    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 不同的编译器,则需要阅读有关如何在编译器上打开此类测试的信息,因为这些开关不是标准化的。

    【讨论】:

      【解决方案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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2015-02-05
        • 1970-01-01
        • 2023-03-13
        • 2020-03-19
        • 1970-01-01
        • 2016-12-15
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多