指针作为伪参数

发布于 2024-09-27 17:54:40 字数 1305 浏览 3 评论 0原文

我对以下程序

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 将例程参数作为引用传递进行传输,这意味着在 foobar 的虚拟参数 test 处出现的实体code> 与 program hello 中堆栈上授予的内存空间相同。到目前为止,一切都很好。

假设我在 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

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

现在假设返回到堆栈分配的程序,并且该子例程栏被定义为该

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

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

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

I am somewhat puzzled by the following program

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

and its derivatives. More on those later. As we know, Fortran transfers routine arguments as a pass-by-reference, meaning that the entity emerging at the dummy argument test for both foo and bar is the same memory space granted on the stack in program hello. So far so good.

Suppose I define in program hello the type(TestType) :: t as a pointer, and allocate it.

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

The code works as before, the only difference being that the object was not allocated on the stack, but on the heap.

Now assume to go back to the stack-allocated program and that subroutine bar is instead defined as

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

The program does not compile anymore because you must use the heap-allocated version to make it work, or to be more accurate it is mandatory to pass a pointer to the routine when the routine is defined to accept a pointer as a dummy argument. On the other hand, if the dummy argument does not contain the pointer keyword, the routine would accept both pointers and non-pointers.

This makes me wonder... what's the point of declaring a dummy argument a pointer ?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

长发绾君心 2024-10-04 17:54:40

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

现在假设返回到堆栈分配的程序,并且
子例程栏被定义为

子程序栏(测试)
类型(TestType),指针::测试
测试%i = 6
结束子程序

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

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

或者更准确地说是
定义例程时必须传递指向例程的指针
接受指针作为虚拟参数。

这也不完全正确。在 Fortran 2008 中,您可以传递
非 POINTER,具有 TARGET 属性,指向虚拟指针,该虚拟指针
具有 INTENT(IN) 属性。 (指针意图是相对于指针
协会地位;对于非指针虚拟对象,其意图是关于
值存储在变量中。)

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

那么,如果参数具有 POINTER 属性,您可以分配并
释放指针目标,您可以将指针与某个目标关联起来
等。直到 Fortran 95,不可能有 ALLOCATABLE dummy
因此,如果必须使用(虚拟)参数,则必须使用指针
在程序中分配。

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

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

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

作者:R. Maine

众所周知,Fortran 将例程参数作为
通过引用传递,

那么我们显然不正确地知道了。该标准从未明确指出
而且,确实不遗余力地避免这样的规范。
虽然你的想法是一个常见的误解,但严格来说并不准确
即使在大多数较旧的编译器中,尤其是在打开优化的情况下也是如此。
严格的引用传递会杀死许多常见的优化。

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

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

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

希望这能回答您的问题。

Reposted from comp.lang.fortran, an answer by Tobias Burns:

Now assume to go back to the stack-allocated program and that
subroutine bar is instead defined as

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

The program does not compile anymore because you must use the
heap-allocated version to make it work,

That's not quite correct: You can also not pass an ALLOCATABLE variable
to a dummy with POINTER attribute. I think one (practical) reason is
that the pointer address can escape and you would thus cause alias
problems. A formal reason is that an ALLOCATABLE is simply not a
POINTER; additionally, the standard does not talk about heap vs. stack
vs. static memory. And in fact, local arrays [with constant bounds] will
often be created in static memory and not on the stack (unless you use
OpenMP or the RECURSIVE attribute). Thus, your "stack" example could
also be a "static memory" example, depending on the compiler and the
used options.

or to be more accurate it is
mandatory to pass a pointer to the routine when the routine is defined
to accept a pointer as a dummy argument.

That's also not completely true. In Fortran 2008 you can pass a
non-POINTER, which has the TARGET attribute, to a pointer dummy which
has the INTENT(IN) attribute. (Pointer intent is relative to the pointer
association status; for non-pointer dummies the intents are about the
value stored in the variable.)

This makes me wonder... what's the point of declaring a dummy argument
a pointer ?

Well, if the argument has the POINTER attribute, you can allocate and
free the pointer target, you can associate the pointer with some target
etc. Up to Fortran 95 it was not possible to have ALLOCATABLE dummy
arguments thus a pointer had to be used if a (dummy) argument had to be
allocated in a procedure.

If you can, you should try to use rather ALLOCATABLEs than POINTERs -
they are easier to use, do not leak memory and have pose no
alias-analysis problems to the compiler. On the other hand, if you want
to create, e.g., a linked list, you need a pointer. (Though, for a heap
usage, also Fortran 2008's allocatable components could be used.*)

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

where the component is of the same type as the type being defined;
before F2008 this was only allowed for pointers but not for allocatables.

and by R. Maine

As we know, Fortran transfers routine arguments as a
pass-by-reference,

We apparently know incorectly, then. The standard never specifies that
and, indeed goes quite a lot out of its way to avoid such specification.
Although yours is a common misconception, it was not strictly accurate
even in most older compilers, particularly with optimization turned on.
A strict pass-by-reference would kill many common optimizations.

With recent standards, pass-by-reference is all but disallowed in some
cases. The standard doesn't use those words in its normative text, but
there are things that would be impractical to implement with
pass-by-reference.

When you start getting into things like pointers, the error of assuming
that everything is pass-by-reference will start making itself more
evident than before. You'll have to drop that misconception or many
things wil confuse you.

I think other people have answered the rest of the post adequately. Some
also addressed the above point, but I wanted to emphasize it.

Hope this answers your question.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文