2

我对以下程序有些困惑

module test
   implicit none

   type TestType
      integer :: i
   end type

contains
   subroutine foo(test)
      type (TestType), intent(out) :: test
      test%i = 5 
   end subroutine

   subroutine bar(test)
      type (TestType), intent(out) :: test
      test%i = 6 
   end subroutine

end module

program hello
   use test
   type(TestType) :: t

   call foo(t)
   print *, t%i 
   call bar(t)
   print *, t%i 
end program hello

及其衍生物。稍后再谈。正如我们所知,Fortran 将例程参数作为传递引用传递,这意味着实体出现在test两者的虚拟参数处,foo并且bar是在program hello. 到目前为止,一切都很好。

假设我在 as 中定义program hello一个type(TestType) :: t指针,并分配它。

program hello
   use test
   type(TestType), pointer :: t

   allocate(t)

   call foo(t)
   print *, t%i
   call bar(t)
   print *, t%i

   deallocate(t)
end program hello

代码和以前一样工作,唯一的区别是对象不是在堆栈上分配,而是在堆上。

现在假设回到堆栈分配的程序,子程序 bar 被定义为

 subroutine bar(test)
    type (TestType), pointer :: test
    test%i = 6 
 end subroutine

该程序不再编译,因为您必须使用堆分配版本才能使其工作,或者更准确地说,当例程被定义为接受指针作为虚拟参数时,必须将指针传递给例程。另一方面,如果虚拟参数不包含pointer关键字,则例程将接受指针和非指针。

这让我想知道......将虚拟参数声明为指针有什么意义?

4

1 回答 1

5

转自 comp.lang.fortran,Tobias Burns 的回答:

现在假设回到堆栈分配的程序,子程序 bar 被定义为

子程序 bar(test) 类型 (TestType), 指针:: test test%i = 6 结束子程序

该程序不再编译,因为您必须使用堆分配的版本才能使其工作,

这不太正确:您也不能将 ALLOCATABLE 变量传递给具有 POINTER 属性的虚拟变量。我认为一个(实际的)原因是指针地址可以转义,因此会导致别名问题。一个正式的原因是 ALLOCATABLE 根本不是指针。此外,该标准没有讨论堆与堆栈与静态内存。事实上,本地数组 [具有常量边界] 通常会在静态内存中创建,而不是在堆栈上(除非您使用 OpenMP 或 RECURSIVE 属性)。因此,您的“堆栈”示例也可能是“静态内存”示例,具体取决于编译器和使用的选项。

或者更准确地说,当例程被定义为接受指针作为虚拟参数时,必须将指针传递给例程。

这也不完全正确。在 Fortran 2008 中,您可以将具有 TARGET 属性的非指针传递给具有 INTENT(IN) 属性的指针虚拟对象。(指针意图是相对于指针关联状态;对于非指针假人,意图是关于存储在变量中的值。)

这让我想知道......将虚拟参数声明为指针有什么意义?

好吧,如果参数具有 POINTER 属性,您可以分配和释放指针目标,您可以将指针与某个目标等相关联。直到 Fortran 95,不可能有 ALLOCATABLE 虚拟参数,因此必须使用指针,如果必须在过程中分配(虚拟)参数。

如果可以,您应该尝试使用 ALLOCATABLEs 而不是 POINTERs - 它们更易于使用,不会泄漏内存并且不会对编译器造成别名分析问题。另一方面,如果你想创建,例如,一个链表,你需要一个指针。(不过,对于堆使用,也可以使用 Fortran 2008 的可分配组件。*)

*I mean:
   type t
       type(t), allocatable :: next
   end type

其中组件的类型与定义的类型相同;在 F2008 之前,这仅允许用于指针,但不允许用于可分配。

R.缅因州

正如我们所知,Fortran 将例程参数作为传递引用传递,

那么,我们显然知道不正确。该标准从不指定这一点,并且确实非常规避此类规范。尽管您的误解是一个常见的误解,但即使在大多数较旧的编译器中,它也不是严格准确的,尤其是在启用优化的情况下。严格的按引用传递会扼杀许多常见的优化。

使用最近的标准,在某些情况下几乎不允许引用传递。该标准在其规范性文本中没有使用这些词,但有些事情通过引用传递是不切实际的。

当你开始研究指针之类的东西时,假设一切都是按引用传递的错误将开始变得比以前更加明显。你必须放弃这种误解,否则很多事情会让你感到困惑。

我认为其他人已经充分回答了帖子的其余部分。有些人也提到了上述观点,但我想强调一下。

希望这能回答你的问题。

于 2010-10-15T16:29:01.303 回答