下面的代码在Code :: Blocks上编译和运行时会显示不同的结果。
void sum(int a,int b){
printf("a=%d b=%d\n",a,b);
}
int main(){
int i=1;
sum(i=5,++i);
printf("i=%d\n\n",i);
/***********************/
i=2;
sum(i=5,i++);
printf("i=%d\n\n",i);
/**********************/
i=3;
sum(i=5,i);
printf("i=%d\n\n",i);
return 0;
}
输出:
a=5 b=5
i=5
a=5 b=2
i=5
a=5 b=5
i=5
我认为这个问题的答案与序列点有关,序列点与此处的++运算符有关。 GCC必须遵循命令将值以固定顺序传递给堆栈,但由于++,答案是不同的。我认为初学者写这样的函数调用并不常见,但关于运算符的教训是通用的,所以可以试试。
我的问题是,它应该是什么样的答案以及类似的问题?在编译的哪个阶段,决定这些事情(明确或不清楚)?涉及哪种特定算法(无论是优化还是一般)?同一编译器可以为这种表达式或语句提供不同的结果吗最后一个是,初学者将如何理解并找出这些问题?有时候非常令人惊讶。
答案 0 :(得分:1)
在编译的多个阶段中决定操作顺序,这是导致您看到的奇怪结果的原因。在优化阶段,特别是编译器可以以不总是显而易见的方式重新排序代码,并且在这种情况下它会影响结果(这很好,因为你做了一些未定义的事情并且编译器被明确允许做任何它想做的事情用那个代码)。没有涉及任何特定的算法,它是在不同点应用的几种不同算法之间的交互,并且在每个点应用的算法可以根据编译器决定处理特定位代码的最佳方式而变化。
当文档提到未定义的行为时,并不是特定编译器的行为是未定义的,而是指定编译器必须或允许执行的操作。编译器的行为是完全定义的,但它是由深入设计它的解析器,代码生成器和优化器模块的详细决策定义的,并且它足够复杂,即使是编写编译器的开发人员也可以告诉你如果没有花费它会做什么很多时候分析一段代码如何流经整个过程。
初学者无法弄清楚结果。即使是专业开发人员也可能无法做到。这就是为什么“未定义”对开发人员来说是一个不受欢迎的词,以及为什么他们试图避免像瘟疫那样的未定义行为。引用对相关语言规范的讨论,"In short, you can't use sizeof() on a structure whose elements haven't been defined, and if you do, demons may fly out of your nose."。