在C中是否有任何保证代码在未定义的行为之前?

Goo*_*son 11 c standards

在下面的代码中保证打印"0 \n"?

#include <stdio.h>
int main(void)
{
    int c = 0;
    printf("%d\n",c);

    printf("%d,%d\n",++c,++c);
}
Run Code Online (Sandbox Code Playgroud)

更一般地说,如果一个程序有未定义的行为,整个程序是不确定的,还是仅从开始出现问题代码的序列点开始?

请注意:我不是在询问编译器对第二个printf的作用.我问的是第一个printf是否有保证会发生.

我知道未定义的行为能够炸毁你的计算机,崩溃你的程序或诸如此类的东西.

Log*_*ldo 8

甚至忽略了"任何事情都可能发生!程序可能会及时返回并阻止自己在第一时间运行!"这样的事情,编译器很可能检测到某些形式的未定义行为而不能在这种情况下编译如果你不能让它在第一时间运行.所以是的,未定义的行为在原则上具有传染性,如果在实践中大部分时间都不一定如此.


Mic*_*urr 5

无论程序在导致未定义的行为之前做了什么,当然已经完成了.

因此,它printf()会将"0 \n"发送到stdout流.该数据是否真正进入设备取决于该流是否是无缓冲,缓冲或行缓冲的.

然后,我认为在完成的,明确定义的操作之后执行的未定义行为可能会导致损坏,直到看起来明确定义的行为未正确完成.我觉得有点像"如果一棵树落在树林里......那些东西".


更新以解决未来未定义行为意味着即使在程序开始执行之前所有赌注都已关闭的信念......

以下是C99标准关于在序列点之间多次修改对象值的说法:

在前一个和下一个序列点之间,对象的存储值最多只能通过表达式的计算来修改一次.

标准也有关于访问对象的说法:

访问

 <execution-time action> to read or modify the value of an object
 NOTE 1   Where only one of these two actions is meant, ``read'' or ``modify'' is used.
 NOTE 2   "Modify'' includes the case where the new value being stored is the same as the previous value.
 NOTE 3   Expressions that are not evaluated do not access objects.
Run Code Online (Sandbox Code Playgroud)

我不认为在转换时在序列点之间多次修改对象是"未定义的行为",因为在转换时不会访问/修改对象.

即便如此,我同意在编译时诊断这种未定义行为的编译器是一件好事,但我也认为如果仅适用于已成功编译的程序,这个问题会更有趣.因此,让我们稍微改变一下这个问题,以便编译器无法在转换时诊断出未定义的行为:

#include <stdio.h>
#include <stdlib.h>

int main(int argc, char* argv[])
{
    int c[] = { 0, 1, 2, 3 };
    int *p1 = &c[0];
    int *p2 = &c[1];

    if (argc > 1) {
        p1 = &c[atoi(argv[1])];
    }
    if (argc > 2) {
        p2 = &c[atoi(argv[2])];
    }

    printf("before: %d, %d\n", *p1, *p2);

    printf("after:  %d, %d\n", ++(*p1),++(*p2)); /* possible undefined behavior */

    return 0;
}
Run Code Online (Sandbox Code Playgroud)

在这个程序中,甚至在转换时都不知道存在未定义的行为 - 只有在程序的输入指示应该处理相同的数组元素时才会发生(或者如果输入指定了可能发生不同类型的未定义行为)无效的索引值).

因此,让我们用这个程序提出同样的问题:标准对于第一个printf()结果或副作用可能发生的事情有什么看法?

如果输入提供有效的索引值,则未定义的行为只能第一个之后发生printf().假设输入是argv[1] == "1"argv[2] == "1":编译器实现没有在第一个之前确定的自由printf(),因为未定义的行为将在程序中的某个点发生,它允许跳过第一个printf()并且右转到其未定义的格式化硬盘的行为(或者其他任何可能发生的恐怖事件).

鉴于编译器同意转换程序,未来未定义行为的承诺不会让编译器在实际发生未定义行为之前自由地执行任何操作.当然,正如我之前提到的,未定义行为所造成的损害可能会破坏以前的结果 - 但这些结果必定已经发生.