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

Passing a null pointer actual argument to a member procedure of a derived type

正在将空指针传递给有效的函数和子例程 Fortran 2003 标准?请假设被调用的函数和 子例程可以正确处理伪参数为 一个空指针。我对这种功能和 子例程是 'member' 派生类型的函数和子例程。

我想避免检查每个指针的关联状态 如果标准允许,则调用方,就像我不允许的一样 必须单独处理零大小数组。同时,我不想 依赖于标准未指定的行为。

就我尝试使用以下示例而言,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 的行为是否优雅?或者,标准没有 关于这一点有什么要说的吗?

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

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

看指针赋值语句

pLL_d => pLL_s%func1()

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

The data-ref in a procedure-designator shall not be ... a pointer that is not associated.

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

超越这个问题继续讨论一般问题 "is passing a null pointer to a function and a subroutine valid?",答案是 "yes, as long as its use doesn't violate the usual conditions"。

综上所述,虽然在概念上参考

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是一个非可选的非指针虚拟对象。要将实参与指针实参相关联,实参必须与指针相关联。因此,函数不是 "correctly handle the case in which the dummy argument is a null pointer".

也就是说,像

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

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