如果在 Linux 下使用 GCC 编译器执行下列程序,输出结果是什么?
#include<stdio.h> int main(){ int a=5; printf("%d %d %d",a++,a++,++a); return 0; }
原问题出处: http://www.revotu.com/advanced-c-interview-questions-and-answers.html
求详细讨论!!!
1
xss 2017-06-27 19:26:34 +08:00
你需要知道的:
1. c 语言的函数调用规则是 cdecl 2. cdecl 调用规则参数由右向左入栈 因此, 计算顺序由右向左, 即 ++a a++ a++ 然后你还需要知道的, gcc 的行为: 所有具有前置自增 /减运算符位置的参数, 需要等所有前&后置运算完成之后, 才能决定. 所有具有后置自增 /减运算符位置的参数, 一旦前面的运算符完成, 值即可决定. 所以 ++a => a=6 -> 决定了第二个参数为 6, 因为第二个参数之前, 再无前置运算, 且第二个参数为后置运算 a++ => a=7 -> 决定了第一个参数为 7, 因为第一个参数之前再无前置运算, 且第一个参数为后置运算 a++ => a=8 -> 所有运算完成, 所有具有前置运算符位置的参数, 全部等于这个值 因此, 输出为: 7, 6, 8 再看个复杂的: printf("%d %d %d %d %d", a++, a++, a++, --a, ++a); 从右向左: ++a -> 6, 前面还有前置运算, 该位置参数的值未确定 --a -> 5, 还有前置, 该位置未确定值, 但是前一个参数是后置运算, 因此前面一个参数的值为 5 a++ -> 6, 这个位置的参数由上一步确定, 为 5, 同时决定下面的一个 a++的值为 6 a++ -> 7, 上一个 a++的值为 7 a++ -> 8, 所有自增操作运算完毕, 参数位置为前置自增的值可以定了, 为 8 因此, 输出为: 7 6 5 8 8 |
2
snnn 2017-06-27 20:02:21 +08:00
神经病。哪凉快哪躺着去
|
4
billlee 2017-06-27 21:28:58 +08:00
在未定义行为上折腾是没有意义的
|
5
gnaggnoyil 2017-06-28 06:33:29 +08:00
按照 C99/11 标准,单个函数调用时各个参数的计算相互之间没有 sequenced before 关系,所以在这里有副作用的几条表达式直接炸成 UB.如果有谁想问 UB 的代码有什么外部可观测结果那么就直接打死,不要废话.
|
7
gnaggnoyil 2017-06-28 13:32:06 +08:00
@xss 首先 libc 库多了去了谁一定要和你说的那样玩
其次,你是面向 C 和 POSIX 标准编程还是面向 glibc/Linux 的 ad hoc 实现编程?熟读语言标准的人都开始写 C 编译成浏览器 JavaScript 的编译器了,谭浩强式出身的人还在那里把 C 代码当作一个自己使用的特定平台的另一种语言的 trivial 对应. |
8
xss 2017-06-28 14:13:57 +08:00
@gnaggnoyil 你要是说平台是 arm, mips 这种架构下的 libc. 好吧, 那我无话可说, 的确有可能并不遵守默认的函数调用标准. 但是就楼主这个情形来说, 明显是 x86 下的 glibc. 或者我这么说更确切一点(?): 目前所有 x86 架构下的 lts linux 发行版本, glibc 的调用标准都是 cdecl.
哪位能指教一下不是上述情况的 glibc ? |
9
liuyao729 2017-08-10 11:46:58 +08:00
5 6 8
|
10
zmj1316 2017-09-03 08:01:19 +08:00 via Android
printf 这种不定长参数一般应该从左向右入栈吧,所以我猜 5 6 8
|