Fortran 是否有未定义的行为?

发布于 01-13 10:55 字数 228 浏览 1 评论 0原文

在C 和C++ 中,有许多操作会导致未定义的行为,即允许编译器做任何它想做的事情的情况。 示例包括在释放变量后使用变量、释放变量两次以及取消引用空指针。

Fortran 也有未定义的行为吗?我查看了一份规范草案,但没有找到任何内容。例如,在释放变量后使用变量是否一定会导致程序崩溃,还是可能默默地做错误的事情?

In C and C++, there many operations that cause undefined behavior, i.e. situations that allow the compiler to do anything it wants. Examples include using a variable after deallocating it, deallocating a variable twice and dereferencing null pointers.

Does Fortran also have undefined behavior? I had a look at a specification draft, but failed to find anything in there. For instance, is using a variable after its deallocation guaranteed to crash the program, or may it silently do the wrong thing?

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

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

发布评论

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

评论(3

独享拥抱2025-01-20 10:55:16

是的,确实如此。只是叫法不同而已。您可以做很多事情,并且会使您的代码不符合标准,为此,处理器(编译器)不需要诊断这种不符合性(当然,许多偏差必须被诊断)。通常,情况与 C 未定义行为类似(例如访问数组越界、有符号整数溢出……)。我们只是说该代码不符合标准,这意味着该标准没有规定此类代码的结果。此类代码并未涵盖,而是标准的,因此如果某些编译器(处理器)确实编译了它并且您确实运行了它,则可能会产生任何结果。

这与依赖处理器的行为不同,它是标准的并且仅依赖于实现。

只需在 StackOverflow 上搜索即可找到大量示例。就像 将相同的实体传递给具有不同意图的参数是否是未定义的行为? Fortran 和 MPI_Reduce 如何处理整数溢出?

这个答案只是回答了所提出的问题,但并不试图列出 Fortran 中可能发生的所有可能类型的 UB。

Yes, it has. It is just called differently. There are many things that you could do and will make your code not standard conforming, for which there is no requirement to for the processor (compiler) to diagnose such non-conformance (of course, many deviations must be diagnosed). Often the situations will be similar to C undefined-behaviour one (like accesing an array out-of-bounds, signed integer overflow,...). We just say that the code is not standard conforming, that means the standard does not prescribe the outcome of such a code. Such code is not covered but the standard and so anything can result if some compiler (processor) does compile it and you do run it.

That is different from processor dependent behaviour, that one is standard and just implementation dependent.

Just searching here at StackOverflow should give you plenty of examples. Like Is passing the same entity to arguments with different intent undefined behavior? How do Fortran and MPI_Reduce deal with integer overflow?

This answer just answers the question asked but does not attempt to list all possible kinds of UB that can happen in Fortran.

倦话2025-01-20 10:55:16

Fortran 标准确实承认与 C 和 C++ 的“未定义行为”概念类似的概念。支持损坏的 Fortran 代码的人们经常将其表述为“编译器现在可能会引发第三次世界大战”或类似的说法。

Fortran 语言规范有两个一致性想法(参见 Fortran 2018, 4.2)。主要问题是程序必须是什么样子才能被视为 Fortran 程序。第二个是处理器必须执行哪些操作来响应提交的程序单元才能被视为 Fortran 处理器。

如果要求符合 Fortran 的处理器处理非 Fortran 程序的内容,该标准很少说明必须发生什么。有些诊断是必须提供的,但通常没有。

在“在释放变量后使用变量”的情况下,尝试执行此操作违反了定义 Fortran 程序的语言标准部分。然后,编译器可以在不违反 Fortran 标准的情况下“发动第三次世界大战”,因为 Fortran 标准并没有说它不能(或必须做其他事情)。

现在,我们如何查看Fortran标准文档并确定不完全Fortran程序是否具有特定的所需效果? 4.2 中的文本提到了编译器必须具有“检测和报告提交的程序单元内的使用情况的能力”的多种情况。如果你提出的计划没有达到其中任何一个目标,那么你就处于“未定义”的领域。

程序错误必须可报告的主要时间是在这种情况下

在提交的程序单元内使用编号语法规则或约束不允许的形式或关系

让我们任意考虑 Fortran 2018, 15.5.1C1523 (R1520) 过程引用的语法。我们看到类似“R1520”的东西:

R1520函数引用过程指示符([actual-arg-spec-list])

和“C1523”:

C1523 (R1520)过程指示符应指定一个函数。

在我们列出以下内容之前:

过程指定符中的数据引用不应是未分配的可分配变量或未关联的指针。

在本例中,规则 R1520、编号约束 C1523(适用于该规则)和以下文本给出了对 Fortran 程序的约束。如果您提交的程序不符合这些要求,则它不是合格的 Fortran 程序。

要求处理此类不合格程序(该程序违反 R1520 或 C1523)的编译器必须能够检测到这一点(基于上述内容)。编译器不必抱怨或检测未编号文本的违规行为。可以假设它提供的任何程序都不会违反这种未编号的限制。

我在这里引用的这个(巧合)是禁止程序错误地使用先前释放的变量的一个示例。

如果处理器以“编译然后运行”的方式操作,则编号的规则/约束通常是可以“在编译时”评估的规则/约束。

未定义行为的另一个具体且重要的示例是使用变量而不首先给它一个值(“定义”它)。 Fortran 标准简单地说(Fortran 2018 9.2 p2):

仅当定义了变量时才允许引用。仅当指针与已定义的目标对象关联时,才允许对数据指针的引用。当 19.6.5 中描述的事件发生时,变量将被定义一个值。

这不是编号语法规则或约束,而是编译器允许假设已满足的程序的(重大)负担。

The Fortran standard does admit similar concepts to the idea of "undefined behaviour" of C and C++. It's often phrased by people supporting broken Fortran code as "the compiler may now start World War III", or similar.

The Fortran language specification has two ideas of conformance (see Fortran 2018, 4.2). The main one is what a program must look like to be able to considered a Fortran program. The second is what a processor must do in response to a submitted program unit to be considered a Fortran processor.

If a conforming Fortran processor is asked to process something that is not a Fortran program, the standard rarely says what must happen. There are some diagnoses that must be offered, but often there is not.

In the case of "using a variable after its deallocation", an attempt to do this is a violation of that part of the language standard that defines the Fortran program. A compiler may then "start World War III" without violating the Fortran standard, because the Fortran standard doesn't say that it must not (or must do something else).

Now, how do we look at the Fortran standard document and decide whether the not-quite-Fortran program has a particular required effect? The text from 4.2 mentions a number of situations where a compiler must have "the capability to detect and report the use within a submitted program unit". If your proposed program doesn't hit any of those you're in "undefined" territory.

The main time a program error must be reportable is in the case

the use within a submitted program unit of a form or relationship that is not permitted by the numbered syntax rules or constraints

Let's consider arbitrarily Fortran 2018, 15.5.1C1523 (R1520) Syntax of a procedure reference. We see things like "R1520":

R1520 function-reference is procedure-designator ( [ actual-arg-spec-list ] )

and "C1523":

C1523 (R1520) The procedure-designator shall designate a function.

before we have a list of things like:

The data-ref in a procedure-designator shall not be an unallocated allocatable variable or a pointer that is not associated.

In this case, the rule R1520, numbered constraint C1523 (which applies to this rule) and following text give constraints on the Fortran program. If your submitted program doesn't meet those, it's not a conforming Fortran program.

A compiler asked to process such a non-conforming program, where that program violates R1520 or C1523, must be able to detect that (based on the above). A compiler doesn't have to complain about or detect violations of the un-numbered text. It's allowed to assume that any program it's presented with doesn't break such an un-numbered restriction.

This one here I quote is (coincidentally) one example of a prohibition on a program incorrectly using a previously deallocated variable.

If a processor operates in a "compile-then-run" way, the numbered rules/constraints are typically ones that can be assessed "at compile time".

Another specific and significant example of undefined behaviour is using a variable without first giving it a value ("defining" it). The Fortran standard simply says (Fortran 2018 9.2 p2):

A reference is permitted only if the variable is defined. A reference to a data pointer is permitted only if the pointer is associated with a target object that is defined. A variable becomes defined with a value when events described in 19.6.5 occur.

This isn't a numbered syntax rule or constraint and is a (significant) burden on the program that the compiler is allowed to assume has been met.

自由范儿2025-01-20 10:55:16

Fortran 和 C 之间的主要区别在于,后者由 Dennis Ritchie 设计,指定了许多在 FORTRAN 中不可能或错误的操作的行为,其方式可用于消除对 FORTRAN 中某些更多操作的需要。复杂的结构。

例如,在 Dennis Ritchie 的语言中,知道多维数组中元素总数的代码可以仅使用最后一个下标迭代整个数组。像这样的一段代码

double a[5][5],b[5][5];
void add_arrays()
{
  int i;
  for (i=0; i<25; i++)
    a[0][i] += b[0][i];
}

不会被视为碰巧有效的错误代码,而是被视为利用了常见的 C 习惯用法的代码。 [请注意,在 FORTRAN 或 Fortran 中执行上述操作的惯用方法是简单地使用矩阵加法]。

到 C 标准编写时,实现可以至少通过三种方式处理函数,例如:

double test(int i)
{
  a[1][0] = 1.0;
  a[0][i] = 2.0;
  return a[1][0];
}
  1. 以惯用的 C 方式,第二个赋值计算地址并写入它,从而允许以下可能性:写入可能会影响a[1][0]。

  2. 如上所述,但编译器假设 a[1][0] 不会受到第二次赋值的影响,因为它位于数组的不同行中。

  3. 进行明确的边界检查,如果 i 不在 0 到 4 的范围内,则会陷入困境。

上述每种解释都会使编译器更适合某些任务,而不太适合其他任务。该标准并未暗示上述任何解释均优于其他任何解释,而是将对 a[0][i] 的访问描述为对于 值而言“不可移植或错误” i在5到24的范围内;根据实现,在某些实现上,此类操作将是错误的,但在其他实现上,尽管它们是不可移植的,但它们将是正确的。

尽管可能会出现 Fortran 编译器以类似于 #2 的方式处理代码的情况(产生与顺序程序执行不一致的语义,但不会产生诊断),但我认为该语言不会有很多情况可以将此类操作视为某些实现是错误的,不可移植但其他实现是正确的。

A major difference between Fortran and C is that the latter language, as designed by Dennis Ritchie, specified the behavior of many actions which would have been impossible or erroneous in FORTRAN, in ways that could be used to eliminate the need for some of FORTRAN's more sophisticated constructs.

For example, in Dennis Ritchie's language, code which knew the total number of elements in a multi-dimensional array could iterate through the entire array using just the last subscript. A piece of code like:

double a[5][5],b[5][5];
void add_arrays()
{
  int i;
  for (i=0; i<25; i++)
    a[0][i] += b[0][i];
}

would not have been regarded as erroneous code that happened to work, but rather as code that exploited a common C idiom. [Note that the idiomatic way of doing the above in FORTRAN or Fortran would be to simply use a matrix addition].

By the time the C Standard was written, there would be at least three ways that implementations might process a function like:

double test(int i)
{
  a[1][0] = 1.0;
  a[0][i] = 2.0;
  return a[1][0];
}
  1. In the idiomatic C fashion, with the second assignment computing an address and writing to it, allowing for the possibility that the write might affect a[1][0].

  2. As above, but with the compiler assuming that a[1][0] will not be affected by the second assignment because it is in a different row of the array.

  3. With a definite bounds check, which would trap if i is not in the range 0 to 4.

Each of the above interpretations would make a compiler more suitable for some tasks and less suitable for others. Rather than suggest that any of the above interpretations was inferior to any other, the Standard instead characterizes an access to a[0][i] as "non-portable or erroneous" for values of i in the range 5 to 24; depending upon the implementation, on some implementations, such actions would be erroneous, but on others they would, despite being non-portable, be correct.

Although situations may arise where a Fortran compiler would process code in a manner analogous to #2 (yielding semantics inconsistent with sequential program execution but without yielding a diagnostic), I don't think the language has many situations where such actions could be viewed as erroneous by some implementations, and non-portable but correct by others.

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