小德GD 发表于 2013-4-25 00:59:38

求教两个简单的C语言问题

int main(void)
{
      int i=1;
      printf("%d,%d,%d",i,++i,i);
      return 0;
}
为什么结果是2,2,1?

int main(void)
{
      int i=1,j=2;
      printf("%d,%d,%d,%d",i,i++,i+=j,++j);
      return 0;
}
为什么结果是4,4,4,3?

damy2008 发表于 2013-4-25 02:30:32

百度到一些资料。不是是否合用
一是:printf函数中的计算是从右向左进行的。
二是:我们在写代码时,应该尽量避免类似下面的无确定意义的表达式出现,因为很有可能不同的编译器,会采用不同的理解方式。

qwe2231695 发表于 2013-4-25 02:58:52

从右到左执行。很简单的。

绿茶山人 发表于 2013-4-25 10:07:12

很多人看代码都讨厌这种i+++++,逻辑不清晰,不要用分析这类问题来显酷。搜一下C语言序列点,副作用。

caicai2317 发表于 2013-4-25 10:25:24

是右到左没错,但是想不明白为什么第二个函数的第一个i是4而不是5

benqme 发表于 2013-4-25 12:19:56

是右到左,第二个理解应该编译优先权不一样

zhugean 发表于 2013-4-25 12:27:22

从右到左的话第二个不应该是5443吗?

yoz 发表于 2013-4-25 13:12:12

undefined behaviour

monkerman 发表于 2013-4-25 14:08:59

楼上正解.
入栈顺序是从右往左, 但是自增和加法操作不保证方向......和编译器有关.
可以看反汇编结果.
这种写法是考试坑爹的.{:sweat:}

souching 发表于 2013-4-25 14:32:20

转一个:
C语言中,只包含一个表达式的语句,如x=(i++)*2;称为“表达式语句”。表达式语句结尾的";"是C标准定义的顺序点之一,但这不等同于说所有的";"都是顺序点,也不是说顺序点只有这一种。下面就是标准中定义的顺序点:
函数调用时,实参表内全部参数求值结束,函数的第一条指令执行之前(注意参数分隔符“,”不是顺序点);
&&操作符的左操作数结尾处;||操作符的左操作数结尾处;?:操作符的第一个操作数的结尾处;逗号运算符;
表达式求值的结束点,具体包括下列几类:
自动对象的初值计算结束处;表达式语句末尾的分号处;do/while/if/switch/for语句的控制条件的右括号处;for语句控制条件中的两个分号处;return语句返回值计算结束(末尾的分号)处。

定义顺序点是为了尽量消除编译器解释表达式时的歧义,如果顺序点还是不能解决某些歧义,那么标准允许编译器的实现自由选择解释方式。理解顺序点还是要从定义它的目的来下手。
再举一个例子:
y=(x++,x+1);
已知这个语句执行前x=2,问y的值是多少?
逗号运算符是顺序点。那么该表达式的值就是确定的,是4,因为按照顺序点的定义,在对x+1求值前,顺序点","前的表达式——x++求值的全部副作用都已发生完毕,计算x+1时x=3。这个例子中顺序点成功地消除了歧义。
注意这个歧义是怎样消除的。因为中间的顺序点使“相邻顺序点间对象的值只更改一次”的条件得到满足。
y=(x++)*(x++),执行前x=2,y=?
答案是,因为这个表达式本身不包含顺序点,顺序点未能消除歧义,编译器生成的代码使y取4,6(以及更多的一些可能值)都是符合标准定义的,程序员自己应为这个不符合顺序点定义的表达式造成的后果负责。

zclcom79 发表于 2013-4-25 15:53:39

要避免这种用发 ,因为和编译器还有优化等级都有关系, 而且出了问题还很难查

wangshumou 发表于 2013-4-25 19:47:25

第二个运行结果是5,4,4,3哦

小德GD 发表于 2013-4-26 00:44:30

wangshumou 发表于 2013-4-25 19:47 static/image/common/back.gif
第二个运行结果是5,4,4,3哦

如果按照printf从右到左的顺序的话,我想也是5,4,4,3,但我用VC6.0是4,4,4,3

小德GD 发表于 2013-4-26 00:46:43

zhugean 发表于 2013-4-25 12:27 static/image/common/back.gif
从右到左的话第二个不应该是5443吗?

但用VC6.0测确实是4,4,4,3

yklstudent 发表于 2013-4-26 07:10:50

吃饱了没事情干吗???

monkerman 发表于 2013-4-26 08:59:17

小德GD 发表于 2013-4-26 00:46 static/image/common/back.gif
但用VC6.0测确实是4,4,4,3

printf("%d,%d,%d,%d",i,i++,i+=j,++j);
唉......VC 的结果既然是这样, 我就给解释下吧.
首先从右往左:
++j 后 j 变成了 3, 注意前加操作是先自增后赋值.
i += j 后 i 变成了 1 + 3 = 4;
i++ 时, 由于后加操作是先赋值后自增, 所以, i 还是 4.
i, 当然也是 4, 因为 , 号不是序列点.

如果你用 GCC 的话, 结果会不一样, 他的前加后加操作有自己的理解和规定(不同版本可能也不一样).
            例如++j, i++都是在 i += j; 之前完成, 而且产生副作用. 这时 i+= j; 后 i 的值是 5. 第一个 i 也是 5.

如果你用 Borland-C++结果可能又不一样. 别纠结了.

takashiki 发表于 2013-4-26 09:34:00

小德GD 发表于 2013-4-26 00:46 static/image/common/back.gif
但用VC6.0测确实是4,4,4,3

又是一个深受谭浩强毒害的……你已经发现这个是坑了,为什么还要往里跳?C语言的陷阱防不胜防的。
主要原因为未定义行为

第一个为什么结果是2,2,1?因为依赖编译器实现,结果还可以是2,2,2。把++放到i后面,你的结论还会更不同。第二个同理。不信请用VC Debug/Release模式分别都试试。
因此,我们要尽量避免编译器的未定义行为,进到坑了尽量绕道,别一脚踏进去了。

wangshumou 发表于 2013-4-26 12:34:34

小德GD 发表于 2013-4-26 00:44 static/image/common/back.gif
如果按照printf从右到左的顺序的话,我想也是5,4,4,3,但我用VC6.0是4,4,4,3

我用C_FREE编的,看来不同的编译器也有不同结果

小德GD 发表于 2013-4-26 12:56:33

monkerman 发表于 2013-4-26 08:59 static/image/common/back.gif
唉......VC 的结果既然是这样, 我就给解释下吧.
首先从右往左:
++j 后 j 变成了 3, 注意前加操作是先自 ...

非常谢谢你用心的回答。想再麻烦问下“i, 当然也是 4, 因为 , 号不是序列点.”这句话怎么理解?

小德GD 发表于 2013-4-26 13:12:40

takashiki 发表于 2013-4-26 09:34 static/image/common/back.gif
又是一个深受谭浩强毒害的……你已经发现这个是坑了,为什么还要往里跳?C语言的陷阱防不胜防的。
主要原 ...

恩,受教了,谢谢

monkerman 发表于 2013-4-26 13:35:43

本帖最后由 monkerman 于 2013-4-26 13:51 编辑

小德GD 发表于 2013-4-26 12:56 static/image/common/back.gif
非常谢谢你用心的回答。想再麻烦问下“i, 当然也是 4, 因为 , 号不是序列点.”这句话怎么理解? ...

不客气. 我也只是恰好知道.
第一个 i 的值其实是第二个 i++ 的 i 值, 因为 VC 对后加操作的自增是在语句结束之后, 也就是分号. 所以还是 4, 逗号在这里并不被当作是序列点.
剩下的上面已经解释了. 你可以改一下, 然后再看结果. 多来几次可能就明白了. 强烈建议比看反汇编, 看到 incl 指令就明白了.
例如:printf("%d, %d, %d, %d", i++, i++, i += j++, ++j); // 看一下结果, 测试时可以这么写, 正式时千万别这么写, 会被骂的.

significance201 发表于 2013-4-26 13:43:24

看来是一个学生
页: [1]
查看完整版本: 求教两个简单的C语言问题