将空指针实际参数传递给派生类型的成员过程

时间:2017-02-21 17:34:29

标签: pointers fortran gfortran intel-fortran

将空指针传递给函数并且子例程有效 到Fortran 2003标准?请假设被调用的函数和 子例程可以正确处理伪参数的情况 空指针。我对这种功能和情况特别感兴趣 子程序是一个'成员'派生类型的函数和子例程。

我想避免检查每个指针的关联状态 如果标准允许,则呼叫方,与我不同的方式相同 必须分别处理零大小的数组。与此同时,我不想 依赖于标准未规定的行为。

据我尝试使用以下示例,ifortgfortran显示 关于这一点的不同行为。

moo.f90

MODULE moo
  IMPLICIT NONE
  PRIVATE

  PUBLIC LL

  TYPE LL
     INTEGER :: i0
   CONTAINS
     PROCEDURE :: func1
     PROCEDURE :: func2
  END type LL

CONTAINS

  FUNCTION func1(self) RESULT(pLL_dest)
    TYPE(LL), POINTER            :: pLL_dest
    CLASS(LL), TARGET, INTENT(IN) :: self

    write(*,*) 'hello from func1'
    pLL_dest => null()
    !pLL_dest => self

  END FUNCTION func1

  FUNCTION func2(self) RESULT(flg)
    LOGICAL                       :: flg
    CLASS(LL), TARGET, INTENT(IN) :: self

    write(*,*) 'hello from func2'
    flg = .true.

  END FUNCTION func2

END MODULE moo

main.f90

PROGRAM chk_nullpo
  USE moo, ONLY : LL
  IMPLICIT NONE

  !CLASS(LL), POINTER :: pLL_s=>null()
  TYPE(LL),  POINTER :: pLL_s=>null()
  TYPE(LL),  POINTER :: pLL_d=>null()
  LOGICAL           :: flg

  write(*,*) 'associated(pLL_s) =',associated(pLL_s)
  write(*,*) 'associated(pLL_d) =',associated(pLL_d)

  write(*,*) 'func1..'
  pLL_d => pLL_s%func1()
  write(*,*) 'associated(pLL_s) =',associated(pLL_s)
  write(*,*) 'associated(pLL_d) =',associated(pLL_d)

  write(*,*) 'func2..'
  flg =pLL_s%func2()
  write(*,*) 'flg=', flg
  write(*,*) 'associated(pLL_s) =',associated(pLL_s)
  write(*,*) 'associated(pLL_d) =',associated(pLL_d)

  write(*,*) 'normal end'
END PROGRAM chk_nullpo

ifort生成的可执行文件在成员时导致运行时错误 使用空指针调用子例程func2

$ ifort -v
ifort version 14.0.2
$ ifort -c moo.f90 -stand f03 -warn all -check
$ ifort -c main.f90 -stand f03 -warn all -check
$ ifort -o ex_ifort moo.o main.o  -stand f03 -warn all -check
ifort: warning #10182: disabling optimization; runtime debug checks enabled
$ ./ex_ifort 
 associated(pLL_s) = F
 associated(pLL_d) = F
 func1..
 hello from func1
 associated(pLL_s) = F
 associated(pLL_d) = F
 func2..
forrtl: severe (408): fort: (7): Attempt to use pointer PLL_S when it is not associated with a target

Image              PC                Routine            Line        Source             
ex_ifort           0000000000402AE1  Unknown               Unknown  Unknown
ex_ifort           0000000000402336  Unknown               Unknown  Unknown
libc.so.6          00002AC53B23DF45  Unknown               Unknown  Unknown
ex_ifort           0000000000402229  Unknown               Unknown  Unknown
$ 

另一方面,gfortran生成的可执行文件没有错误地完成。

$ gfortran --version
GNU Fortran (Ubuntu 4.8.4-2ubuntu1~14.04.3) 4.8.4
Copyright (C) 2013 Free Software Foundation, Inc.

GNU Fortran comes with NO WARRANTY, to the extent permitted by law.
You may redistribute copies of GNU Fortran
under the terms of the GNU General Public License.
For more information about these matters, see the file named COPYING
$ gfortran -c moo.f90 -std=f2003 -Wall -fbounds-check
moo.f90:26.21:

  FUNCTION func2(self) RESULT(flg)
                     1
Warning: Unused dummy argument 'self' at (1)
moo.f90:16.21:

  FUNCTION func1(self) RESULT(pLL_dest)
                     1
Warning: Unused dummy argument 'self' at (1)
$ gfortran -c main.f90 -std=f2003 -Wall -fbounds-check
$ gfortran -o ex_gfortran moo.o main.o  -std=f2003 -Wall -fbounds-check
$ ./ex_gfortran 
 associated(pLL_s) = F
 associated(pLL_d) = F
 func1..
 hello from func1
 associated(pLL_s) = F
 associated(pLL_d) = F
 func2..
 hello from func2
 flg= T
 associated(pLL_s) = F
 associated(pLL_d) = F
 normal end
$ 

ifort的行为是否符合标准,或 gfortran只是优雅的行为?或者,标准没有 对这一点说什么呢?

我注意到这两个编译器都是旧版本,我猜 较新的版本可能会显示不同的行为。

1 个答案:

答案 0 :(得分:2)

这两个编译器在这里都以合法的方式运行。也就是说,代码有问题但不是需要编译器提供诊断的方式。

查看指针赋值语句

pLL_d => pLL_s%func1()

这是对func1类型的绑定名LL的引用。但是,在此处执行到达时pLL_s没有关联。因此不允许使用过程参考。根据Fortran 2008 12.5.1:

  

procedure-designator 中的 data-ref 不应是......与之无关的指针。

由于这不是编号约束,程序员有责任确保合规。

继续将这个问题转移到一般问题"是将空指针传递给函数和子程序有效吗?",答案是"是的,只要它的使用不&# 39;违反通常的条件"。

总之,虽然在概念上是参考

pLL_d => pLL_s%func1()   ! Using func1 as a binding name

就像

中的那样
pLL_d => func1(pLL_s)    ! For the module's procedure func1

问题不在于pLL_s是一个没有关联的指针实际参数。后一种形式是允许的,但前者不是。 1

对这个问题感兴趣的是关于函数结果定义的要求。对于func1,函数结果是指针,因此必须定义结果的关联状态。如果函数由其绑定名称引用,则必须定义self的关联状态。实际上,self是关联的。

1 这实际上有点过于简单化了。虽然可以允许一个过程是一个非关联指针的实际参数,但这并不适用于这些过程。

查看伪参数的声明

    CLASS(LL), TARGET, INTENT(IN) :: self

这里self是一个非可选的非指针虚拟对象。要成为与指针实际参数相关联的参数,实际参数必须与指针相关联。因此,函数不是正确处理伪参数为空指针的情况的函数"。

那就是说,像

这样的陈述没有错
pLL_d => pLL_s%func1(ptr) ! or
pLL_d => func1(pLL_s, ptr)

pLL_s指针关联,ptr与指针虚拟对应的潜在非关联实际。传递对象伪参数实际上只是一个非常特殊的情况。