FORTRAN 77 中子例程参数的范围

Scope of subroutine parameters in FORTRAN 77

我有一个声明 i 并将其传递给其他小子程序的子程序。在这些小子程序中,声明了其他同名变量,即 i,并在内部使用。一旦离开小子程序,人们会期望具有与最初传递的相同的 i 值,但这不是真实情况,并且 i 包含在小子程序中分配的最后一个参数的值.这是一个简短的例子:

subroutine smallSub1(i)
 integer i,start,end
 start=i
 end = start +10
 do i=start, end
    write(*,*) i
 enddo
end subroutine smallSub1

subroutine caller
integer i
i = 1
call smallSub1(i)
write(*,*) i
end subroutine caller

我的问题是,如何在 F77 中避免这种行为?

这里我关心的是:考虑这样一种情况,子例程是一个黑盒子,您只需要传递一个整数,但您不希望该整数值从 smallSub1 子例程中改变。变量 startend 将取决于 i 的值,但是它们不应替换 smallSub1

i 的原始值

来自 https://gcc.gnu.org/onlinedocs/gfortran/Argument-passing-conventions.html Fortran 通过引用传递大多数参数

因此,如果不想让变量改变,就不要在子程序中改变它。您可以将变量 smallSub1(i) 重命名为 smallSub1(j) 你不想改变整个功能。

在给定的代码示例中有两个变量i:每个子例程一个。在子程序 caller 中,i 是一个局部变量,而在子程序 smallSub1 中,它是一个伪参数。

当你有 call smallSub1(i) 时,你通过参数关联将两个 i 变量相互关联。那么,在这个简单的例子中,smallSub1i 的任何更改都会影响 caller 中的 i。这里有参数关联的工作原理。

传统上确实有黑匣子,当不需要时,子例程中的参数会被更改。用作作品的地方——例如space。在那种情况下,人们会做类似

的事情
inew = i
call smallSub1(inew)
... continue using i

然而,在这种情况下,可以很容易地(我想)更改子例程。引入一个额外的局部变量:

subroutine smallSub1(i)
  integer i   ! Dummy argument - we don't want to change it
  integer start,end
  integer j   ! Local variable - we're quite free to change it
              ! In general, we'd have j=i at the start and use that instead
  start=i
  end = start +10
  do j=start, end
     write(*,*) j
  enddo
end subroutine smallSub1

现代 Fortran 甚至具有 value 属性,可以将其应用于伪参数,允许在不影响实际参数的情况下更改它。

如另一个答案中所述,Fortran 通常通过引用传递,当不可能时,它会执行类似 copy-in/copy-out 的操作。正如其他人在评论中简明扼要地说的那样,如果你不想改变 i 那么就不要改变 i.

在您的子例程 smallSub1 中,i 被用作循环迭代变量,并且您不希望其值更改对调用者可见。更改 icaller 可见的原因是因为 i 不是局部变量,它是一个伪参数。解决方案是用不同的名称调用循环变量和伪参数。一种这样的解决方案是:

subroutine smallSub1(i_from_caller)
  integer  i,i_from_caller,start,end
  start = i_from_caller
  end = start +10
  do i=start, end
     write (*,*) i
  end do
end subroutine smallSub1

在这种情况下,虚拟参数已重命名为 i_from_caller 并用于初始化 start。现在循环变量 i 是子程序真正的局部变量(因为它不再是伪参数的名称)并且在这里更改 i 不会更改 [=17= 中的 i ]


为了帮助避免这种行为,您可以向编译器提示伪参数用于输入、输出或两者。如果您在原始示例中的 smallSub1 中将 i 声明为 integer, intent(in) :: i,则编译器会抱怨:

     do i=start, end
       1
Error: Dummy argument ‘i’ with INTENT(IN) in variable definition context (iterator variable) at (1)

让您意识到您正在对虚拟参数进行不需要的更改。