首页 > 解决方案 > Fortran 读取 SIGSEGV 分段错误

问题描述

我对 Fortran 不熟悉,但我不得不使用我顾问的旧代码,但它不起作用,我可以将它追踪到一个最小的工作示例。

hello.f 如下:

  implicit none
  character*200 rec
  integer var,idum
  real*4 rdum
  call xparse('-t0',1,1,'required',rec,idum,rdum)
  print *, rec
  read(rec,'(i6)')   var
  print *, var
  END

extra.f 如下:

c------------------------------------------------------------------
      subroutine xparse(cc,nth,ifo,req,carg,iarg,rarg)
c------------------------------------------------------------------
      character*(*) cc,req,carg
      character*256 rec
c
      iver=0
      lcc=len(cc)
      na=iargc()
      ith=0
      do 100 ia=1,na
      call getarg(ia,rec)
      if(rec(1:15).eq.'-xparse_verbose') iver=1
      lrec=ilen(rec,256)
      if(lrec.eq.lcc) then
      if(rec(1:lcc).eq.cc(1:lcc)) then
      if(iver.eq.1)write(0,'(a,a)')'xparse ,parsing:',rec(1:lcc)
c
      if(nth.le.0) then
      iarg=1
      return
      endif
c
      ith=ith+1
      if(ith.eq.nth) then
c
      if(ia.eq.na) then
      write(0,*)'parse error :  missing value for ',cc
      endif
      call getarg(ia+1,rec)
      lrec=ilen(rec,256)
      if(iver.eq.1)write(0,'(a,a)')'xparse, string:',rec(1:lrec)
      if(ifo.eq.1) then
      if(iver.eq.1)write(0,'(a,a)')'xparse, character:',rec(1:lrec)
      carg=rec
      else if(ifo.eq.2) then
      s=gets(rec)
      if(s.ge.0.0)iarg=s+0.1
      if(s.lt.0.0)iarg=s-0.1
      if(iver.eq.1)write(0,*)'xparse, integer:',iarg
      else if(ifo.eq.3) then
      rarg=gets(rec)
      if(iver.eq.1)write(0,*)'xparse, real:',rarg
      endif
      return
c
      endif
      endif
      endif
100   continue
c
      if(req(1:8).eq.'required') then
      write(0,*)'parse error :  cant find required arg:  ',cc
      stop
      endif
      end
c------------------------------------------
      real function gets(cc)
c------------------------------------------
c
c  decodes integer or floating f format
c  from character string
c
      character*(*) cc
      nn=len(cc)
c
      gets=0.0
      fak=1.
      ief=0
      l1=0
      l2=0
      do 200 i=1,nn
      if(cc(i:i).eq.'e'.or.cc(i:i).eq.'E')ief=i
      if(cc(i:i).eq.'d'.or.cc(i:i).eq.'D')ief=i
      if(l1.eq.0.and.cc(i:i).ne.' ')l1=i
      if(cc(i:i).ne.' ')l2=i
      if(cc(i:i).eq.' '.and.l2.gt.0) goto 201
200   continue
201   continue
      nn=l2
      if(ief.gt.0) then
      lex=l2-ief
      iex=-9999999
      if(lex.eq.1)read(cc(ief+1:l2),'(i1)',err=900) iex
      if(lex.eq.2)read(cc(ief+1:l2),'(i2)',err=900) iex
      if(lex.eq.3)read(cc(ief+1:l2),'(i3)',err=900) iex
      if(lex.eq.4)read(cc(ief+1:l2),'(i4)',err=900) iex
      if(lex.eq.5)read(cc(ief+1:l2),'(i5)',err=900) iex
      if(iex.gt.-999999) then
      if(iex.lt.0)fak=1./( 10.**(-iex) )
      if(iex.gt.0)fak=10.**iex
      else
      write(0,*)'gets: cannot read ',cc
      endif
      nn=ief-1
      endif
c
      sig=1.
      ss=0.
      tt=1.
      ip=0
      do 100 l=1,nn
      if(cc(l:l).ne.' ') then
      if(cc(l:l).eq.'.') then
      ip=1
      else if(cc(l:l).eq.'-') then
      sig=-1.
      else
c      read(cc(l:l),'(i1)',err=900) ii
      ii=ichar(cc(l:l))-48
      if(ii.lt.0.or.ii.gt.9) goto 109
      if(ip.eq.0) then
      ss=10.*ss+float(ii)
      else
      tt=0.1*tt
      ss=ss+tt*float(ii)
      endif
      endif
      endif
100   continue
109   continue
      gets=ss*sig*fak
      return
900   continue
      write(0,*)' gets: error reading formatted integer:'
      write(0,*)nn
      write(0,'(a,a,a)')'$',cc,'$'
      return
      end
c-------------------------------------
      integer function ilen(c,m)
c-------------------------------------
      character*80 c
      k=1
      do 100 i=1,m
      if(c(i:i).ne.' '.and.c(i:i).ne.char(0))k=i
100   continue
      ilen=k
      jlen=k
      return
      end

如果我做

gfortran hello.f extra.f

./a.out -t0 10800

我得到错误

10800

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

Backtrace for this error:
#0  0x10a01735c
#1  0x10a0166f3
#2  0x7fff7376cb5c
#3  0x10a15b340
#4  0x10a15bd2d
#5  0x10a15978f
#6  0x10a00c917
#7  0x10a00c9e5
Segmentation fault: 11

该读取函数(?)是代码中经常用于读取用户输入的函数(如 Python 的 sys.argv),但我不明白它为什么会失败。

我正在使用 GNU Fortran (Homebrew GCC 9.3.0_1) 9.3.0,MacOS Mojave 10.14.6。

标签: fortrangfortran

解决方案


函数中存在错误ilen。考虑

      character*80 c
      k=1
      do 100 i=1,m
      if(c(i:i).ne.' '.and.c(i:i).ne.char(0))k=i
100   continue

变量(虚拟参数)c被声明为长度为 80,但在循环中,如果子字符串大于(在程序中调用时),c(81:81)肯定会尝试访问子字符串。这是不正确的。m80

您可以使用索引c到 80 位

      do 100 i=1,80
      ...

或直到它的长度:

      do 100 i=1,len(c)
      ...

或者,您可以使c长度为 256 或不(必须)为 80:

c Have the length of the dummy argument assumed
      character*(*) c

或者

c Have the length given by the argument m
      character*(m) c

还有一些现代方法来编写字符声明,例如

      character(len=*) c

      character(len=m) c

推荐阅读