首页
社区
课程
招聘
求助 c++ 代码
发表于: 2011-7-5 16:36 7051

求助 c++ 代码

2011-7-5 16:36
7051
int main()
{
        int i = 1;

        int j = (++i) + (++i) + (++i);           //        i = 4   j = 10  ??
//        int j = (++i) + ( (++i) + (++i) );           //        i = 4   j = 12  ??
        return 0;       
}

谁能解析一下,上面的两种情况,j为什么一个会是10?一个会是12?

[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课

收藏
免费 0
支持
分享
最新回复 (15)
雪    币: 1981
活跃值: (771)
能力值: ( LV13,RANK:420 )
在线值:
发帖
回帖
粉丝
2
蛋腾的人才会写这种标准里没有说明的代码
2011-7-5 17:10
0
雪    币: 129
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
3
没有实际意义吧
2011-7-5 17:19
0
雪    币: 622
活跃值: (294)
能力值: ( LV13,RANK:410 )
在线值:
发帖
回帖
粉丝
4
VC++2010Debug编译,2行结果均为12
过程都是先计算3次++i,然后结果为3个i之和。即4x3=12。
2011-7-5 17:56
0
雪    币: 959
活跃值: (66)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
5
Setting environment for using Microsoft Visual Studio 2010 x86 tools.

C:\Program Files\Microsoft Visual Studio 10.0\VC>cd /d c:\tmp

C:\tmp>cl ++.c
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86
Copyright (C) Microsoft Corporation.  All rights reserved.

++.c
Microsoft (R) Incremental Linker Version 10.00.40219.01
Copyright (C) Microsoft Corporation.  All rights reserved.

/out:++.exe
++.obj

C:\tmp>++
i = 4 j = 12
C:\tmp>cl ++2.c
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86
Copyright (C) Microsoft Corporation.  All rights reserved.

++2.c
Microsoft (R) Incremental Linker Version 10.00.40219.01
Copyright (C) Microsoft Corporation.  All rights reserved.

/out:++2.exe
++2.obj

C:\tmp>++2
i = 4 j = 12
C:\tmp>
2011-7-5 20:15
0
雪    币: 603
活跃值: (40)
能力值: ( LV9,RANK:140 )
在线值:
发帖
回帖
粉丝
6
只有教C语言的老师才会问你这么扯淡的问题。。。。各种编译器都有不一样的结果。。。追求这个太无聊了!
2011-7-5 20:54
0
雪    币: 446
活跃值: (723)
能力值: ( LV7,RANK:100 )
在线值:
发帖
回帖
粉丝
7
看了IDA反汇编DEBUG的程序,与编译器的实现有关,从左向右求值的话
第一个应该等效于 int j = ((++i) + (++i)) + (++i);

i = 1;

tmp1 = i;
tmp1 += 1;
i = tmp1; // i = 2;

tmp2 = i;
tmp2 += 1;
i = tmp2; // i = 3;

tmp3 = i;
tmp3 += i; // tmp3 = 3 + 3 = 6;

tmp4 = i;
tmp4 += 1;
i = tmp4; // i = 4;

tmp3 += tmp4; // tmp3 = 6 + 4;
j = tmp3; // 这里就是 10 了;

第二个改变了顺序 int j = (++i) + ((++i) + (++i));

i = 1;

tmp1 = i;
tmp1 += 1;
i = tmp1; // i = 2;

tmp2 = i;
tmp2 += 1;
i = tmp2; // i=3;

tmp3 = i;
tmp3 += 1;
i = tmp3; // i=4;

tmp4 = i;
tmp4 += i;
tmp4 += i;
j = tmp4; // 这里 j = 12;
2011-7-5 22:24
0
雪    币: 113
活跃值: (100)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
8
这真的是很无聊的问题啊。
引用一段<狂人C>里的话来回答你的问题,希望对你有所帮助。

在现实的运用中,这个两个“++”运算符还是会经常让我们感到头痛,甚至很多成熟的编程团队会将其定义为尽量避免使用的运算符。那么,这个谜一样的运算符,为什么会成为不少程序员极力回避的禁忌呢?
我们首先来看看下面这个简单的代码片段。
i = 3;
j = ++ i + i;
按照前面我们讲过的原理,在第二行的表达式里,CPU读取第一个i值(求++i的值)之前,需要完成将i赋值为i+1这个副效应。但问题在于,在前后两个序点之间,CPU需要两次读取i的值,我们并不清楚会先读哪个i,这个次序选择权在于编译器。我们根本无法控制。也就是说,上面那句话可能表示两种不同的运算语意,产生两种完全不同的运算结果。
语意一
完成副效应i=i+1(i的值变为4)
读第一个i值(此时赋值的副效应已经完成,i的值为4)
读第二个i值(i值已经变为了4,这个i值自然也不例外)
两次读得的i值相加,把结果写入j内存(结果即是8)
语意二
读第二个i值(此时i值为3)
完成副效应i=i+1(i的值变为4)
读第一个i值(此时赋值的副效应已经完成,i的值为4,于是出现了第一个i值和第二个i值之间的值并不相等的现象)
两次读得的i值相加,把结果写入j内存(结果竟然是7)
由于C语言并未明确规定这些运算的次序,因此在完全符合C语言语法规则的前提下,竟然能得到两种结果,这就是所谓的“二义性”。
编写程序时候是不可能容忍代码存在这种“二义性”的,否则程序很可能就变成了“鸡同鸭讲”。代码必须具备唯一确定的语意。
除了涉及到序点,C语言没有规定编译器在这种情况下应该究竟选择哪种语意,这样,表达式 j = ++ i + i就成了一种未定义行为。如同前面曾经提到的那样,这种未定义行为尽管不违背C语言的语法规则,但本质上却是一种错误的代码。
以这里讨论的表达式为例,在求“+”运算符右边的i值的时候,从C语言或代码的角度来说,并不能确定i在内存中确切的值。因为在求“+”左面的操作数——表达式“++i”的值的时候可能改变i的值。由于没有规定求“++i”的值和求“+”右边的i值这两个动作之间的次序,于是求表达式“++ i + i”的值就成了一个未定义的行为。
未定义的行为出现在代码中,就是一个“语病”。只不过这里我们说的“语病”不是那种不符合语法要求的语病,而是那种语法上符合要求,但在语言或代码层面却无法确定其唯一含义的语病。比如,有一个大家很熟悉的广告词――“xx皮鞋,足以自豪的皮鞋”,语法上这句话绝对没有问题,但那个“足”字显然是一语双关的,作为广告语这很好,但编程不是做广告,计算机也不会听你忽悠,它只接受具体明确的、不带有“二义性”的指令。而代码中没有语法错误的“二义性”会导致编译器为你“胡乱”选择一种语意。这当然是不可接受的。
根据程序运算结果揣测j = ++ i + i这样未定义行为没有确定含义的表达式的含义是肤浅幼稚的。因为未定义行为不但是不可能预测的,同样也不可以逆向推测。它产生什么样的后果都不奇怪,哪怕让机器死机,关闭电源甚至火山爆发。C语言的学习者之间经常会出现很多类似这样的可笑对话:一个学习者问,为什么这个计算机(编译器)说“足以自豪的皮鞋”里面的“足”字是“脚”的意思,而不是“足够”的意思?另一个学习者立刻反驳,不对!我的计算机(编译器)明明说“足”是足够的意思嘛!
这两个不明就里的学习者也许会争论上好一阵子,却也得不出一个所以然来。本书的读者对此应该有个清晰的认识,能够很轻松地告诉他们代码“二义性”的来龙去脉。
下面,列出了一些C语言中典型的“二义性”例子。
int i = 3,j;
j = (++i)+(++i)+(++i);
(i++)+ (i++)+(i++)
i = i++
printf(“%d %d\n”, i , i++ );
p=(++p>0)?(p++) :p++);
j = (i = 4) + (i = 5) ;
执行 int k = 11 ; k = 1/3*k++;后,k的值是____。
a += a -= a * a
这些例子,都会让编译器陷入那个“足”是脚还是足够的疑惑。写出这种表达式的人,说明其对于运算符的真实含义还是缺乏了解。可惜的是,在现在国内很多专业的C语言论坛中,还是会有不少程序员,在这个问题上疑惑不解。
这些人往往都还有另一个误区,这个误区就是把优先级和结合性与运算次序相混淆,他们难以理解为什么优先级高的反而后计算。比如下面的表达式:
j + i ++
在这个表达式中,“++”的优先级最高,但这个运算却不是最先进行的。这里的优先级只是决定了“++”这个运算符的运算对象是i,而不是“j+i”,即:
j + (i ++ )
也就是说这个表达式的意义是计算“j+ i”的值,再加上一个副效应。而这个副效应发生的时间,我们只知道会是在编译器求完i值之后,但我们无法知道会是发生在计算“j+i”值的之前还是之后。
然而,不少人把优先级理解成了小学里的“先乘除后加减”,这是完全的误解。这里需要再次强调的是,优先级和运算次序完全是两回事!
对于初学者来说,另外一个错误不得不提。就是,++或--(无论前缀或后缀),只能用于左值。比如,int i; “++i”是可以的,因为i是左值;但++(i+1)是一个语法错误。因为(i+1)只有值的含义不可能表示一块连续的具有类型含义的内存(左值),因此(i+1)只是一个右值表达式。在目前这个学习阶段,只有变量名这种初级表达式是左值表达式。
4.        总结
好了,现在我们完全搞清了“++”运算符的来龙去脉。那么,在代码中应该如何避免上面所提到的“二义性”问题呢?
首先,我们需要把握一个原则,即不在两个序点之间更改同一个变量(严格的术语是对象)两次或更多次(a += a -= a * a就是违背了这种原则的错误代码)。如果两个序点之间只写一次同一对象的值,但同时还存在着读这个对象值的情况,那么必须确保写这个对象的值发生在读这个对象值之后。所以,表达式 i = i + 1 的行为是确定的,而表达式++ i + i则属于未定义的行为。
其次,尽量少使用可能引发“二义性”的复杂表达式。熟练的程序员在使用“++”这类运算符时是极其审慎的,在利用“++”的副效应时,一定要确保不会发生出乎自己意料之外的结果。
或许有人会问,这么麻烦干什么,直接取消可恶的“副效应”不久可以了吗?然而,“副效应”真的那么可恶吗?是否取消了副效应就可以一了百了了呢?其实不是的。
副效应不一定是什么坏事。比如前面例子中for语句中的“++”就是利用了其将i值加1的副效应使得代码写得非常简洁,而求得的i值本身倒是没有什么用处的。
而且,没有副效应的表达式语句,在编译器看来是可以不理睬的废话。比如:
2 + 4 ;
这句话,几乎所有的编译器都不会执行。我们最常用的printf()函数,其实多数情况下使用的是它的副效应,而函数调用得到的值几乎很少被用到。编译器对这样有副效应的表达式语句不可能置之不理。
因此,副效应是非常有用的,有时候甚至是必须的。作为一个合格的程序员,应该善于使用副效应。但是在涉及到改变变量在内存中的值的表达式中,一定要慎重,否则就会像前文中那些例子一样,画虎不成反成犬。
2011-7-5 22:34
0
雪    币: 214
活跃值: (31)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
9
o(∩∩)o...哈哈,难道真的每个C++新手都会问这个问题么?
一看到这个问题就想起我初学C++时的样子,我也曾经跑到安全焦点去发帖问……tk大牛把反汇编结果给贴出来了,我貌似是回复了一句,看不懂,能再详细讲一下么,还是别的什么,哈哈,现在都能想象的出tk大牛看到我的回复后脸色铁青的愤怒样子。

这属于未定义(undefined)行为,至于最终结果是什么,取决于编译器。
2011-7-16 16:59
0
雪    币: 270
活跃值: (97)
能力值: ( LV8,RANK:140 )
在线值:
发帖
回帖
粉丝
10
其实这种代码没有必要研究,没有意义
2011-7-16 17:11
0
雪    币: 238
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
11
这种蛋疼的东西放在考试里只能说明出题的人水平有问题
编译器决定的东西你怎么做
2011-7-17 21:13
0
雪    币: 19
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
12
给个有意义的问题:
        char cbuf= 0X80;
        if(cbuf ==0x80)
        {
                ::AfxMessageBox("错误答案");
       
        }
        else if(cbuf !=0x80)
        {
                ::AfxMessageBox("正确答案");       
        }
2011-7-17 23:48
0
雪    币: 576
活跃值: (1495)
能力值: ( LV12,RANK:210 )
在线值:
发帖
回帖
粉丝
13
char 取值范围 [-128, 127 ],0x80 = 128,你懂的
2011-7-18 00:32
0
雪    币: 44
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
14
把char改成unsigned char
2015-5-25 20:55
0
雪    币: 25
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
15
的确是这样,VC6和VS2010结果都不同,这种蛋疼的问题,我在项目中还真修正过。
2015-5-25 21:03
0
雪    币: 8
活跃值: (120)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
16
这个其实和你编译器相关的;不同编译器得到答案不一样的
2015-5-25 21:06
0
游客
登录 | 注册 方可回帖
返回
//