将函数作为参数传递给子例程时出现分段错误 [英] Segmentation fault when passing a function as argument in a subroutine

查看:191
本文介绍了将函数作为参数传递给子例程时出现分段错误的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我尝试说明如何将函数传递给Newton Raphson过程。我用一个非常简单的函数(称为 unefonction 见下文)成功,但它不适用于具有参数的函数。第二个函数称为 gaussienne ,它有一个参数x和两个可选参数 mu SIG 。在我的牛顿raphson程序中,我以这种方式调用函数: f(x)。对我来说奇怪的是,在执行过程中,程序就好像可选参数 sig mu 一样但他们不......因此我不明白...

这里是包含函数的模块

 模块函数

隐式无

! parametre pour la gaussienne
double precision :: f_sigma = 1.d0,f_mu = 0.d0

! pi可访问唯一性en interne
double precision,parameter :: pi = 3.14159265359d0

包含

双精度函数unefonction(x)
! fonction:unefonction
! renvoie
! $ \frac {e ^ x - 10} {x + 2}

隐式无

!参数
双精度,意图(in):: x

unefonction =(exp(x) - 10.)/(x + 2)

结束函数unefonction

! * * * * * * * * * * * * * * * * * * * * * * * *

双精度函数gaussienne(x,mu,sig)
! fonction gaussienne
!利用参数定义模块si
! mu et sig ne sont pas pass en参数

隐含无

!参数
双精度,意图(in):: x
双精度,意图(in),可选:: mu,sig

!变量locales
双精度:: norme,moy,sigma

! sigma
if(present(sig))then
write(*,*)sig present
sigma = sig
else
sigma = f_sigma
结束如果

!如果(现在(亩)),那么
写(*,*)mu present
moy = mu
else
moy = f_mu
结束如果

! calcul de la gaussienne
norme = 1.d0 /(sigma * sqrt(2.d0 * pi))
gaussienne = norme * exp( - (x - moy)** 2 /(2.d0 * sigma ** 2))

结束函数gaussienne

结束模块函数

以下是包含newton raphson过程的模块

 模块rechercheRacine 

隐式无

包含

子程序newtonRaphson(racine,f,eps,cible)

! recherche l'antecedant de cible

隐含无

!参数
双精度,意图(inout):: racine
双精度,intent(in),可选:: cible,eps

! fonction不要在cherche la racine
双精度,外部:: f

!变量locales
integer :: compteur
双精度:: xold,xnew,delta,valcible
双精度::阈值,fprim,fdex

!如果(当前(eps))然后
阈值= eps
其他
阈值= 1.d-10
结束,如果

!价值圣经
if(present(cible))then
valcible = cible
else
valcible = 0.d0
如果

写完(*,*)-------------------------------------------- ------------
写入(*,*)NEWTON RAPHSON
写入(*,*)------------ --------------------------------------------
写入(*,('x0 =',e16.6))racine
write(*,('seuil =',e16.6))threshold
write(*, cible =',e16.6))valcible
write(*,*)---------------------------- ----------------------------
write(*,*)ITERATIONS
write(*, *)----------------------------------------------- ---------

!初始化
compteur = 0
delta = 1.d0
xold = racine
write(*,'(i4,4e16.6)')compteur,f(xold),xold ,0.,阈值

!迭代
在(delta>阈值和。compteur< = 100)时执行

!计算结果en bold
fdex = f(xold) - 强迫

! calcul de la derivee numerique
fprim =(f(xold + threshold) - f(xold - threshold))/(2.d0 * threshold)

!应用程序代码Newton Raphson
xnew = xold - fdex / fprim
delta = abs(xnew - xold)
compteur = compteur + 1

! (*,'(i4,4e16.6)')compteur,fdex,xnew,delta,threshold

!如果(delta <阈值),则
racine = xnew
write(*,* b *)如果(delta <阈值)则b $ b = xnew
end do

)'------------------------------------------------ --------'
write(*,*)'CONVERGE'
write(*,*)'----------------- ---------------------------------------'
write(*,*) 'a la convergence demandee,une solution est:'
write(*,('x =',e20.10,'f(x)=',e20.10))racine,f(racine)
write(*,*)
else
write(*,*)'------------------------ ------------------------------'
写(*,*)'NON CONVERGE'
write(*,*)'------------------------------------------ --------------'
结束如果

结束子程序newtonRa phson

结束模块rechercheRacine

以下是主程序:

 程序主体

!使用rechercheRacine

! contient la fonction
使用功能

隐含无

双精度:: racine,eps,cible

! appel de la子程序newtonRaphson
! sans la valeur cible:cible(defaut = 0)
! sans la precision:eps(defaut 1d-10)
racine = 1.d0
call newtonRaphson(racine,unefonction)

! -------------------------------------------------- ------

! appel de la子程序newtonRaphson
! avec pour cible 10
racine = 1.d0
eps = 1.d-14
cible = 10.d0
call newtonRaphson(racine,unefonction,eps,cible)

! -------------------------------------------------- ------

! parametre de la gaussienne
f_sigma = 2.d0
f_mu = 5.d0
! appel de la子程序newtonRaphson
!通过des论证sous la forme clef = valeur
cible = 0.1d0
racine = 2.d0
call newtonRaphson(cible = cible,f = gaussienne,racine = racine)

end program main

主程序适用于名为 unefonction的函数但它不适用于 gaussienne 函数。



这是错误消息:

 编程接收到的信号SIGSEGV:分段错误 - 无效的存储器引用。 

Backtrace for this error:
#0 0x7F1B6F5890F7
#1 0x7F1B6F5896D4
#2 0x7F1B6EEEB49F
#3 0x4009D2在__fonction_MOD_gaussienne at mod_fonction.f90:54
#4 0x40104D在__rechercheracine_MOD_newtonraphson在mod_racine.f90:59
#5 0x4016BA在MAIN__在main.f90:40
Erreur de分段(核心转储)

我认为无效的内存引用是由于程序确实如此可选参数 sig mu 已存在,因此可以查找这些参数。

$ b

解决方案

是的,问题是你传递的函数确实需要三个参数而不是一个。如果在子例程 newtonRaphson 中更改外部声明 f >

  double precision,external :: f 

转换为一个显式接口(描述如何使用它):

 接口
双精度函数f(x)
双精度,意图(in):: x
结束函数f
结束接口



由于参数数目不匹配,您的代码甚至无法编译。



它们是将参数传递给从例程 newtonRaphson 调用的函数 f 的不同方法:




  • 您可以期望 f 有两个 intent( in)参数而不是一个:除了值x之外,它还可以使用一个真正的数组,它可以是任意大小,并且可以包含必要的参数。这将需要以下接口:

     接口
    双精度函数f(x,params)
    双精度,intent(in):: x
    双精度,intent(in):: params(:)
    结束函数f
    结束接口

    这些不需要参数的函数(如 unefonction )不会使用第二个参数,而其他的参数(如 gaussienne )将从它们的参数中获取参数。 newtonRaphson 用一个类型绑定过程返回给定x值的值给定可扩展类型( class ) 。然后,您可以创建这种类型的abritrary扩展,它可以基于存储为扩展类型中的字段的一些参数来计算给定x值的值。然后该程序可能如下所示(我剥离了几个部分),但它需要Fortran 2003编译器:

      module rechercheRacine 
    隐式无

    类型,抽象::计算器
    包含
    过程(getvalue_iface),deferred :: getvalue
    结束类型计算器

    接口
    双精度函数getvalue_iface(self,x)
    import :: calculator $ b $ class(calculator),intent(in):: self
    double precision,intent(in) :: x
    结束函数getvalue_iface
    结束接口

    包含

    子程序newtonRaphson(racine,f,eps,cible)
    双精度,intent(inout):: racine
    class(计算器),intent(in):: f
    双精度,intent(in),可选:: cible,eps

    (delta>阈值,并且compteur< = 100)
    fdex = f%getvalue(xold) - 有价值的

    结束做

    结束子程序newtonRaphson

    结束模块rechercheRacine


    模块函数
    使用rechercheRacine
    隐式无

    类型,扩展(计算器):unefonction
    包含
    过程:: getvalue => unefonction_getvalue
    结束类型unefonction

    类型,extends(计算器):: gaussienne
    双精度:: mu = 0.0d0,sigma = 1.0d0
    包含
    procedure :: getvalue => gaussienne_getvalue
    结束类型gaussienne

    包含

    双精度函数unefonction_getvalue(self,x)
    class(unefonction),intent(in):: self
    double precision,intent(in):: x
    unefonction_getvalue =(exp(x) - 10.)/(x + 2)
    结束函数unefonction_getvalue

    双精度函数gaussienne_getvalue(self,x)
    class(gaussienne),intent(in):: self
    双精度,intent(in):: x


    gaussienne_getvalue = norme * exp( - (x - moy)** 2 /(2.d0 * self%sigma ** 2))

    结束函数gaussienne_getvalue

    结束模块功能


    程序主体
    使用rechercheRacine
    使用功能
    隐含无

    类型(unefonction):: fone
    type(gaussienne):: fgauss


    call newtonRaphson(racine,fone)
    call newtonRaphson(cible = cible,f = fgauss,racine = racine )

    结束程序主要



I try to illustrate how to pass a function to a Newton Raphson procedure. I succeed with a very simple function (called unefonction see below) but it does not work with a function which has got parameters. This second fonction is called gaussienne and it takes one argument, x, and two optional arguments mu and sig. In my newton raphson procedure I called the fonction in this way : f(x). What is strange for me is that during the execution, the program does as if the optional parameters sig and mu were present but they don't... Thus I do not understand ...

Here is the module which contains the functions

module fonction

  implicit none

  ! parametre pour la gaussienne
  double precision :: f_sigma = 1.d0, f_mu = 0.d0

  ! pi accessible uniquement en interne
  double precision, parameter :: pi = 3.14159265359d0

  contains

    double precision function unefonction(x)
        ! fonction : unefonction
        ! renvoie
        !    $\frac{e^x - 10}{x + 2}$

        implicit none

        ! arguments 
        double precision, intent(in) :: x

        unefonction = (exp(x) - 10.) / (x + 2.)

    end function unefonction

! * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 

    double precision function gaussienne(x, mu, sig)
        ! fonction gaussienne
        ! utilise les parametres definis dans le module si
        ! mu et sig ne sont pas passes en argument

        implicit none

        ! arguments
        double precision, intent(in)           :: x
        double precision, intent(in), optional :: mu, sig

        ! variables locales
        double precision :: norme, moy, sigma

        ! sigma
        if (present(sig)) then
            write(*,*)"sig present"
            sigma = sig
        else
            sigma = f_sigma
        end if

        ! mu
        if (present(mu)) then
            write(*,*)"mu present"
            moy = mu
        else
            moy = f_mu
        end if

        ! calcul de la gaussienne
        norme = 1.d0 / (sigma * sqrt(2.d0 * pi))
        gaussienne = norme * exp(-(x - moy)**2 / (2.d0 * sigma**2))

    end function gaussienne

end module fonction

Here is the module which contains the newton raphson procedure

module rechercheRacine

    implicit none

    contains

        subroutine newtonRaphson(racine, f, eps, cible)

            ! recherche l'antecedant de cible

            implicit none

            ! arguments
            double precision, intent(inout)        :: racine
            double precision, intent(in), optional :: cible, eps

            ! fonction dont on cherche la racine
            double precision, external :: f

            ! variables locales
            integer          :: compteur
            double precision :: xold, xnew, delta, valcible
            double precision :: threshold, fprim, fdex

            ! precision
            if (present(eps)) then
                threshold = eps
            else
                threshold = 1.d-10
            end if

            ! valeur cible
            if (present(cible)) then
                valcible = cible
            else
                valcible = 0.d0
            end if

            write(*,*) "--------------------------------------------------------"
            write(*,*) "                      NEWTON RAPHSON"
            write(*,*) "--------------------------------------------------------"
            write(*,"('x0    = ',e16.6)") racine
            write(*,"('seuil = ',e16.6)") threshold
            write(*,"('cible = ',e16.6)") valcible
            write(*,*) "--------------------------------------------------------"
            write(*,*) "                        ITERATIONS"
            write(*,*) "--------------------------------------------------------"

            ! initialisation
            compteur = 0
            delta    = 1.d0
            xold     = racine
            write(*, '(i4,4e16.6)') compteur, f(xold), xold, 0., threshold

            ! iterations
            do while (delta > threshold .and. compteur <= 100)

                ! calcul de la fonction en xold
                fdex = f(xold) - valcible

                ! calcul de la derivee numerique
                fprim  = (f(xold + threshold) - f(xold - threshold)) / (2.d0 * threshold) 

                ! application de l'iteration de Newton Raphson
                xnew = xold - fdex / fprim
                delta = abs(xnew - xold)
                compteur = compteur + 1

                ! affichage de la convergence
                write(*, '(i4,4e16.6)') compteur, fdex, xnew, delta, threshold

                ! mise a jour de xstart
                xold = xnew
            end do

            if (delta < threshold) then
                racine = xnew
                write(*, *) '--------------------------------------------------------'
                write(*, *) '                        CONVERGE'
                write(*, *) '--------------------------------------------------------'
                write(*, *) 'A la convergence demandee, une solution est:'
                write(*, "('x = ',e20.10,'    f(x) = ', e20.10)") racine, f(racine)
                write(*, *)
            else
                write(*, *) '--------------------------------------------------------'
                write(*, *) '                      NON CONVERGE'
                write(*, *) '--------------------------------------------------------'
            end if

        end subroutine newtonRaphson

end module rechercheRacine

Here is the main program :

program main

    ! contient la subroutine newtonRaphson
    use rechercheRacine

    ! contient la fonction
    use fonction

    implicit none

    double precision :: racine, eps, cible

    ! appel de la subroutine newtonRaphson
    ! sans la valeur cible : cible (defaut = 0)
    ! sans la precision : eps (defaut 1d-10)
    racine = 1.d0
    call newtonRaphson(racine, unefonction)

    ! --------------------------------------------------------

    ! appel de la subroutine newtonRaphson
    ! avec pour cible 10
    racine = 1.d0
    eps    = 1.d-14
    cible  = 10.d0
    call newtonRaphson(racine, unefonction, eps, cible)

    ! --------------------------------------------------------

    ! parametre de la gaussienne
    f_sigma = 2.d0
    f_mu = 5.d0
    ! appel de la subroutine newtonRaphson
    ! passage des arguments sous la forme clef = valeur
    cible = 0.1d0
    racine = 2.d0
    call newtonRaphson(cible = cible, f = gaussienne, racine = racine)

end program main

The main program works for the function called unefonction but it doesn't work for the gaussienne function.

Here is the error message :

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

Backtrace for this error:
#0  0x7F1B6F5890F7
#1  0x7F1B6F5896D4
#2  0x7F1B6EEEB49F
#3  0x4009D2 in __fonction_MOD_gaussienne at mod_fonction.f90:54
#4  0x40104D in __rechercheracine_MOD_newtonraphson at mod_racine.f90:59
#5  0x4016BA in MAIN__ at main.f90:40
Erreur de segmentation (core dumped)

I think that the invalid memory reference is due to the fact that the program does as if optional parameters sig and mu were present and thus looks for them while they are not.

解决方案

Yes, the problem is that the function you pass indeed expects three arguments instead of one only. If you change the external declaration of f in the subroutine newtonRaphson

double precision, external :: f

to an explicit interface (which describes, how you really use it):

interface
  double precision function f(x)
    double precision, intent(in) :: x
  end function f
end interface

your code won't even compile due to the mismatch in the number of parameters.

They are different ways to pass "parameters" to the function f which is called from the routine newtonRaphson:

  • You could expect f to have two intent(in) arguments instead of one: Additional to the value x, it could take also a real array, which may be of arbitrary size and may contain the necessary parameters. That would require following interface:

    interface
      double precision function f(x, params)
        double precision, intent(in) :: x
        double precision, intent(in) :: params(:)
      end function f
    end interface
    

    Those functions, which do not need parameters (like unefonction) would just not use the content of the second parameter, while others (like gaussienne) would take their parameters from it.

  • You could make newtonRaphson to expect a given extensible type (class) with a type bound procedure returning the value for a given x-value. You can then create abritrary extensions of this type, which may calculate the value for the given x-value based on some parameters stored as fields in the extended type. Then the program could look like below (I stripped several parts), but it would require Fortran 2003 compiler:

    module rechercheRacine
      implicit none
    
      type, abstract :: calculator
      contains
        procedure(getvalue_iface), deferred :: getvalue
      end type calculator
    
      interface
        double precision function getvalue_iface(self, x)
          import :: calculator
          class(calculator), intent(in) :: self
          double precision, intent(in) :: x
        end function getvalue_iface
      end interface
    
    contains
    
      subroutine newtonRaphson(racine, f, eps, cible)
        double precision, intent(inout)        :: racine
        class(calculator), intent(in)          :: f
        double precision, intent(in), optional :: cible, eps
    
        do while (delta > threshold .and. compteur <= 100)
          fdex = f%getvalue(xold) - valcible
          :
        end do
    
      end subroutine newtonRaphson
    
    end module rechercheRacine
    
    
    module fonction
      use rechercheRacine
      implicit none
    
      type, extends(calculator) :: unefonction
      contains
        procedure :: getvalue => unefonction_getvalue
      end type unefonction
    
      type, extends(calculator) :: gaussienne
        double precision :: mu = 0.0d0, sigma = 1.0d0
      contains
        procedure :: getvalue => gaussienne_getvalue
      end type gaussienne
    
    contains
    
      double precision function unefonction_getvalue(self, x)
        class(unefonction), intent(in) :: self
        double precision, intent(in) :: x
        unefonction_getvalue = (exp(x) - 10.) / (x + 2.)
      end function unefonction_getvalue
    
      double precision function gaussienne_getvalue(self, x)
        class(gaussienne), intent(in) :: self
        double precision, intent(in) :: x
    
        :
        gaussienne_getvalue = norme * exp(-(x - moy)**2 / (2.d0 * self%sigma**2))
    
      end function gaussienne_getvalue
    
    end module fonction
    
    
    program main
      use rechercheRacine
      use fonction
      implicit none
    
      type(unefonction) :: fone
      type(gaussienne) :: fgauss
    
      :
      call newtonRaphson(racine, fone)
      call newtonRaphson(cible = cible, f = fgauss, racine = racine)
    
    end program main
    

这篇关于将函数作为参数传递给子例程时出现分段错误的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆