求解一个C#语法的怪问题
有一段很简单的代码:int a=10;
a = a++
这段代码在c#下编译执行后 a仍然=10 ;而按c++语法执行后 a=11
请牛人给予解释
本人之前认为在c#下编译执行后应该为11,a应该只指向了同一块内存空间呀 --------------------编程问答-------------------- 看来楼主应该好好了解下C#值类型和引用类型的区别 --------------------编程问答-------------------- a++是先赋值,再运算(这语句等效于 a=10;a++;)
++a是先运算,再赋值(这语句等效于 a++;a=11;)
这是基本内容啊!
--------------------编程问答-------------------- 不认真看书的结果... --------------------编程问答-------------------- ++ 运算符(C# 参考)
增量运算符 (++) 将操作数加 1。增量运算符可以出现在操作数之前或之后:
备注
第一种形式是前缀增量操作。该操作的结果是操作数加 1 之后的值。
第二种形式是后缀增量操作。该运算的结果是操作数增加之前的值。
数值类型和枚举类型具有预定义的增量运算符。用户定义的类型可重载 ++ 运算符。在枚举时通常允许整型运算。
示例
复制代码
// cs_operator_increment.cs
using System;
class MainClass
{
static void Main()
{
double x;
x = 1.5;
Console.WriteLine(++x);
x = 1.5;
Console.WriteLine(x++);
Console.WriteLine(x);
}
}
输出
2.5
1.5
2.5
*****************************************************************************
欢迎使用CSDN论坛专用阅读器 : CSDN Reader(附全部源代码)
http://www.cnblogs.com/feiyun0112/archive/2006/09/20/509783.html --------------------编程问答-------------------- 和C++的运算规则不一样。要分开对待。
两者只是相似,绝不等同。 --------------------编程问答-------------------- C#下结果如下:
int a = 10;
a++;
++a;//与上句等同
MessageBox.Show(a.ToString());
///////////////结果都为11
int a = 10;
a = a++;
MessageBox.Show(a.ToString());
////////////结果为10
--------------------编程问答-------------------- 补充说明一下:
a++与++a的结果是相同的!
也就是四楼所说的
增量运算符 (++) 将操作数加 1。增量运算符可以出现在操作数之前或之后: --------------------编程问答-------------------- 又看了一下
感觉楼上诸位对困扰楼主的问题理解有误,我以为楼主认为
int a=10;
a = a++;
会得到a=11,是因为C++中a= 和a++都是对同一块内存空间进行的运算
而在C#中,int做为值类型在运算的时候传递的是一个副本
也就是说这个式子,先建立一个a的副本,然后把副本的值赋给a,再对副本进行自增的操作
最后的结果和先赋值还是先自增并没有关系,如果是对a本身的操作,自增后的结果都会是11的 --------------------编程问答-------------------- ++a是前缀增量操作...该操作的结果是操作数加1之后的值...此时++a==11,此后a=11...
a++是后缀增量操作...该运算的结果是操作数增加之前的值...此时a++==10,此后a=11...
a=a++;等同于a=10;...结果...注意是结果... --------------------编程问答-------------------- 上面解釋已經很清楚了,不再蕾書! --------------------编程问答-------------------- 楼主应该换成这样
int a = 10;
int b = a++;
那么就清楚很多了。 --------------------编程问答-------------------- 事实上:
int a=10;
a = a++;
Console.Write(a);
输出的是10
int a = 10;
int b = a++;
Console.Write(a);
输出的是11 --------------------编程问答-------------------- 这个问题问的好,学了不少东西,也多谢8楼的解释,该死的.net运行机制跟C的都不一样了 --------------------编程问答-------------------- 又研究了一下,8楼的答案有问题。
在C#中,a = a++; 的执行过程是先做一个a的副本值为10,然后对a做++操作,此时a值为11,然后再把那个副本的值10赋给a,a的值就是10了。
而C++中,a = a++; 的执行顺序是:先做一个a的副本值为10,然后把副本值10赋给a,a还为10,然后执行++,a的值就变为11了。
这回应该解释清楚了,呵呵! --------------------编程问答-------------------- 嗯,具体的过程确实应该和楼上的一样
--------------------编程问答-------------------- 被后缀自增描述的先操作后自增过程给忽悠了 --------------------编程问答-------------------- 谢谢楼上各位的讨论
我刚才看了下中间代码
c#编译器下的中间代码:
int a = 10;
00000027 mov esi,0Ah
a = a++;
0000002c mov edi,esi -------将a的值移入一个寄存器
0000002e inc esi -------执行++操作
0000002f mov esi,edi -------再将临时寄存器的值覆盖回来
感觉在c#编译器下 ++写在后面的时候,不太看得懂中间代码的意思,整个就是折腾了一圈又回来
c++编译器下的中间代码:
int a=10;
0041137E mov dword ptr [a],0Ah
a = a++;
00411385 mov eax,dword ptr [a] --------先将a的值放入一个临时寄存器
00411388 mov dword ptr [a],eax --------执行赋值操作
0041138B mov ecx,dword ptr [a] --------再将赋值操作之后的a放入临时寄存器
0041138E add ecx,1 --------在临时寄存器里执行++操作
00411391 mov dword ptr [a],ecx --------最后把++计算结果再返回给指针 ptr [a]
C++编译器下,中间代码的逻辑很清楚 --------------------编程问答-------------------- c#遇到对于值类型的赋值操作时,会先创建副本,然后用这个副本来进行真正的赋值
并且从中间代码来看,c#中的++后缀似乎并没有很好的实现先赋值再加1的准则 --------------------编程问答-------------------- c#里头b=a++是先把a的值保存在临时变量中 然后a的值自加 然后把临时变量的值给b
c++里头b=a++是先把a放到一个临时变量中 然后把临时变量赋给b 然后a自加
所以遇到a=a++的时候再c#中就是设一个临时变量c 先c=a 然后a++ 然后a=c 所以a先自加了1 然后右还原成原来的值
在c++里头a=a++时 先c=a 然后a=c 然后a++
执行细节顺序不同而已 可以看看楼上的反汇编 --------------------编程问答-------------------- 相信自己测试的结果,道理讲不讲都不重要 --------------------编程问答-------------------- 这是好贴,学到不少东西 --------------------编程问答-------------------- a++是先赋值,再运算
++a是先运算,再赋值
--------------------编程问答-------------------- 其实就是个执行顺序的问题 --------------------编程问答-------------------- 不错啊 a++是先赋值,再运算 ++a是先运算,再赋值 总之就是个执行顺序的问题 --------------------编程问答-------------------- 是啊,这个帖要顶的.
没事也学习一下,学习成果如下:
{
int a = 10;
//a++;
int b;
b = a++;
Console.WriteLine(b);
Console.WriteLine(a);
System.Console.ReadLine();
}
IL_0000: nop
IL_0001: ldc.i4.s 10
IL_0003: stloc.0 //存为a
b = a++
IL_0004: ldloc.0 //参数入栈,用a
IL_0005: dup //复制a,栈顶有两个连续的10值
IL_0006: ldc.i4.1 //加1
IL_0007: add
IL_0008: stloc.0 //存为a,出栈
IL_0009: stloc.1 //存为b,出栈
以上相当于:
a1 = a; //副本10
a = a.add(1); //a = 11
b = a1 //b = 10,故即使把"b"换成"a",结果仍为10
打印结果:
IL_000a: ldloc.1 //用b,打印出10
IL_000b: call void [mscorlib]System.Console::WriteLine(int32)
IL_0010: nop
IL_0011: ldloc.0 //用a,打印出11
IL_0012: call void [mscorlib]System.Console::WriteLine(int32)
IL_0017: nop
若C++如17楼分析,则:
b = a++;
可能相当于:
a1 = a; //为自己创造一个替身a1参与表达式计算
b = a1; //赋值到b
a = a.add(1); //a入寄存器增1后赋值给a
--------------------编程问答-------------------- ++写在前面跟后面是有很大区别的 --------------------编程问答-------------------- 感谢25楼,我也跟你一样学习了,以下是终极答案
楼主给出的代码
class Program
{
static void Main()
{
int a = 10;
a = a++;
}
编译后成为exe文件,用微软自带的ildasm.exe打开它,观察其内部的il语句执行步骤
.method private hidebysig static void Main() cil managed
{
.entrypoint
// 代码大小 11 (0xb)
.maxstack 3
// 初始化一个int32型变量a,其代号是0
.locals init ([0] int32 a)
// 空指令
IL_0000: nop
// ldc代表把一个数字常数压入堆栈。i4代表int32,s代表sum,10是sum
IL_0001: ldc.i4.s 10
// 从堆栈中弹出一个值,存入第0个变量,也就是存入变量a。
// 注意弹出堆栈的同时堆栈会消去该值,至此完成了第一句 int a = 10;
IL_0003: stloc.0
// 将第0个变量压入堆栈
IL_0004: ldloc.0
// 在堆栈上复制一个值,由于是a++,所以先保留变量a值的副本,然后再执行a的自增
IL_0005: dup
// 把一个数字常数压入堆栈。i4代表int32,1代表数字1,只有1,2,4,8等数可以跳过sum方式
IL_0006: ldc.i4.1
// 将堆栈上两个数字相加,消去堆栈上的这两个数字,再将结果压入堆栈
IL_0007: add
// 将堆栈上的数字赋给第0个变量,消去堆栈上的这个数字,此时完成了a的自增,a值为11
IL_0008: stloc.0
// 将堆栈上的数字赋给第0个变量,消去堆栈上的这个数字
// 这句是"="操作符的操作,将变量a自增前值的副本赋给"="左边的变量a,这样a值最终为10
IL_0009: stloc.0
// 返回指令,它使得方法返回到调用现场。
IL_000a: ret
} // end of method Program::Main
有兴趣了解il中间语句的朋友,我将在稍后放上详尽的微软官方的资料,保证你能看懂每一句il代码,更加了解.net,请有兴趣的朋友加我QQ 282898034 --------------------编程问答-------------------- LZ你太有才了,不要一味的拿样比样,看事物要看本质,不同的环境要不同对待。 --------------------编程问答-------------------- 简单一些,lz 看这段代码应该就懂了:
int a = 10;
Console.WriteLine(a++);
Console.WriteLine(a);
输出
10
11
Console.WriteLine(a++); 先把 a的值给输出来,然后再 + 1 --------------------编程问答-------------------- 好帖,学习了 --------------------编程问答-------------------- 先赋值,后自加 --------------------编程问答-------------------- 都告诉你们了,是先自加,后赋值,好好研究27楼再发言 --------------------编程问答-------------------- 又学到了新知识 --------------------编程问答-------------------- Re:楼主
俺没看见牛人,只看见很多牛在天上飞,下边的人都在吹,自说自话的解释起来了a++和++a的区别,连楼主要问什么都没搞明白。
能提这么典型的问题,楼主当然懂得a++是什么意思,更不用别人解释a++和++a的区别,楼主显然明白。
楼主是问:
“本人之前认为在c#下编译执行后应该为11,a应该只指向了同一块内存空间呀”
这个问题非常典型,短短一句 a = a++; 既可以观察.net算术操作符优先级别,又可以观察++自增运算符的值复制机制,当然那是发生在临时堆栈上的,估计吹牛的人还不认识这个临时堆栈。
正如我在27楼解释的那样,a确实从头到尾只分配了一块内存空间,但此空间只是用来保存和提取a变量值,而不进行任何运算,在运算的时候,.net会另外开辟一个临时堆栈来做。
因为++自增运算符比=赋值运算符优先级高,所以顺序一定是:先做a的自增,再做赋值。
在a自增之前,因为考虑到a++对外表现的应当是a自增前的值,所以.net会在临时堆栈上提前复制一个a的值,稍后会用到。
这样临时堆栈上会出现两个10,新复制的那个10自增后变成11,先保存进了变量a的内存空间,同时出栈,这样a就成了11,临时堆栈上就只剩下了原来的那个10。
这时赋值运算符开始执行,临时堆栈里剩下的这个10就保存进了变量a的内存空间,同时出栈,这样a又变回10,临时堆栈也空了。
运算结束。
楼主原来以为会是11,我也以为是,因为我们看不到临时堆栈上的双胞胎a,从a = a++;语句字面上理解,还以为a自始至终只有一个,就像楼主说的同一块内存空间,所以才会误以为会是11。 --------------------编程问答-------------------- 很多人都是不看下面的回帖就发言的 所以上面的解释成透明的了 --------------------编程问答-------------------- 学习啊,都是知识啊。
大家都好牛。
顶 --------------------编程问答-------------------- 支持34楼的 --------------------编程问答-------------------- 呵呵,看了楼上的发言真是学了不少东西,
我学过C++和C# ,不过没有注意这些细节,惭愧。。。
看来还是楼主心细啊!
顶一下! --------------------编程问答-------------------- 来顶一个 --------------------编程问答-------------------- 来顶一个 --------------------编程问答-------------------- 支持34楼 --------------------编程问答-------------------- 高深啊,学习 --------------------编程问答-------------------- 语法这东西,也有很多细节需要深挖啊。 --------------------编程问答-------------------- 是啊是啊~ 嘿嘿 学习学习!~ --------------------编程问答-------------------- 是啊,这个帖要顶的.
没事也学习一下,学习成果如下:
{
int a = 10;
//a++;
int b;
b = a++;
Console.WriteLine(b);
Console.WriteLine(a);
System.Console.ReadLine();
}
IL_0000: nop
IL_0001: ldc.i4.s 10
IL_0003: stloc.0 //存为a
b = a++
IL_0004: ldloc.0 //参数入栈,用a
IL_0005: dup //复制a,栈顶有两个连续的10值
IL_0006: ldc.i4.1 //加1
IL_0007: add
IL_0008: stloc.0 //存为a,出栈
IL_0009: stloc.1 //存为b,出栈
以上相当于:
a1 = a; //副本10
a = a.add(1); //a = 11
b = a1 //b = 10,故即使把 "b "换成 "a ",结果仍为10
打印结果:
IL_000a: ldloc.1 //用b,打印出10
IL_000b: call void [mscorlib]System.Console::WriteLine(int32)
IL_0010: nop
IL_0011: ldloc.0 //用a,打印出11
IL_0012: call void [mscorlib]System.Console::WriteLine(int32)
IL_0017: nop
若C++如17楼分析,则:
b = a++;
可能相当于:
a1 = a; //为自己创造一个替身a1参与表达式计算
b = a1; //赋值到b
a = a.add(1); //a入寄存器增1后赋值给a
-------------------------------------------
支持 --------------------编程问答-------------------- C#这点的确让从C++来到人有点不适应 --------------------编程问答-------------------- 细节啊,细节 --------------------编程问答-------------------- 当我们说出的话让别人产生误解的时候,我们要考虑的是换个说法,而不是一味地嘲笑别人看不懂。
当然,我觉得楼主问题拿来探讨很好,但如果我看到了我们部门有人在程序中这么使用的话,叫他改那是没话说的了。
另:
CSDN里真是有太多的回帖不看贴或者连帖子问题都看不懂就开始为别人指“阳光大道”的人了。 --------------------编程问答-------------------- "++"或"--"放在变量前与放在变量后的意义是不一样的```
如果"++"是放在变量前,那么程序执行的过程是:先运算,后符值.
比如:
int a=10,b;
b=++a;
程序执行结果为:
a=11,b=11
也就是说,当程序执行到"b=++a"语句时,先运算"++",既a=10+1=11;
然后再通过符值运算符"="把a等于11的值符与b,这时b的值也就变为11了.
如果"++"是放在变量后,那么程序执行的过程是:先符值,后运算.
比如:
int a=10;
int b;
b=a++;
程序执行结果为:
a=11,b=10
也就是说,当程序执行到"b=a++"语句时,先把a等于10的值通过符值运算符"="符与b,
这时b就有了一个初始值为10;
然后程序在计算"++",把计算后的值符与a,即a=10+1=11.
区别就是这样了,其实也好理解的,执行的顺序就向一般阅读一样,从左往右就OK了.
ps:
本人系初学者,如有说得不对的地方还望各位大侠指点啊,在此谢过大家了. --------------------编程问答-------------------- 这里有详细解释:
http://blog.csdn.net/yydy1983/archive/2007/06/29/1671107.aspx
http://www.program-life.cn/Contents.aspx?id=95 --------------------编程问答-------------------- 49楼没搞明白啊,看懂了34楼和27楼再发言,否则永远是初学者,你看哪本书上写过=比++优先级高? --------------------编程问答-------------------- 的确是基本内容,我这不看书的懒人都知道!!!看来楼主要挨PP --------------------编程问答-------------------- 5555555555555~~~~~~~~~~~~~我错了,刚刚看了27楼和34楼才明白了```````````今后我一定加强学习~~~~~~~~~~~~~~~~~~~ --------------------编程问答-------------------- 支持 17楼~~~~~~~~~~~~~
支持 17楼~~~~~~~~~~~~~
支持 17楼~~~~~~~~~~~~~ --------------------编程问答-------------------- 上面解释的已经十分清楚了,请你好好掌握基础!!! --------------------编程问答-------------------- 写得好的帖子看的人就是多 支持. --------------------编程问答-------------------- 学习....
编译永远多比较重要 --------------------编程问答-------------------- 靠,这么个问题至于这么讨论吗? --------------------编程问答-------------------- 分成两行写,避免一切烦恼 --------------------编程问答-------------------- 先回复,再看贴 --------------------编程问答-------------------- 对比一下
int b;
int a = 10;
00000031 mov edi,0Ah
b = a++;
00000036 mov ebx,edi
00000038 inc edi
00000039 mov dword ptr [ebp-44h],ebx
int i = 10;
0000003c mov esi,0Ah
i=i++;
00000041 mov ebx,esi
00000043 inc esi
00000044 mov esi,ebx --------------------编程问答-------------------- 应该是看你在什么情况下输出了,
是输出a还是输出a++,还是a++之后输出a
分情况定吧 --------------------编程问答-------------------- 61楼对比的好,问题的所在就是这里了。
其实就是“用副本赋值”和“自身加一”谁先做的问题
如果是
a = 10;
b = a++;这种情况时“用副本赋值”和“自身加一”谁先做都一样。“用副本赋值”是b的是,“自身加一”是a的事,相互不影响。而
a = 10;
a = a++;的话,“用副本赋值”和“自身加一”的左值都是a,顺序不能忽略了。
个人认为a最终等于11才是更符合逻辑的结果,不明白MS的大牛门的用意。。。 --------------------编程问答-------------------- 不过等于10也解释得过去,a=a++嘛,如果是
int b=10;
int a=b++;那么a就应该等于10嘛。
呵呵,这个题可以拿来做辩证题了。经典~
补充:.NET技术 , C#