1

我有一个带有例程的 FORTRAN 代码:

SUBROUTINE READ_NC_VALS(NCID, RECID, VARNAME, VARDATA)
integer ncid, recid
character*(*) varname
real*8 vardata
dimension vardata(15,45,75)

etc.

我想为这段代码增加一些灵活性,我想我会先添加一个可选的标志参数来做到这一点:

SUBROUTINE READ_NC_VALS(NCID, RECID, VARNAME, VARDATA, how_to_calculate)

! everything the same and then ...
logical, optional :: how_to_calculate

现在,在这一点上,我什至没有使用“how_to_calculate”。我只是将其放入代码中进行测试。所以我毫不费力地编译了代码。然后我运行它,我在子程序中得到一个错误。具体来说,稍后代码中的某些值会“神奇地”从没有该可选参数的情况下改变。新值对代码的逻辑没有意义,因此它会礼貌地退出并显示错误消息。让我再次强调,在这一点上,我什至没有使用这个可选争论。因此,在云雀中,我回到源代码中调用这个例程的所有地方,即使我的新参数是可选的,我在所有调用中都为其输入了值。当我这样做时,代码运行良好。那么这是什么一回事?仅在子例程中存在未使用的可选参数怎么会导致其他数据被破坏?为这个可选参数添加输入参数如何再次解决问题?顺便说一下,这是用 PGI 编译的。

有任何想法吗?谢谢。

顺便说一句,很抱歉没有提供更多代码。如果我这样做,我的老板可能对我不太满意。我不制定规则;我只是在这里工作。

4

2 回答 2

4

Fortran 中的可选参数是通过0为每个调用子例程没有提供值的可选参数传递(空指针)来实现的。因此,采用可选参数的子例程必须:

  • INTERFACE要么在调用子程序中有明确的定义
  • 或者是一个模块级的子程序(它们的接口是自动生成的)

如果您向子例程添加一个可选参数,但它在调用者中既没有接口也不是模块级子例程,那么编译器将不会生成正确的调用序列 - 它会传递比预期更少的参数。这可能会在 Unix 系统上造成问题,其中 PGICHARACTER*(*)在参数列表的末尾传递所有参数的长度(在 Windows 上,它将长度作为字符串地址之后的下一个参数传递)。一个缺失的参数会改变堆栈中的长度参数位置(或将它们放在 x64 上的错误寄存器中),从而VARNAME导致READ_NC_VALS. 这可能导致各种不良行为,包括覆盖内存和“神奇地”更改不应根据程序逻辑更改的值。

于 2012-07-10T11:53:08.687 回答
1

显然,只需向子例程添加一个虚拟参数,无论有无optional属性,都不会导致任何问题。但是,可能发生的情况是,更改暴露了代码的其他一些问题,这些问题已经存在,但没有造成任何明显的不良影响。

没有更多的代码,我们所能做的就是猜测,这通常不是很有用。想到的一件事是在使用可选参数时显式接口的必要性。代码是否组织成模块?

于 2012-07-09T22:47:49.007 回答