gpt4 book ai didi

C "observable behavior"在 UB "undefined behavior"的上下文中

转载 作者:太空狗 更新时间:2023-10-29 16:11:04 30 4
gpt4 key购买 nike

(问题最初是由对 Are there race conditions in this producer-consumer implementation? 的回答下的评论提示的,但这里严格从 C 语言的角度提出问题,不涉及任何并发或多线程。)

考虑这个最小的代码:

#define BUFSIZ 10
char buf[BUFSIZ];

void f(int *pn)
{
buf[*pn]++;
*pn = (*pn + 1) % BUFSIZ;
}

int main()
{
int n = 0;
f(&n);
return n;
}

问题:C “as-if” 规则是否允许编译器重写代码如下?

void f(int *pn)
{
int n = *pn;
*pn = (*pn + 1) % BUFSIZ;
buf[n]++;
}

一方面,上述内容不会改变编写的程序的可观察行为。

另一方面,f 可能会使用无效索引调用,可能来自另一个翻译单元:

int g()
{
int n = -1001;
f(&n);
}

在后一种情况下,代码的两种变体都会在访问越界数组元素时调用 UB。但是,原始代码会将 *pn 保留为传递给 f (= -1001) 的值,而重写的代码仅在修改 后才会进入 UB-land >*pn(到 0)。

这种差异是否算作“可观察”,或者回到实际问题,C 标准中是否有任何内容明确允许或排除此类代码重写/优化?

最佳答案

  1. 如果程序的任何部分有未定义的行为,那么整个程序的行为就是未定义的。换句话说,即使在任何行为未定义的构造“之前”,程序的行为也是未定义的。 (这是允许编译器根据定义的行为执行某些优化所必需的。)

  2. 鉴于这两个变量都没有声明为 volatile,我相信内存更新的顺序可能会按照指示重新排序,因为只有在没有未定义的情况下才能保证可观察到的行为符合执行模型行为。

  3. “可观察行为”(在标准 C 中)在 §5.1.2.3 中定义为:

    • Accesses to volatile objects are evaluated strictly according to the rules of the abstract machine.
    • At program termination, all data written into files shall be identical to the result that execution of the program according to the abstract semantics would have produced.
    • The input and output dynamics of interactive devices shall take place as specified in 7.21.3. The intent of these requirements is that unbuffered or line-buffered output appear as soon as possible, to ensure that prompting messages actually appear prior to a program waiting for input.

    此列表不包括对未定义行为(例如陷阱或信号)的任何潜在响应,即使在白话术语中通常可以观察到段错误。问题中的具体例子不涉及这三点。 (UB 可以阻止程序成功终止,这基本上使可观察行为的第二点无效。)因此,在问题代码的特定情况下,重新排序不会改变任何可观察行为并且可以清楚地执行。

  4. 我声明实现对未定义行为的响应不限于严格遵循会产生未定义行为的组件执行,这在评论线程中引起了比我预期更多的争议,因为这是一个相当知名的功能现代 C。可能值得回顾 John Regehr 的有用 essayundefined behaviour ,我从中引用:(在第三部分)

    More specifically, when a program dies due to performing an illegal operation such as dividing by zero or dereferencing a null pointer, is this considered to be side effecting? The answer is definitely “no.” … Since crash-inducing operations are not side effecting, the compiler can reorder them with respect to other operations,

    作为一个可能更有趣的例子(取自评论线程),如果程序产生多行输出,然后故意执行显式除零,人们可能期望编译和运行程序会产生在以任何未定义的方式响应被零除响应之前输出。然而,检测到被零除并可以证明程序的控制流保证其执行的编译器将完全有权在翻译时生成错误消息并拒绝生成可执行文件图片。

    或者,如果它不能证明控制流达到被零除,它有权假设被零除不会发生,因此删除所有明确导致被除的代码-通过零(包括对输出函数的调用)作为死代码。

    以上两者都符合 §3.4.3 中对未定义行为的示例响应列表:“从完全忽略具有不可预测结果的情况,……到终止翻译或执行(发布诊断消息)。 "

关于C "observable behavior"在 UB "undefined behavior"的上下文中,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34823457/

30 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com