通过指针改变intent(in)变量

Jel*_*lby 4 fortran pointers

intent(in)我想知道通过与相应实际参数关联的指针修改虚拟参数是否合法/安全。特别是在子例程调用之前建立指针关联时。

例如,以下内容可以吗(它似乎与 gfortran 一起工作正常)?

program test
implicit none

type compound
  integer, allocatable :: A1(:)
  integer, pointer :: A2(:,:)
end type compound

type(compound), target :: my
integer :: n=5, i

allocate(my%A1(n**2))
my%A2(1:n,1:n) => my%A1(:)

do i=1,n**2
  my%A1(i) = i
end do

do i=1,n
  print *, my%A2(i,:)
end do

call assign_A(my)

do i=1,n
  print *, my%A2(i,:)
end do

contains

subroutine assign_A(var)
type(compound), intent(in) :: var
var%A2(:,:) = 42
end subroutine

end program test
Run Code Online (Sandbox Code Playgroud)

诀窍是用户定义的类型包含一个指针和一个目标,前者指向后者。它的实例作为传递intent(in),并在子例程中通过指针组件对其进行修改(intent(in)可以修改指针指向的值)。我对它的工作原理感到有点惊讶,但也许这只是编译器缺少诊断。如果我直接改变var%A1那当然会失败。

编辑:上述程序的输出(用 gfortran 7.5.0 编译):

       1           6          11          16          21
       2           7          12          17          22
       3           8          13          18          23
       4           9          14          19          24
       5          10          15          20          25
      42          42          42          42          42
      42          42          42          42          42
      42          42          42          42          42
      42          42          42          42          42
      42          42          42          42          42
Run Code Online (Sandbox Code Playgroud)

fra*_*lus 9

不允许您以intent(in)这种方式更改对象的值。编译器不必(能够)告诉您您做错了什么。(此外,对编译器应该能够检测到进行非法更改的所有情况的期望是不合理的。)

虚拟参数var是一个非指针对象,这意味着它的组件也具有相同的意图属性。您试图通过赋值来定义var及其组件,因为是 与 关联的指针(通过with的参数关联);通过和的参数关联, 的定义是 的定义。var%A1var%A2(:,:) = 42var%A2my%A1varmyvarmymy%A1var%A1

这违反了以下声明 (F2018 8.5.10 p2):

非指针虚拟参数的 INTENT (IN) 属性指定它在过程的调用和执行期间既不会被定义也不会变得未定义。

但是,这不是编号约束或语法规则,因此编译器不需要能够诊断违规情况。其中要求编译器能够诊断更改varis 的违规 (F2018 C844 (R826)):

具有 INTENT (IN) 属性的非指针对象不应出现在变量定义上下文中 (19.6.7)。

变量定义上下文是特定的,问题的赋值语句不是varor的变量定义上下文var%A1。诸如var%A1=42(您注意到编译器抱怨的)之类的赋值是在变量定义上下文中出现var%A1(但不是)。var

您还可能想知道别名限制是否起作用。

总之,可以intent(in)从编译器中隐藏对虚拟变量的更改。正如程序员不喜欢被欺骗一样,如果您这样做,您的编译器也不必仍然是您的朋友。编译器可以假设var在子例程期间不会更改值,并且如果您找到一种方法来破坏它,那么它不欠您任何东西。