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

Changing intent(in) variables through a pointer

我想知道是否 legal/safe 通过与相应实际参数关联的指针修改 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

技巧是一个包含指针和目标的用户定义类型,前者指向后者。 this 的实例作为 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

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

伪参数var是一个非指针对象,这意味着它的组件也具有相同的意图属性。您正在尝试通过赋值 var%A2(:,:) = 42 定义 var 及其组件 var%A1 因为 var%A2 是与 my%A1 关联的指针(通过 [=11 的参数关联=] 与 my);通过var和[=18=的参数关联,my%A1的定义就是var%A1的定义。

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

The INTENT (IN) attribute for a nonpointer dummy argument specifies that it shall neither be defined nor become undefined during the invocation and execution of the procedure.

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

A nonpointer object with the INTENT (IN) attribute shall not appear in a variable definition context (19.6.7).

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

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

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