Chinaunix

标题: 新手问一个关于i++和++i的问题 [打印本页]

作者: FeCen    时间: 2009-05-05 14:46
标题: 新手问一个关于i++和++i的问题
#include <stdio.h>

#define FUN1(x) (x * x)
#define FUN2(x) (x * x + x)

int main()
{
int i = 3, j, k;
j = FUN1(i++);
k = FUN1(++i);
printf("j = %d, k = %d\n", j, k);

i = 3;
j = FUN2(i++);
k = FUN2(++i);
printf("j = %d, k = %d\n", j, k);

return 0;
}

具体结果我当然可以运行一下这个程序来看,但是对于得到的结果我不能很好的进行分析说明。
大家帮忙看看啊。

多谢!
作者: xinglp    时间: 2009-05-05 14:47
很经典的考试面试问题啊
作者: FeCen    时间: 2009-05-05 14:55
标题: 回复 #2 xinglp 的帖子
哈哈,没错!
我就是在有关面试的书中看到的,稍加修改,加上了FUN2而已。
作者: langue    时间: 2009-05-05 14:58
#include <stdio.h>

int main()
{
int i = 3, j, k;
j = i++ * i++;
k = ++i * ++i;
printf("j = %d, k = %d\n", j, k);

i = 3;
j = i++ * i++; + i++;
k = ++i * ++i + ++i;
printf("j = %d, k = %d\n", j, k);

return 0;
}
作者: herohust    时间: 2009-05-05 15:02
标题: 回复 #2 xinglp 的帖子
第一步:
int i = 3, j, k;   
j = FUN1(i++);  // j = 3*3 =9; 之后 i =5
k = FUN1(++i);  // k = (++i)*(++i) 在乘之前,i所标识的内存单元累加1两次,为7; 然后再相乘 k = 49.
作者: daybreakcx    时间: 2009-05-05 15:06
强烈建议:加上括号,不然很容易出错的
先把所有的都用字符串替换先进行:
int i = 3, j, k;
j = i++ * i++;
k = ++i * ++i;
i = 3;
j = i++ * i++; + i++;
k = ++i * ++i + ++i;
然后个人认为进行反汇编是最好理解的:
j = i++ * i++;
00401033  mov         eax,dword ptr [ i]
00401036  imul        eax,dword ptr [ i]
0040103A  mov         dword ptr [j],eax
0040103D  mov         ecx,dword ptr [ i]
00401040  add         ecx,1
00401043  mov         dword ptr [ i],ecx
00401046  mov         edx,dword ptr [ i]
00401049  add         edx,1
0040104C  mov         dword ptr [ i],edx
k = ++i * ++i;
0040104F  mov         eax,dword ptr [ i]
00401052  add         eax,1
00401055  mov         dword ptr [ i],eax
00401058  mov         ecx,dword ptr [ i]
0040105B  add         ecx,1
0040105E  mov         dword ptr [ i],ecx
00401061  mov         edx,dword ptr [ i]
00401064  imul        edx,dword ptr [ i]
00401068  mov         dword ptr [k],edx
j = i++ * i++; + i++;
00401091  mov         edx,dword ptr [ i]
00401094  imul        edx,dword ptr [ i]
00401098  mov         dword ptr [j],edx
0040109B  mov         eax,dword ptr [ i]
0040109E  add         eax,1
004010A1  mov         dword ptr [ i],eax
004010A4  mov         ecx,dword ptr [ i]
004010A7  add         ecx,1
004010AA  mov         dword ptr [ i],ecx
004010AD  mov         edx,dword ptr [ i]
004010B0  add         edx,1
004010B3  mov         dword ptr [ i],edx
k = ++i * ++i + ++i;
004010B6  mov         eax,dword ptr [ i]
004010B9  add         eax,1
004010BC  mov         dword ptr [ i],eax
004010BF  mov         ecx,dword ptr [ i]
004010C2  add         ecx,1
004010C5  mov         dword ptr [ i],ecx
004010C8  mov         edx,dword ptr [ i]
004010CB  add         edx,1
004010CE  mov         dword ptr [ i],edx
004010D1  mov         eax,dword ptr [ i]
004010D4  imul        eax,dword ptr [ i]
004010D8  add         eax,dword ptr [ i]
004010DB  mov         dword ptr [k],eax

晕啊[ i]不带空格还显示不出来

[ 本帖最后由 daybreakcx 于 2009-5-5 15:10 编辑 ]
作者: dreamice    时间: 2009-05-05 15:07
这种题很晕,项目中没人这么用
作者: FeCen    时间: 2009-05-05 15:11
标题: 回复 #4 langue 的帖子
我的疑问是:
为什么i++ * i++不是3 * 4而是3 * 3呢?

另外,
i = 3;
j = i++ * i++; + i++;
k = ++i * ++i + ++i;
这两句的执行有点诡异。
首先看下面那句,它的结果是k=73,根据这个结果反推应该是8*8 + 9,也就是说,在执行乘法的时候后面加号后面的那个++i是先不执行的。
那么用这个经验来看上面的第二句,为什么不是3*3 + 5呢?事实上,结果是3*3 + 3
作者: daybreakcx    时间: 2009-05-05 15:12
对这种题目很无语,难道多拿一两行把代码分开很繁琐或者是很降低效率吗,非得都弄到一起
作者: FeCen    时间: 2009-05-05 15:14
标题: 回复 #7 dreamice 的帖子
呵呵,确实是没人用,我就是好奇,想不通啊。
作者: daybreakcx    时间: 2009-05-05 15:15
原帖由 FeCen 于 2009-5-5 15:11 发表
我的疑问是:
为什么i++ * i++不是3 * 4而是3 * 3呢?

另外,
i = 3;
j = i++ * i++; + i++;
k = ++i * ++i + ++i;
这两句的执行有点诡异。
首先看下面那句,它的结果是k=73,根据这个结果反推应该是8 ...

诡异也没办法,规则是别人定的,翻译成汇编代码是编译器干的,你不能插手,只能把东西交给他
作者: FeCen    时间: 2009-05-05 15:27
原帖由 daybreakcx 于 2009-5-5 15:15 发表

诡异也没办法,规则是别人定的,翻译成汇编代码是编译器干的,你不能插手,只能把东西交给他


别人定的规则也是规则啊,呵。

而且好像也跟运算符优先级没有关系,因为不管是先++还是后++,它们的优先级都高于*和+。
不知道有没有人能够解释清楚,虽然除了在有些变态的面试题中,意义不大。
作者: langue    时间: 2009-05-05 16:13
是的,这就叫 implementation-defined behavior。
看见这种面试题,完全可以直接彪悍地写上一句:依赖于实现。
真用这种面试题的公司,不去也罢。

J:\>cl testincr.c
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 12.00.8804 for 80x86
Copyright (C) Microsoft Corp 1984-1998. All rights reserved.

testincr.c
Microsoft (R) Incremental Linker Version 6.00.8447
Copyright (C) Microsoft Corp 1992-1998. All rights reserved.

/out:testincr.exe
testincr.obj

J:\>testincr
9
12

J:\>


这是 GCC 3 (i386)

$ gcc -o testincr testincr.c
$ ./testincr
9
12


再给你看一下 MSC 6.0 的输出:

testincr.png (8.14 KB, 下载次数: 10)

testincr.png

作者: daybreakcx    时间: 2009-05-05 16:20
标题: 回复 #13 langue 的帖子
把程序员当作编译器来用的
作者: prolj    时间: 2009-05-05 16:30
这也是NC喜欢拿出来出的NC题,我愿意把GCC改成什么样子就什么样子。
作者: FeCen    时间: 2009-05-05 16:47
原帖由 langue 于 2009-5-5 16:13 发表
是的,这就叫 implementation-defined behavior。
看见这种面试题,完全可以直接彪悍地写上一句:依赖于实现。
真用这种面试题的公司,不去也罢。

J:\>cl testincr.c
Microsoft (R) 32-bit C/C++ Optimiz ...


呵呵,既然如此,那此帖讨论就到此为止吧。
我也就是求一个放心,担心自己的理解有疏漏,既然是implementation-defined behavior,那就没有什么可深究的了。

多谢各位!
作者: vitacy    时间: 2009-05-05 18:09
提示: 作者被禁止或删除 内容自动屏蔽
作者: cgmeco    时间: 2009-05-05 19:57
先把C/C++的宏定义搞清楚,建议先看看c premir,其中对于宏定义的解释很详细,对于你这个问题很有帮助!
作者: glasslion    时间: 2009-05-05 20:12
我觉得这没标准答案吧,标准里对这类应该是未定义的
作者: glasslion    时间: 2009-05-05 20:23
俺回去温习下ANSI/ISO,再来
作者: flyingtime    时间: 2009-05-06 09:28
根编译器的实现相关:
vs 2005:
j=9,k=49
j=12,k=90
gcc:
j=9,k=49
j=12,k=73
作者: daybreakcx    时间: 2009-05-06 10:01
标题: 回复 #21 flyingtime 的帖子
两遍编译的标准不同,这种问题就显得很没有意义了,还是在函数里面加上括号吧
作者: 大卫与审判之剑    时间: 2009-05-06 13:34
#include <stdio.h>

#define FUN1(x) (x * x)
#define FUN2(x) (x * x + x)

int main()
{
int i = 3, j, k;
j = FUN1(i++);       //i++  先使用,后++, 相当于 i*i; 两次i++;   因此j=3*3=9   
k = FUN1(++i);       //++i  先++,再使用.  由于宏的作用之前两次i++, i=5. 两次++i之后,i=7. i*i=49.  K=49
printf("j = %d, k = %d\n", j, k);

i = 3;
j = FUN2(i++);       //i++  先使用,后++, 相当于 i*i+i; 三次i++;   因此j=3*3+3=12  
k = FUN2(++i);       //++i  先++,再使用.  由于宏的作用之前三次i++, i=6. 两次++i之后计算表达式i*i的值8*8=64。再做一次++i,i=9. 64+9=73.  K=73
printf("j = %d, k = %d\n", j, k);

return 0;
}



你也可以用gcc -E 看宏展开后的代码
int main()
{
int i = 3, j, k;
j = (i++ * i++);
k = (++i * ++i);
printf("j = %d, k = %d\n", j, k);

i = 3;
j = (i++ * i++ + i++);
k = (++i * ++i + ++i);
printf("j = %d, k = %d\n", j, k);

return 0;
}
作者: langue    时间: 2009-05-06 13:46
标题: 回复 #22 daybreakcx 的帖子
加括号也没用,清清楚楚是由实现决定的行为,括号改变的只是把 i++ 作为整体以后的运算顺序。具体哪个 i 先求,哪个 i 后求,还是先一起求了再自增,标准里压根就没说。
作者: 大卫与审判之剑    时间: 2009-05-06 13:52
21楼提到的不同结果问题,由于没有VS,没有办法验证。如果真如所言。 就晕了
作者: egmkang    时间: 2009-05-06 14:03
删帖吧




欢迎光临 Chinaunix (http://bbs.chinaunix.net/) Powered by Discuz! X3.2