问题正是标题:Fortran 允许在SUBROUTINE
即使有的情况下更改派生类型数据成员INTENT(IN)
,如何保护数据?我对 Fortran 还很陌生,尽管INTENT(IN)
派生类型伪参数具有属性,但这种允许改变派生类型数据成员的行为让我非常担心。有没有办法用编译器捕获这些不安全的突变?例如,gfortran -Wall
似乎没有这样做,但也许这是遗留甚至现代代码库中一个相当常见的问题,以至于可以通过某种方式对问题进行某种静态检测?
示例程序:
! @file main.f90
!
! @purpose: Show data members of derived types are mutable in SUBROUTINE
!
! @compile: gfortran -Wall main.f90
PROGRAM test
IMPLICIT NONE
INTEGER :: n_states = 1
INTEGER :: len_state = 5
INTEGER :: i, j
TYPE t_states
INTEGER, POINTER, CONTIGUOUS :: state(:) ! (len_state,)
END TYPE t_states
TYPE(t_states), ALLOCATABLE :: states(:) ! (n_states,)
ALLOCATE(states(1:n_states))
! Populate allocated array of states
DO i = 1, n_states
ALLOCATE(states(i)%state(len_state))
DO j = 1, len_state
states(i)%state(j) = j
PRINT *, states(i)%state(j)
END DO
PRINT *
END DO
CALL unsafe_mutation(states(1))
! Print state array after modification
DO j = 1, len_state
PRINT *, states(1)%state(j)
END DO
CONTAINS
! Arbitrary update of state data member even though INTENT(IN)!!!
SUBROUTINE unsafe_mutation(p_states)
TYPE(t_states), INTENT(in) :: p_states
p_states%state(1) = -1
END SUBROUTINE
END PROGRAM test
输出:
1
2
3
4
5
-1
2
3
4
5
根据语言规则,示例代码不会导致
p_states
哑参数本身的值发生变化 - 相反,改变的是哑参数的组件“指向”的某个东西的值p_states
。指针组件所引用的东西(指针组件的目标)不被视为具有该组件的对象的值的一部分。
现代 Fortran 中的指针通常用于引用事物。如果您想要具有指针的动态特性(可以在运行时以不同的特性创建和销毁)但行为像值的一部分而不是引用的东西,那么请使用 ALLOCATABLE 组件。
标准方面——参见(所有 F2023 参考):
INTENT属性描述(8.5.10)下的注释3,几乎直接回答了这个问题;
INTENT 属性如何传播到非指针对象的子对象(也见 8.5.10);
子对象的定义(9.4.2)对具有指针属性的组件的数量、位置和使用性质有所限制;
派生类型的值的定义(7.5.8)不包括指针组件引用的对象的值,但包括这些组件的指针关联状态。