使用常量参数调用函数时的Segfault

时间:2013-05-09 08:20:34

标签: fortran fortran90

我在Fortran中编写了这个非常简单的代码:

program su
  implicit none
  real ran3
  write(*,*) ran3(0)
end program su

real*8 function ran3(iseed)
  implicit none
  integer iseed
  iseed=iseed*153941+1
  ran3=float(iseed)*2.328+0.5     
end function ran3

我编译它没有问题,但是当我执行代码时,我收到了这条消息:

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

Backtrace for this error:
#0  0xB76BAC8B
#1  0xB76BB2DC
#2  0xB77BA3FF
#3  0x8048653 in ran3_
#4  0x80486B3 in MAIN__ at der.f90:?
Segmentation fault (core dumped)

你能告诉我为什么,以及如何解决它?

5 个答案:

答案 0 :(得分:5)

我发现代码存在两个问题。第一个是我认为是错误原因的那个。函数ran3以常量0作为实际参数引用,但相应的伪参数iseed在函数的赋值语句的左侧使用。这是一个错误:您无法更改零值。

第二个错误是ran3返回real*8(无论可能是什么;它是非标准声明),但在主程序ran3中声明为默认real }。

以下程序和函数使用gfortran 4.7.2进行编译。

program su
    implicit none
    real :: ran3

    write(*, *) ran3(0)
end program su

function ran3(iseed)
    implicit none
    integer :: iseed, temp
    real :: ran3

    temp = iseed * 153941 + 1
    ran3 = temp * 2.328 + 0.5
end function ran3

答案 1 :(得分:2)

虽然上面提出了很多好处,但上述大多数解决方案都无法实现该功能的直接目的。值得注意的是,随机数生成器在许多情况下也需要返回iSeed的“新”值(虽然OP的帖子没有明确说明),因为通常在下一次调用Ran s / r时,“new”值为iSeed是必需的。

作为一项基本规则,常量应仅作为Args传递给Intent(In)dummy。

从某种意义上说,OP获得segv是“幸运的”,因为在(糟糕的)过去,可以将“数字”“0”发送为“0”,但返回“0”在其他任何地方都会包含iSeed和没有segv的值,但是有很多不好的算法。

在这种特殊情况下,更合适的解决方案是不要传递常数,而是:

program su
implicit none
Integer      :: iSeed
!
iSeed = 0   ! or whatever iSeed is requried
!
write(*, *) ran3(iSeed)

contains

     function ran3(iseed)
        implicit none
        real :: ran3
        integer, intent(InOut) :: iSeed


        iseed = iseed*153941+1
        ran3 = float(iseed)*2.328+0.5     
    end function ran3

end program su

现在,Ran3()的调用/使用可以是迭代的,例如通过循环或元素等来创建(准)随机序列。

还有其他可能使用外部等,但这是另一天。

答案 2 :(得分:1)

您的代码没有告诉编译器声明

real ran3

指的是您稍后在源文件中定义的函数。对于编译器,您已声明了一个名为ran3的实数变量。一旦编译器在程序结束时读取了end语句,它就可以根据需要进行操作,并且不需要再进行任何编译 - 尽管您可能会发现某些编译器会这样做。

构造Fortran程序的一般规则是编译器在遇到任何实体之前必须遇到实体的定义(变量,函数,子例程,派生类型,有什么用)。你的代码违反了这条规则。

一旦代码声明了一个真正的变量,它就会在这个语句中尝试

write(*,*) ran3(0)

访问名为ran3的数组的第0个元素,这一切都以泪水结束。

快速解决方法是将end program su移动到源文件的末尾,并在函数定义之前放置包含关键字contains的行。然后,您可以删除声明real ran3,因为编译器将处理需要完成的任何链接。

哦,在我写作的时候,你可以做自己,那些试图理解你的代码的人,通过更加注意格式化你发布的内容来帮助你。个人(意见即将到来,如果你很容易不高兴的话,现在就把目光移开)我会解雇那些把代码看起来像这样的程序员,理由是那些对这些小东西很少关注的人可能并不太关注也很重要。

答案 3 :(得分:1)

如果您因任何原因想要通过该功能修改iseed,则应使用intent(in out)进行标记。如果这样做,编译器将在编译时使用文字常量调用函数时触发错误。如果您想将参数作为输入使用,则可以将其标记为intent(in),并且由于您在函数内部分配了iseed,因此会再次出现错误。

我认为养成宣布意图的习惯是个好主意。

您的代码可能看起来像

program su
    implicit none

    write(*, *) ran3(0)

contains

  function ran3(iseed)
    implicit none
    real :: ran3
    integer, intent(in) :: iseed
    ! or intent(in out) :: iseed

    iseed = iseed*153941+1
    ran3 = float(iseed)*2.328+0.5     
  end function ran3

end program su

(无论你是否使用“in”或“in out”作为意图,这都不会编译,因为早期解释了这一点。)

以下代码将编译(也应该有效)

program su
    implicit none

    write(*, *) ran3(0)

contains

  function ran3(iseed)
    implicit none
    real :: ran3
    integer, intent(in) :: iseed

    ran3 = real(iseed*153941+1)*2.328+0.5     
  end function ran3

end program su

答案 4 :(得分:1)

首先,您必须将idum定义为整数。

  program su
  implicit none
  integer idum
  real ran3
  idum = 334
  write(*,*) ran3(idum)
  end program su

那么你的代码就可以了