Fortran:检测传递为虚拟参数的空指针

3
我想在子程序内检测带有intent(in)的虚参是否为 null 指针:
program testPTR
  
implicit none
  
integer, target :: ii
integer, pointer :: iPtr
  
  iPtr => ii
  iPtr = 2
  
  print *, "passing ii"
  call pointer_detect(ii)
  
  print *, "passing iPtr"
  call pointer_detect(iPtr)
  
  iPtr => null()
  print *, "passing iPtr => null()"
  call pointer_detect(iPtr)
  
contains
                                                                                                                                                                      
  subroutine pointer_detect(iVal)
      implicit none
      integer, intent(in), target :: iVal
      integer, pointer :: iPtr
      character(len = *), parameter :: sub_name = 'pointer_detect'
    
      iPtr => iVal
      if (associated(iPtr)) then
        print *, "Pointer associated. Val=", iVal, ", iPtr = ", iPtr
      else
        print *, "Pointer not associated. Val=", iVal, ", iPtr = ", iPtr
       endif
  
  end subroutine pointer_detect
  
end program

令我惊讶的是它可以在gfortran-9和gfortran-12上工作。然而我有一些问题:

  1. 这个检查合法、可移植且符合Fortran标准吗?
  2. 由于某种原因,在最后一个打印处它并没有发生段错误,而是打印了0并干净地退出:
$ gfortan test.f90
$ ./a.out && echo ok
passing ii
 Pointer associated. Val=           2 , iPtr =            2
 passing iPtr
 Pointer associated. Val=           2 , iPtr =            2
 passing iPtr => null()
 Pointer not associated. Val=           0 , iPtr =            0
ok
$ 

有什么想法吗?谢谢!


可能相关,但也许不是重复的问题,https://dev59.com/98Tra4cB1Zd3GeqP4FSc#72140728 - High Performance Mark
2
这个检查根本不具备可移植性:你的程序不是有效的Fortran,因此任何Fortran编译器都可以随意处理,而你无法提出异议。 - francescalus
1
哦,还有一个必须的问题:为什么现在在Fortran中使用指针?或者你只是想把这种语言拉伸成它不喜欢的形状来玩乐吗?(这没什么问题,只是想弄清楚你所问的背后是否还有其他问题。) - High Performance Mark
我同意@francescalus的观点,如果指针为null(),则调用该例程不符合标准。但它无法在编译时检测到。如果有适当的编译选项,可能可以在运行时检测到。 - PierU
1
@HighPerformanceMark 在某些情况下仍有使用指针的非常有效的原因,但我认为问题不在于此。OP想要了解指针的行为。 - PierU
谢谢!事实上,我已经试图说服我的同事永远不要使用这样的检查,但令我惊讶的是程序通过了。我只是在寻找更多的论据。(现在我有了!:) - Roux
2个回答

6

该片段

  iPtr => null()
  print *, "passing iPtr => null()"
  call pointer_detect(iPtr)

违反Fortran标准会使您的程序无效(Fortran 2008,25.5.2.3):

除了对内在查询函数的引用外,与非可选非指针虚拟参数相对应的指针实际参数必须与目标关联。

非内在过程的虚拟参数既不是可选的也不是指针。

避免此问题的责任完全由程序员承担,编译器没有责任为您检测这些错误的代码。

如果需要,编译器通常可以在运行时检测到此类错误:

At line 19 of file brokenpointer.f90
Fortran runtime error: Pointer actual argument 'iptr' is not associated

使用gfortran编译选项-fcheck=pointer时的输出,或者。
forrtl: severe (408): fort: (7): Attempt to use pointer IPTR when it is not associated with a target

使用ifort的-check pointers选项。

程序员无法在过程内可靠地进行类似的检查,因为Fortran编译器不必遵守以这种方式违反规则的程序员。

例如,查看此处过程的努力:

      iPtr => iVal
      if (associated(iPtr)) then

iVal不是一个指针,因此,在该指针赋值中,iPtr与该变量相关联。编译器可以假设您没有违反Fortran的规则,因此iPtr被关联,并且该测试条件始终为真。在任何有效的Fortran程序中,该测试条件都不能解决为false。

然而,不要失去所有希望。我引用标准中的文本与“非指针”这个说法不同,它说的是“非可选项”。如果iVal是可选的,则使用PRESENT()

  subroutine pointer_detect(iVal)
      implicit none
      integer, intent(in), optional :: iVal
      character(len = *), parameter :: sub_name = 'pointer_detect'
    
      if (present(iVal)) then
        print *, "Actual argument pointer was associated. Val=", iVal
      else
        print *, "Actual argument pointer was not associated."
      endif
  
  end subroutine pointer_detect

如果与未关联的指针实参相关联,非指针、非可分配、可选虚拟参数将被视为不存在。

但请注意,如果iPtr处于未定义的关联状态,则此方法无效。任何方法都无法解决这个问题。


3
如果指针为空,call pointer_detect(iPtr)不符合标准。例程pointer_detect()的命名不恰当,因为它不能检测有关原始指针的任何信息,该原始指针根本未被传递:虚拟参数不是指针,因此在调用时编译器将传递iPtr的目标地址,而不是iPtr本身。但是,如果iPtr为空,则它没有目标:行为会变得不确定。
未定义行为是不确定的,或者说是不可预测的。它可能会崩溃,也可能输出不可预测的值等等...这种违反标准的行为实际上无法在编译时检测到,并且编译器也不需要这样做。gfortran可能具有编译选项,以在运行时进行检测(但会带来性能惩罚,就像任何其他运行时检查一样)。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接