【问题标题】:Array of functions and Segmentation fault - invalid memory reference函数数组和分段错误 - 无效的内存引用
【发布时间】:2017-07-16 13:59:04
【问题描述】:

我正在尝试将我的函数 f 设置为一个数组,但出现以下错误:

Program received signal SIGSEGV: Segmentation fault - invalid memory reference.

Backtrace for this error:

#0  0x6f8b36e3
#1  0x6f8a2722
#2  0x402752
#3  0x747bd411

我必须为M 的每个值求解开普勒方程:f=psi-e*sin(psi)-M。所以,如果我有一个维度为 8 的数组 M,我的程序将计算 8 个零。问题是,如果我写 f=psi-e*sin(psi)-M(1) 我将计算第一个零,如果我写 f=psi-e*sin(psi)-M(8) 我将计算最后一个零。但是,我的问题是,如果我想一次计算所有零,我会必须写f=psi-e*sin(psi)-M(1:8),我的程序应该输入全零,但是,这并没有发生,我得到了我前面提到的错误。这里是代码:

子程序(我在外部使用):这个子程序是二分法(取零):

   subroutine bisecc(f,xl,xr,kmax,tol,k,xm)
  implicit real*8 (a-h,o-z)
  real*8 f
  fl=f(xl)
  fr=f(xr)

  if(fl*fr .gt. 0.0D0) goto 100  
  do k=1,kmax
    xm=(xr+xl)/2.0D0              
    fm=f(xm)

    dif=abs((xr-xl)/xm)        
    if(dif .lt. tol) goto 200    
    write(*,*) k,xm!,dif  
    if (fm*fr .le. 0.0D0) then
      xl=xm
      fl=fm
    else
      xr=xm
      fr=fm
    end if
  end do

  return
200   write(*,*) 'WISHED PRECISION  REACHED'
      return
100   write(*,*) 'BAD CHOICE OF DATA'
      return
      end

主程序:

       include 'bisecc.f' 
   implicit real*8 (a-h,o-z)            
   external f     
   real*8 f    
   ! I  WRITE  THE INTERVAL OF MY 8 ZEROS(left and right point)
   b=0.1D0


   xl1=-0.5D0                
   xr1=0.D0                 
   xl2=xr1+b
   xr2=1.D0
   xl3=xr2+b
   xr3=2.D0
   xl4=xr3+b
   xr4=3.D0
   xl5=xr4+b
   xr5=4.D0
   xl6=xr5+b
   xr6=5.D0
   xl7=xr6+b
   xr7=6.D0
   xl8=xr7+b
   xr8=7.D0        
   kmax=100                            
   tol=0.0001D0                               
   call bisecc(f,xl1,xr1,kmax,tol,k,xm1)         
   call bisecc(f,xl2,xr2,kmax,tol,k,xm2)          
   call bisecc(f,xl3,xr3,kmax,tol,k,xm3)         
   call bisecc(f,xl4,xr4,kmax,tol,k,xm4)         
   call bisecc(f,xl5,xr5,kmax,tol,k,xm5)         
   call bisecc(f,xl6,xr6,kmax,tol,k,xm6)         
   call bisecc(f,xl7,xr7,kmax,tol,k,xm7)          
   call bisecc(f,xl8,xr8,kmax,tol,k,xm8)                     
   write(*,*) 'Program ended'
   stop
   end program

   real*8 function f(psi)
   implicit real*8 (a-h,o-z)
   real*8 M(8)
   dimension f(8)
      e=0.2056D0                          
      pi=acos(-1.0D0)                        
      M=(/pi/4.D0,pi/2.D0,3.D0/4.D0*pi,pi,5.D0/4.D0*pi,3.D0*
 &    pi/2.D0,7.D0/4.D0*pi,2.D0*pi/)
      c=sqrt((1.0D0-e)/(1.0D0+e))                
   f=psi-e*sin(psi)-M(1:8)             !KEPLER EQUATION

   return
   end function   

示例: 这里我想计算 M 的第一个值 M(1)=pi/4 的 psi 值。

http://imgur.com/a/Xdsgf 中,您可以看到psi=0.95303344726562489。所以我刚刚计算了第一个零。但是,您也可以看到这条消息 7 次 datos mal elegidos。这意味着程序只能向我显示那个零(对于M(1)),而其他7 个零没有计算出来,因为我写了f=psi-e*sin(psi)-M(1)。 我应该写什么才能得到 all 零而不是 1 的结果,就像在这个例子中一样?

【问题讨论】:

  • 您必须展示编写代码的代码。请使用-g -fbacktrace -fcheck=all -Wall 或等效代码编译您的代码,然后重试并报告输出。
  • 我没有得到编译错误或构建错误,当我执行程序时得到它并显示在 cmd 中。
  • 如果你想检查代码,你可以给我你的电子邮件地址,我会把两个文件都发给你。f所以你在你自己的电脑上运行它,只有你想要的时候
  • 我提交了一个新问题,如果你知道答案,它可能是解决这个问题的绝佳选择:"stackoverflow.com/questions/42460615/…"
  • 可以将 f() 更改为返回数组的函数,但如果它用于二等分,那么保持它只是一个标量函数并调用二等分例程不是更好吗?输入数组的每个元素...?

标签: arrays function fortran dimension geany


【解决方案1】:

因为函数f()在二分例程bisecc()中使用,我认为通过DO循环将每个输入传递给bisecc()会简单得多,而不是让f()成为一个返回数组的函数(因为后者也需要修改bisecc())。我们可以通过各种方式将M 的值传递给f()(这几乎是FAQ,我相信有很多Q/A 页面)。一种简单的方法是在主程序中包含f(),并为M 使用主机关联。所以一个简化的代码可能看起来像

program main
    implicit none
    integer  kmax, kiter, i
    real*8   xl( 8 ), xr( 8 ), xans( 8 ), tol, M( 8 ), b, pi

    pi   = acos(-1.0D0)                 
    kmax = 100
    tol  = 1.0d-8

    M = [ pi/4.D0,      pi/2.D0,      3.D0/4.D0*pi, pi, &
          5.D0/4.D0*pi, 3.D0*pi/2.D0, 7.D0/4.D0*pi, 2.D0*pi ]
    ! or M = [( i, i=1,8 )] * pi/4.0D0

    ! Use a fixed interval for simplicity.
    xl   =  0.0d0
    xr   = 10.0d0
    xans =  0.0d0

    do i = 1, 8
        call bisecc( f, xl( i ), xr( i ), kmax, tol, kiter, xans( i ) )
        ! print *, "check: f(xans(i)) = ", f( xans( i ) )
    enddo

contains

function f( psi ) result( res )
    implicit none
    real*8  psi, e, res
    e = 0.2056D0
    res = psi - e * sin( psi ) - M( i )   !<-- this "M(i)" refers to that defined above
end function 

end program

使用外部bisecc 例程(稍作修改以免使用 GOTO)

subroutine bisecc( f, xl, xr, kmax, tol, k, xm )
    implicit none
    real*8  f, xl, xr, tol, xm
    external f
    integer kmax, k
    real*8  fl, fr, fm, dif

    fl = f( xl )
    fr = f( xr )
    if( fl * fr > 0.0D0 ) then
        write(*,*) "bad input data (xl,xr)"
        return
    endif

    do k = 1, kmax
        xm = (xr + xl) / 2.0D0              
        fm = f( xm )

        dif = abs( (xr-xl) / xm )
        if ( dif < tol ) then
            write(*,*) "bisection converged: k=", k, "xm=", xm
            return
        endif

        if ( fm * fr <= 0.0D0 ) then
            xl = xm
            fl = fm
        else
            xr = xm
            fr = fm
        end if
    end do  !! iteration

    write(*,*) "bisection did not converge: k=", k, "xm=", xm
end

给了

 bisection converged: k=          31 xm=  0.95299366395920515     
 bisection converged: k=          31 xm=   1.7722388869151473     
 bisection converged: k=          30 xm=   2.4821592587977648     
 bisection converged: k=          30 xm=   3.1415926571935415     
 bisection converged: k=          29 xm=   3.8010260276496410     
 bisection converged: k=          29 xm=   4.5109464414417744     
 bisection converged: k=          29 xm=   5.3301916457712650     
 bisection converged: k=          29 xm=   6.2831853143870831

答案似乎与Kepler equation 的情节一致,其中 e = 0.2056(所以bisecc() 可能还可以)。

上面的代码还有很多需要改进的地方。特别是,将f() 之类的函数包含到模块中(甚至将所有例程包含到模块中)通常更方便。我们也可以通过将M 设为模块变量并将usef()(而不是使用common 语句)或通过主机关联来传递M,所以如果有兴趣,请尝试一下。

【讨论】:

  • (上面的“答案”实际上是一种解决方法,因为它没有回答为什么在 OP 的代码中会出现分段错误,或者如何从函数返回数组。)
  • 感谢您的帮助,我知道你们对代码有点严格,因为这可能对任何人都有用,但我编写代码的方式,是我们老师教我们的方式,所以目前我必须像他一样工作并使用他向我们展示的内容,将来我会学习正确的编码方式,我的物理老师说他不是程序员他是只使用电脑解决问题的物理学家,所以我们对代码不是很严谨
  • @stepdead0X 没问题,“common”有点老了,但仍然是传递变量的有效方法。当变量的数量变大时,考虑在那个时候将 common 切换到一个模块可能会很有用(以避免 common 的各种陷阱)。这个 SO Docs 可能是一个不错的起点stackoverflow.com/documentation/fortran/topics
  • 我们一个月前开始学习计算物理,还需要更多的东西来学习,比如模块等等。
【解决方案2】:

我的解决方案:我将在我的练习中添加一个更通用的解决方案,避免上述错误。这是一个更通用的解决方案,用于 M 的 N 值,而不是 8:

   include 'bisecc.f'  
   implicit real*8 (a-h,o-z)             
   external f     
   parameter (Mlong=100)              !Number of elemnts of M(from 0 to 2pi)
   real*8 f ,M                               
   common M,e                         !to not copy them twice 

   kmax=100                           !max number of iterations
   tol=0.0001D0                       !Tolerance of 0.01%
   e=0.2056D0                         !Mercury excentricity
   pi=acos(-1.0D0)                     
   c=sqrt((1.0D0-e)/(1.0D0+e))     

   open(10,file='153b.dat',status='unknown') !data will apear in a .dat file
   write(*,*)'            i        M                 Theta(rad)'
   write(10,*)'           i        M                 Theta(rad)'

  do i=1,Mlong
     xl=-1.D0                     !LEFT STARTING POINT
     xr=7.D0                      !RIGHT POINT(psi wont be more than 2*pi)  
     M=2.D0*pi*i/Mlong                         !GENERIC M(0 TO 2PI 100STEPS) 
     call bisecc(f,xl,xr,kmax,tol,k,xm)        !CALLING THE SUBROUTINE
     write(10,*) i,M,theta             ! I WILL PLOT THETA IN FUNCTION OF M
     write(*,*) i,M,theta
   end do 

   close(10)
   write(*,*)
   write(*,*) 'Program ENDED'
   stop
   end program

*我的外部功能


   real*8 function f(psi)
   implicit real*8 (a-h,o-z)
   real*8 M
   common M,e
    f=psi-e*sin(psi)-M                       !KEPLER EQUATION
   return       
   end function    

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-06-01
    • 1970-01-01
    • 1970-01-01
    • 2013-04-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多