神秘的C语言问题,考考你们这些货们。

均陵鼠侠 2014-03-29 11:04:42
# include<stdio.h>
# include<stdlib.h>

int x = 0;

int inc (void)
{
return x++;
}

int main (void)
{
x++ + inc();
printf("%d\n", x);
}

打印多少?
...全文
2156 119 打赏 收藏 转发到动态 举报
写回复
用AI写文章
119 条回复
切换为时间正序
请发表友善的回复…
发表回复
均陵鼠侠 2014-04-04
  • 打赏
  • 举报
回复
引用 89 楼 nadleeh123 的回复:
[quote=引用 85 楼 gemo 的回复:] 对LZ这种人就应该狠狠地打击一下。 vs2008: gcc4.6:
貌似lz歇菜了[/quote] 这几天到北京出差修摄像机去了,昨天才回来。
均陵鼠侠 2014-04-04
  • 打赏
  • 举报
回复
引用 114 楼 lovesmiles 的回复:
虽然我不是高手,但这样理解这句话: x++ + inc(); 1 先x++,再inc().那么结果是2 2先inc(),再x++,结果还是2. 难道不是2吗?这个结果根本就不用理会它的执行顺序吧。prinft语句和他们又不是在同一个序列点
你这样说,是我发现所有发言中比较靠谱的。函数比较特殊。a+b,a和b的求值可能交错。但a+f()就不同了。你能想像a的求值和f的执行是交错的吗?f()+g(),这两个函数可能会交错执行吗?标准中是有明确说法的。
均陵鼠侠 2014-04-04
  • 打赏
  • 举报
回复
引用 85 楼 gemo 的回复:
对LZ这种人就应该狠狠地打击一下。 vs2008: gcc4.6:
这也叫打击? 我用的最新版的gcc,也是1呢。但是,扣标准的话,它就应该是2。 编译器说明不了什么问题,还谈何打击。
均陵鼠侠 2014-04-04
  • 打赏
  • 举报
回复
首先,认为这种题没有意义的可以不用费神了。 前面有人认为问题的焦点在于以下这个条款: The order of evaluation of the function designator, the actual arguments, and subexpressions within the actual arguments is unspecified,but there is a sequence point before the actual call. 这句的意思是,对于表达式f(e),f和e的求值顺序是未指定的,但它们前序于函数体的执行。 实际上,问题的焦点不在这里。考虑一个一般意义上的表达式E1+E2,假如E1和E2都不是函数调用表达式,根据本版那个没什么大用的置顶帖,E1和E2的求值是unsequenced(无序的)。也就是说,可能先求值E1再求值E2,也可能相反,也可能交叉求值,求值包括副作用的值的计算。 不过,标准显然考虑到了函数执行的特殊性。考虑一下表达式f(a)+g(b),f、g、a和b的求值是unsequenced(无序的),但是,这两个函数的函数体执行不可能是无序的,因为,不可能对f和g的函数体反复交错执行。对吧?所以,可能是先执行f的函数体,再执行g的函数体,或者相反。但无论如何,唯一可以确定的是,a的求值前序于f函数体的执行,b的求值前序于g函数体的执行。 再来看表达式x++ +inc()。表面上看,x++和inc的求值是无序的。但是,我已经说过了,最重要的那一句是 N1570,6.5.2.2: Every evaluation in the calling function (including other function calls) that is not otherwise specifically sequenced before or after the execution of the body of the called function is indeterminately sequenced with respect to the execution of the called function. 很明显,x++可以在inc()函数体执行之前,或者之后求值。inc的求值必然发生在inc函数体执行之前(前序于函数体执行),但不可能与x++交错执行,因为N1570,6.5.2.4: The value computation of the result is sequenced before the side effect of updating the stored value of the operand. With respect to an indeterminately-sequenced function call, the operation of postfix++is a single evaluation.
IT_Linux 2014-04-04
  • 打赏
  • 举报
回复
2
FrankHB1989 2014-04-02
  • 打赏
  • 举报
回复
引用 96 楼 supermegaboy 的回复:
不过,对于楼主的结论,偶现在仍有保留意见。由于工作忙,偶现在还没时间详细看C11关于序列点的叙述,但把C11与C++11两者的相关内容粗略比较了一下,发现两者是基本一致的,问题是,g++4.8.1(已完全支持C++11)出来的结果是1,鉴于gcc一贯良好的编译器质量,所以这个问题现在还不能说就是2了。
C11跟C++11还是有些差别的。 比如int a = 42; a = ++a;在C++11里就不算UB但是C11还是。 然后是概念上的。C++11直接移除了sequence point的说法,用sequenced before代替。C11保留两者。 所以具体问题上这里没可比性。
FrankHB1989 2014-04-02
  • 打赏
  • 举报
回复
引用 94 楼 nadleeh123 的回复:
[quote=引用 93 楼 FrankHB1989 的回复:] [quote=引用 92 楼 nadleeh123 的回复:] [quote=引用 91 楼 FrankHB1989 的回复:] [quote=引用 89 楼 nadleeh123 的回复:] [quote=引用 85 楼 gemo 的回复:] 对LZ这种人就应该狠狠地打击一下。 vs2008: gcc4.6:
貌似lz歇菜了[/quote] 艹gcc不开-std=c11 -pedantic么。 [/quote] 我会告诉你一大堆arm mips mipsel ppc powerpc等等一大堆16bit 32bit 64bit的编译器依然是默认89标准[/quote] LZ都明确说用什么了,你现在默认的有什么说服力。 而且gcc默认是gnu89,要是测VLA或者//什么的支持是不是还要怀疑撞鬼了? (顺便,clang是gnu99。) [/quote] 有明确说用啥么,我咋只看见了一堆废代码?[/quote] LZ早就说了用N1570。 难道还要正版ISO/IEC 9899:2011?反正我敢确定这两个版本在这里的问题上没什么区别。
一根烂笔头 2014-04-02
  • 打赏
  • 举报
回复
擦,+是左结合性,看编译器语法分析与优化!有毛线的研究意义?如果实际项目中有人给我写出这样代码来,我非要他给我重写去!可读性差,bug潜在强
FancyMouse 2014-04-02
  • 打赏
  • 举报
回复
引用 104 楼 supermegaboy 的回复:
但实际上g++4.8.1的结果是1,这也是一个间接的证据。
咦。我找了个在线的4.8.1,结果也是2啊。
赵4老师 2014-04-02
  • 打赏
  • 举报
回复
凡是人造的东西都有缺陷。 标准也是人造的。
赵4老师 2014-04-02
  • 打赏
  • 举报
回复
凡是人造的东西都有缺陷。 编译器也是人造的。
FancyMouse 2014-04-02
  • 打赏
  • 举报
回复
引用 104 楼 supermegaboy 的回复:
问题的焦点在于以下这个条款: The order of evaluation of the function designator, the actual arguments, and subexpressions within the actual arguments is unspecified, but there is a sequence point before the actual call. 这里出现了两种理解(或许更多,但现在只发现两种): 一是这里所说的序列点也是函数调用之前其它子表达式的序列点; 二是这里所说的序列点的作用范围只包括前半句所说的函数指示符、实参及其子表达式; 看完gcc的bug报告,我坚持第二种理解。因为: 1、标准条款前半句和后半句之间是一个逗号(我用红色标出来),这表示后半句承接的是前半句的意思,因而,那个序列点表达的意思是:在此序列点达到的时候,函数指示符、实参及子表达式的副作用被完全计算(不包括之前的子表达式); 2、如果标准想表达这个序列点也作用于函数调用之前的子表达式的话,应该新起一个子条款,而不是跟在陈述函数指示符、实参及其子表达式这样一句范围受到限定的语句中。 所以,这个场景符合未定义的情景。但是,出于尊重你的观点的立场,我也接受以下说法:标准关于此处的描述比较模糊,没有表达精确的意思。 此外,BUG报告中版本范围限定在4.5、4.6、4.7,但实际上g++4.8.1的结果是1,这也是一个间接的证据。
这句话只说明了存在有一个sequence point,没有任何sequence point作用范围的描述。你那么大一段全是臆测。 C99的原文: At certain specified points in the execution sequence called sequence points, all side effects of previous evaluations shall be complete and no side effects of subsequent evaluations shall have taken place. seq pt就是这么定义的。seq pt的存在性我们没有疑问,那么除非你能告诉我插在函数前后的那俩seq pt和这里定义的seq pt不是同一种seq pt,否则我不觉得这seq pt还有什么作用域的限制。 顺便,我手头4.8.2输出2。
飞天御剑流 2014-04-02
  • 打赏
  • 举报
回复
引用 102 楼 FancyMouse 的回复:
[quote=引用 101 楼 supermegaboy 的回复:] [quote=引用 44 楼 FancyMouse 的回复:] [quote=引用 30 楼 supermegaboy 的回复:] 你没想通的事情是,由于C/C++标准规定在两序列点之间发生的多于一次的副作用属于未定义行为,所以x++和inc中的x++的后缀增量在x++ + inc();语句的分号前何时发生是不确定的;x++的++有可能在+inc( )之前发生,当然也有可能在x+inc( )之后再发生两次++,这些行为取决于编译器设计者,因而造成可能的结果为0---4之间。
即使用旧标准你这句话也是不对的。函数调用前后都有sequence point,如果外面的x++在inc之前发生,那么在进入inc之前被进入函数的sequence point已经保证了副作用已经发生。如果外面的x++在inc之后发生,那么离开inc的seq point保证了外面的x++起始x值已经是被inc给++过的。 另外别用某些版本的gcc试。几个旧版本在这点上有bug的。[/quote] 请重新学习一下什么是undefined behaviour,如果标准规定这个行为是implmentation defined或者unspecified,那么你的想法就是有道理的。但作为未定义行为,作为错误的代码结构,其结果是不能以常理去推测的,你不能依然企盼编译器设计者还会为你做帕拉图式的收尾。一个常见的例子是超过提升后类型二进制宽度的移位,同一个编译器会出现不同的结果。[/quote] 我当然知道undefined behavior怎么回事。问题是那个代码符合标准,根本就不是什么undefined。 楼上几个测gcc的,bug在这里:http://gcc.gnu.org/bugzilla/show_bug.cgi?id=48814 注意直到4.7这bug还是有。 注意那个bug里的例子,你们如果把它和a[i]=++i认为是一回事的话那我只能摊手。[/quote] 问题的焦点在于以下这个条款: The order of evaluation of the function designator, the actual arguments, and subexpressions within the actual arguments is unspecified, but there is a sequence point before the actual call. 这里出现了两种理解(或许更多,但现在只发现两种): 一是这里所说的序列点也是函数调用之前其它子表达式的序列点; 二是这里所说的序列点的作用范围只包括前半句所说的函数指示符、实参及其子表达式; 看完gcc的bug报告,我坚持第二种理解。因为: 1、标准条款前半句和后半句之间是一个逗号(我用红色标出来),这表示后半句承接的是前半句的意思,因而,那个序列点表达的意思是:在此序列点达到的时候,函数指示符、实参及子表达式的副作用被完全计算(不包括之前的子表达式); 2、如果标准想表达这个序列点也作用于函数调用之前的子表达式的话,应该新起一个子条款,而不是跟在陈述函数指示符、实参及其子表达式这样一句范围受到限定的语句中。 所以,这个场景符合未定义的情景。但是,出于尊重你的观点的立场,我也接受以下说法:标准关于此处的描述比较模糊,没有表达精确的意思。 此外,BUG报告中版本范围限定在4.5、4.6、4.7,但实际上g++4.8.1的结果是1,这也是一个间接的证据。
FrankHB1989 2014-04-02
  • 打赏
  • 举报
回复
引用 114 楼 lovesmiles 的回复:
虽然我不是高手,但这样理解这句话: x++ + inc(); 1 先x++,再inc().那么结果是2 2先inc(),再x++,结果还是2. 难道不是2吗?这个结果根本就不用理会它的执行顺序吧。prinft语句和他们又不是在同一个序列点
前提是先考虑清楚没有未定义行为。 涉及后置++坑爹你应该懂的。
没事人 2014-04-02
  • 打赏
  • 举报
回复
用编译软件试一下不就知道了吗
FancyMouse 2014-04-02
  • 打赏
  • 举报
回复
引用 101 楼 supermegaboy 的回复:
[quote=引用 44 楼 FancyMouse 的回复:] [quote=引用 30 楼 supermegaboy 的回复:] 你没想通的事情是,由于C/C++标准规定在两序列点之间发生的多于一次的副作用属于未定义行为,所以x++和inc中的x++的后缀增量在x++ + inc();语句的分号前何时发生是不确定的;x++的++有可能在+inc( )之前发生,当然也有可能在x+inc( )之后再发生两次++,这些行为取决于编译器设计者,因而造成可能的结果为0---4之间。
即使用旧标准你这句话也是不对的。函数调用前后都有sequence point,如果外面的x++在inc之前发生,那么在进入inc之前被进入函数的sequence point已经保证了副作用已经发生。如果外面的x++在inc之后发生,那么离开inc的seq point保证了外面的x++起始x值已经是被inc给++过的。 另外别用某些版本的gcc试。几个旧版本在这点上有bug的。[/quote] 请重新学习一下什么是undefined behaviour,如果标准规定这个行为是implmentation defined或者unspecified,那么你的想法就是有道理的。但作为未定义行为,作为错误的代码结构,其结果是不能以常理去推测的,你不能依然企盼编译器设计者还会为你做帕拉图式的收尾。一个常见的例子是超过提升后类型二进制宽度的移位,同一个编译器会出现不同的结果。[/quote] 我当然知道undefined behavior怎么回事。问题是那个代码符合标准,根本就不是什么undefined。 楼上几个测gcc的,bug在这里:http://gcc.gnu.org/bugzilla/show_bug.cgi?id=48814 注意直到4.7这bug还是有。 注意那个bug里的例子,你们如果把它和a[i]=++i认为是一回事的话那我只能摊手。
飞天御剑流 2014-04-02
  • 打赏
  • 举报
回复
引用 44 楼 FancyMouse 的回复:
[quote=引用 30 楼 supermegaboy 的回复:] 你没想通的事情是,由于C/C++标准规定在两序列点之间发生的多于一次的副作用属于未定义行为,所以x++和inc中的x++的后缀增量在x++ + inc();语句的分号前何时发生是不确定的;x++的++有可能在+inc( )之前发生,当然也有可能在x+inc( )之后再发生两次++,这些行为取决于编译器设计者,因而造成可能的结果为0---4之间。
即使用旧标准你这句话也是不对的。函数调用前后都有sequence point,如果外面的x++在inc之前发生,那么在进入inc之前被进入函数的sequence point已经保证了副作用已经发生。如果外面的x++在inc之后发生,那么离开inc的seq point保证了外面的x++起始x值已经是被inc给++过的。 另外别用某些版本的gcc试。几个旧版本在这点上有bug的。[/quote] 请重新学习一下什么是undefined behaviour,如果标准规定这个行为是implmentation defined或者unspecified,那么你的想法就是有道理的。但作为未定义行为,作为错误的代码结构,其结果是不能以常理去推测的,你不能依然企盼编译器设计者还会为你做帕拉图式的收尾。一个常见的例子是超过提升后类型二进制宽度的移位,同一个编译器会出现不同的结果。
勤奋的小游侠 2014-04-02
  • 打赏
  • 举报
回复
虽然我不是高手,但这样理解这句话: x++ + inc(); 1 先x++,再inc().那么结果是2 2先inc(),再x++,结果还是2. 难道不是2吗?这个结果根本就不用理会它的执行顺序吧。prinft语句和他们又不是在同一个序列点
飞天御剑流 2014-04-02
  • 打赏
  • 举报
回复
引用 108 楼 FancyMouse 的回复:
[quote=引用 104 楼 supermegaboy 的回复:] 但实际上g++4.8.1的结果是1,这也是一个间接的证据。
咦。我找了个在线的4.8.1,结果也是2啊。[/quote] 中午在公司下载了个4.8.1,这回见鬼了,结果是2,但在家里明明是1的,可能在家编译时搞错了什么东西。不过无论如何,重新仔细思考了一下,我同意这个不是未定义行为,对104楼的条款理解有误。
FrankHB1989 2014-04-02
  • 打赏
  • 举报
回复
引用 99 楼 jiandingzhe 的回复:
去看置顶贴!!!!
置顶早就不够用了。
引用 100 楼 lzhui1987 的回复:
[quote=引用 49 楼 sholber 的回复:] [quote=引用 21 楼 lin5161678 的回复:] 这种事 可以让你去看置顶 到考虑到鼠侠你一直是专研标准 那就这样回答好了 i++ + f(i) 这里 的确存在序列点 问题是 这个序列点值确认了 f(i)的确会让 i+1 但没确认 i++ 的时候 已经调用了 f(i) 对于i++ 可以在 f(i) 之前求值 这时候 i++的值是0 也可以是在f(i)之后求值 这时候 i++的值是1 so 依然是一个未定义行为
你们知道序列点就稍微好办一点。 你先弄懂 求值(evaluation) 前序(sequenced before) 后序(sequenced after) 无序(unsequenced) 不确定顺序(indetermined sequenced) 这些概念,然后再看看C11草案N1570,6.5.2.2: Every evaluation in the calling function (including other function calls) that is not otherwise specifically sequenced before or after the execution of the body of the called function is indeterminately sequenced with respect to the execution of the called function.[/quote] 心算是1,但是用GCC编译执行打印1,用VS2010编译执行打印2,能打印出2实在不解,x++的后自增操作符优先级较低,不是在自增之前函数已经返回了吗???[/quote] 优先级这种纯语法上的东西跟这里说的有什么关系? 副作用发生在文件作用域对象上,返回值又没用到,x在inc()求值以后存储了什么值跟inc的返回有什么关系? 顺便,我手头的mingw-w64-gcc-4.8.2和clang-3.5的结果都是2。
加载更多回复(99)

69,382

社区成员

发帖
与我相关
我的任务
社区描述
C语言相关问题讨论
社区管理员
  • C语言
  • 花神庙码农
  • 架构师李肯
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧