将空指针实际参数传递给派生类型的成员过程
Passing a null pointer actual argument to a member procedure of a derived type
正在将空指针传递给有效的函数和子例程
Fortran 2003 标准?请假设被调用的函数和
子例程可以正确处理伪参数为
一个空指针。我对这种功能和
子例程是 'member' 派生类型的函数和子例程。
我想避免检查每个指针的关联状态
如果标准允许,则调用方,就像我不允许的一样
必须单独处理零大小数组。同时,我不想
依赖于标准未指定的行为。
就我尝试使用以下示例而言,ifort
和 gfortran
显示
关于这一点的不同行为。
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
与指针虚拟对象对应的潜在非关联实际值。传递对象伪参数实际上只是一种非常特殊的情况。
正在将空指针传递给有效的函数和子例程 Fortran 2003 标准?请假设被调用的函数和 子例程可以正确处理伪参数为 一个空指针。我对这种功能和 子例程是 'member' 派生类型的函数和子例程。
我想避免检查每个指针的关联状态 如果标准允许,则调用方,就像我不允许的一样 必须单独处理零大小数组。同时,我不想 依赖于标准未指定的行为。
就我尝试使用以下示例而言,ifort
和 gfortran
显示
关于这一点的不同行为。
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
与指针虚拟对象对应的潜在非关联实际值。传递对象伪参数实际上只是一种非常特殊的情况。