在循环中的什么时候整数溢出变成未定义的行为?

这是一个例子来说明我的问题,其中涉及一些更复杂的代码,我不能在这里发表。

#include <stdio.h>
int main()
{
    int a = 0;
    for (int i = 0; i < 3; i++)
    {
        printf("Hellon");
        a = a + 1000000000;
    }
}

该程序包含了我的平台上不确定的行为,因为a会3号循环溢出。

这是否会使整个程序具有未定义的行为,或者仅在溢出实际发生之后 ? 编译器是否有可能解决a 溢出的问题,因此它可以将整个循环声明为undefined,并且即使它们都发生在溢出之前,也不打算运行printfs?

(即使标记C和C ++是不同的,因为如果它们不同,我会对这两种语言的答案感兴趣。)


如果你对纯粹的理论答案感兴趣,C ++标准允许未定义的行为来“时间旅行”:

[intro.execution]/5:执行格式良好的程序的一致性实现应产生与具有相同程序和相同输入的抽象机器的相应实例的可能执行之一相同的可观察行为。 然而, 如果任何这样的执行包含未定义的操作,则本国际标准不要求执行该程序的实现具有该输入(甚至不涉及在第一个未定义操作之前的操作)

因此,如果你的程序包含未定义的行为,那么你的整个程序的行为是不确定的。


首先,让我纠正这个问题的标题:

未定义行为不是(特别是)执行领域。

未定义的行为会影响所有步骤:编译,链接,加载和执行。

一些例子来巩固这一点,请记住,没有一部分是详尽的:

  • 编译器可以假设包含未定义行为的代码部分永远不会执行,因此假定导致它们的执行路径是死代码。 请参阅每个C程序员应该了解的有关未定义行为的信息,而不仅限于Chris Lattner。
  • 链接器可以假设在存在弱符号的多个定义(由名称识别)的情况下,所有定义都相同,这要归功于一个定义规则
  • 加载器(如果你使用动态库)可以假设相同,因此挑选它找到的第一个符号; 这通常是(ab)用于在Unix上使用LD_PRELOAD技巧拦截调用
  • 如果您使用悬挂指针,则执行可能会失败(SIGSEV)
  • 这对于未定义的行为是如此可怕:几乎不可能事先预测到会发生什么确切的行为,并且这个预测必须在每次更新工具链,底层操作系统时重新进行...


    我建议您通过Michael Spencer(LLVM开发人员)观看此视频:CppCon 2016:My Little Optimizer:未定义的行为是魔术。


    针对16位int的积极优化C或C ++编译器会知道向int类型添加1000000000的行为未定义。

    任何标准都允许做任何事情,包括删除整个程序,只留下int main(){}

    但是更大的int呢? 我不知道有这样做的编译器(并且我不是以任何方式在C和C ++编译器设计方面的专家),但我想有时候编译器的目标是32位int或更高的编译器会发现循环是无限的( i不改变)等a最终会溢出。 所以再一次,它可以优化输出到int main(){} 。 我试图在这里指出的一点是,随着编译器优化变得越来越积极,越来越多未定义的行为构造以意想不到的方式体现出来。

    由于您正在向循环体中的标准输出写入数据,因此循环无限的事实本身并未定义。

    链接地址: http://www.djcxy.com/p/14025.html

    上一篇: At what point in the loop does integer overflow become undefined behavior?

    下一篇: This C function should always return false, but it doesn’t