转自:http://blog.chinaunix.net/u1/34452/showart_1922118.html
看到一篇文章说在C语言中,i=7;使用i++ * i++后得出的值为49,而不是56。按照定义,++运算符在后面,是先引用后自加。在第一次引用后为什么在第二次引用时却没有自加呢。因为我平时使用的是C#,因此在C#中进行了测试,结果表示计算的值为56。看来二者的编译原理不同。
为了深入的比较二者的不同,特用C和C#各自编写了相同的代码,用来计算自加。代码如下:
int i=7;
int x = i++ * i++;
将两者的源代码编译成可执行文件,运行后得出的结果,C编译下为49,.Net编译下为56。
首先看看.Net编译后的IL代码。
.maxstack 4
.locals init (int32 V_0,
int32 V_1)
IL_0000: nop
IL_0001: ldc.i4.7
IL_0002: stloc.0
IL_0003: ldloc.0
IL_0004: dup
IL_0005: ldc.i4.1
IL_0006: add
IL_0007: stloc.0
IL_0008: ldloc.0
IL_0009: dup
IL_000a: ldc.i4.1
IL_000b: add
IL_000c: stloc.0
IL_000d: mul
IL_000e: stloc.1
.Net编译后,首先分配了4个字节的堆栈,并初始化了两个变量。接着装载整数7到堆栈,从堆栈弹出数据保存到变量0,也就是将整数7保存到变量0,再将变量0装载到堆栈,然后将堆栈顶部的数据(也就是整数7)复制一份。这样堆栈中就保存了两份整数7。再加载整数1到堆栈顶部,add指令将堆栈顶部的第一个和第二个数值进行相加,也就是7+1,再将结果存入堆栈,此时堆栈上只有两个数7和8。将8从堆栈顶部弹出保存至变量0,再加载到堆栈并复制一份。这样堆栈中就保存了三个数值,7,8,8。再加载整数1到堆栈,执行add指令后,堆栈中的数值为7,8,9。从堆栈顶部弹出9保存到变量0(也就是变量i),最后,将堆栈中的7,8进行mul操作,得到数值56,弹出保存在变量1也就是变量x。
可以看出C#编译器在自加操作时,复制了一份作为临时变量,紧接着就会执行加法操作。在第二次引用时,其值已经自加了1。所以得出的结果为56。
再来看看C编译器编译后的结果。使用反汇编工具反汇编后的代码。
push ebp
mov ebp, esp
sub esp, 00000008
mov [ebp-08], 00000007
mov eax, dword ptr [ebp-08]
imul eax, dword ptr [ebp-08]
mov dword ptr [ebp-04], eax
mov ecx, dword ptr [ebp-08]
add ecx, 00000001
mov dword ptr [ebp-08], ecx
mov edx, dword ptr [ebp-08]
add edx, 00000001
mov dword ptr [ebp-08], edx
mov eax, dword ptr [ebp-08]
push eax
mov ecx dword ptr [ebp-04]
push ecx
首先分配8个字节堆栈空间,再将整数7加到堆栈,再将堆栈顶部的整数7给寄存器eax,然后将这两个数进行imul操作,结果存入eax,其后的指令则是对整数7进行了两次加1的操作,并将结果存入寄器ecx。再将这两个值移入堆栈。
可以看出C编译器在执行这个操作时,直接先进行了乘法的操作,然后再处理自加的问题。
这是两个编译器对何时先引用后自加的理解执行不同,C#编译器对++运算符,是在引用后马上进行自加,那么在同一表达式中第二次引用时就已经是自加后的值了。而C编译器则是先引用,并执行完整个表达式后,再将变量自加,在执行表达式时,不管表达式中有多少个引用,都不会执行自加操作,只有在表达式执行完后,才会进行自加,有几次自加就加几次。
分享到:
相关推荐
例如,在C++中,可以使用`__asm`关键字插入汇编代码,而在C#中,可以调用DllImport特性来引用C或C++编写的动态链接库(DLL),这些库可能包含汇编代码。 学习汇编语言对于理解计算机底层工作原理至关重要,而掌握C#...
在C#中直接操作硬件接口,特别是GPIO(General Purpose Input/Output)接口,通常需要使用底层的汇编语言,因为C#作为高级语言并不直接支持汇编指令。在本案例中,我们需要控制带有GPIO接口的工控机来驱动继电器,而...
《C#试题汇编》是针对C#编程语言的一份综合学习资料,旨在帮助学习者深入理解和掌握C#的关键概念和技术。这份资源包含了各种类型的题目,涵盖了从基础语法到高级特性的广泛范围,同时提供了参考答案,使得学习过程更...
《C与汇编语言构建DOS操作系统:源码解析》 在计算机科学的历史长河中,DOS(Disk Operating System)操作系统曾是个人电脑领域的主导力量。它以其简单、高效的特点,为程序员提供了深入理解操作系统原理的机会。本...
C#的`System.IO`命名空间提供了一些基础的文件和流操作,但完整的设备驱动实现通常需要更低级别的语言,如C或汇编。 4. 文件系统管理:文件系统是操作系统用于组织和管理磁盘上数据的一种方式。模拟文件系统需要...
- **I/O操作**:对于汇编语言中的输入输出操作,比如设置端口方向、读取/写入端口值等,在C语言中通常通过库函数或者直接操作内存地址实现。例如: ```c void send() { unsigned char *r0 = (unsigned char*)30; ...
- **低级特性**:C语言接近汇编,可以直接操作内存,提供指针和位运算,适合底层编程。 - **结构化编程**:C语言强调结构化编程,通过函数划分代码模块,提高可读性和可维护性。 - **预处理器**:C语言的预处理器...
C语言的特点在于它的简洁性和高效性,可以直接对硬件进行操作,因此常用于操作系统、嵌入式系统以及需要高性能的软件开发。然而,C语言的学习曲线相对较陡,因为它要求程序员对内存管理有深入的理解,包括指针的使用...
9. **汇编和高级语言结合**:在实际开发中,通常将汇编语言用于性能敏感的部分,而其他部分则使用高级语言(如C++或C#)编写,通过预处理器宏或链接时嵌入来结合两者的优点。 学习Win32汇编语言需要理解处理器的...
在 C 或 C 中与委托最接近的是函数指针,但函数指针只能引用静态函数,而委托可以同时引用静态方法和实例方法。在后一种情况中,委托不仅存储对方法入口点的引用,还存储对调用其方法的对象的引用。与 C 函数指针...
本文将深入探讨四种编程语言:汇编、C语言、Turbo C、Visual Basic (VB) 和 D 语言,以及它们的常用命令集。 首先,我们来看看汇编语言。汇编语言是一种低级编程语言,它的指令直接对应于计算机的机器代码。每个...
在C#中,有时候我们需要使用低级别的操作,比如直接操纵内存或执行特定的CPU指令,这时就需要嵌入汇编代码。虽然C#本身并不支持内联汇编(inline-asm),但它提供了其他方式来实现类似的功能。本文将探讨如何通过C#...
如C/C++、PASCAL、FORTRAN、COBOL、JAVA、BASIC等,优点是较好地克服了机器语言和汇编语言的不足,采用近似自然语言的记号和语法,大大提高了编程的效率和程序的可读性,但缺点是高级语言的运行效率没有汇编语言和...
在IT领域,尤其是在系统管理和软件开发中,有时我们需要获取计算机硬件的特定信息,例如硬盘物理序列号。硬盘序列号是每个硬盘独特的标识符,对于跟踪设备、验证软件许可证或者进行故障排查都非常重要。本文将详细...
微软的MSDN(Microsoft Developer Network)提供了丰富的C++、C和汇编语言的官方文档,包括API参考、教程、示例代码以及开发工具的使用指南。这些文档对于开发者来说是宝贵的资源,有助于理解和应用上述语言特性,...
C#中可用来显式重载的运算符比C++要少,很大程度上是因为C#编译器是运用一些定制的基本操作符重载来自动计算出组合操作符的重载。 库的支持是C#和C++的一种区别。C++依赖于标准库,而C#依赖于.NET基类库。C#的库和...
这类语言包括Java、C、C++、C#、Pascal、Python、Lisp、Prolog、FoxPro、VC、易语言、C语言习语言等。高级语言的语法和命令格式多样,使得程序员可以更加专注于解决问题,而不必关心底层硬件细节。高级语言的优点...
通过这个库,你可以深入学习汇编语言的基本概念,如指令集、寄存器操作、内存访问以及如何将汇编代码与高级语言(如C/C++)混合使用。此外,你还能了解到不同处理器架构(如x86/x64、ARM等)的汇编语言差异,并掌握...
描述中提到的“汇编和C编写”是指该程序使用了两种编程语言:汇编语言和C语言。汇编语言是一种低级语言,直接对应于机器指令,编写出来的程序执行效率高,但编写复杂度较高。而C语言则是一种中级语言,具有较好的...
此外,P/Invoke技术允许C#调用未托管的C/C++代码,这对于需要高性能操作的场景非常有用。 综上所述,C#和C++各有优势。C#适合于快速开发、易于维护的项目,尤其是那些需要利用.NET Framework丰富特性的项目;而C++...