重读TCPL:从汇编语句浅析++i和i++

TCPL(The C Programming Language)对自增和自减运算符的叙述并不多。在中文版的12页(英文版18页)和37页(英文版46页)中,只是简单地标明TCPL这本书会同意使用前缀形式,并用一段话解释了两者对值和值+1先后的区别。

之所以纠结这个,是因为用C语言写单片机的程序的时候,有的老师会强调++i和i++在使用的时候没有区别。那这这两个语句在汇编层到底是怎样的呢?

用简单的几行代码测试了一下,代码如下:

int main()
{
    int a = 0, c = 0;
    c = a++;
    c = ++a;
    printf("%d", a);

    for (; a < 4; a++) printf(" ");
    for (; a < 6; ++a) printf(" ");

    getchar();

    return 0;
}

++a和a++的汇编显示:

int a = 0;
0027179E mov dword ptr [a],0
a++;
002717A5 mov eax,dword ptr [a]
002717A8 add eax,1
002717AB mov dword ptr [a],eax
++a;
002717AE mov eax,dword ptr [a]
002717B1 add eax,1
002717B4 mov dword ptr [a],eax

其中只要是单独出现的a++和++a。那么,他们的汇编语句就是一样的。不但行数一样,顺序也是相同的。所以前置语句和后置语句在单独使用,不赋值给其他变量的情况下不会出现有什么不同。老师说的话,估计就是看准了C语言控制单片机的相当一部分程序只有循环才会用到自增自减,才会这么说。

不同出现在赋值语句上,前置和后置在汇编层的顺序不同,如下:

int a = 0, c = 0;
000F179E mov dword ptr [a],0
000F17A5 mov dword ptr [c],0
c = a++;
000F17AC mov eax,dword ptr [a]
000F17AF mov dword ptr [c],eax
000F17B2 mov ecx,dword ptr [a]
000F17B5 add ecx,1
000F17B8 mov dword ptr [a],ecx
c = ++a;
000F17BB mov eax,dword ptr [a]
000F17BE add eax,1
000F17C1 mov dword ptr [a],eax
000F17C4 mov ecx,dword ptr [a]
000F17C7 mov dword ptr [c],ecx

可仍然能看到,不管是前置语句,还是后置语句,在汇编层语句的数量上是一样的,那么占用的空间和时间相同。但在网上经常搜到的前置语句和后置语句的效率问题又是什么导致的呢?
效率问题一般出在C++上,我去翻了C++ Primer后,发现是运算符重载的问题。书上的示例代码和网上找到的并不一样。网上对效率问题的解释是:后置++需要有临时对象才可以完成重载。

在第五版C++ Primer的502页(英文版566页)上则是:

class StrBlobPtr
{
public:
strBlobPtr& operator++();
};

StrBlobPtr& StrBlobPtr::operator++()
{
check(curr,”increment past end of StrBlobPtr”);
++curr;
return *this;
}

class StrBlobPtr
{
public:
strBlobPtr& operator++(int);
};

StrBlobPtr& StrBlobPtr::operator++()
{
StrBlobPtr ret = *this;
++ *this;
return ret;
}

而且书上强调了“定义自增自减的运算符的类,需要同时定义前置和后置两个版本。”所以效率问题应该是仅对比两者的定义。

前置中多了函数检验,但从传递和值的返回上来看,后置返回的形式是一个值而非引用。而且因为要解决普通重载形式无法区分两种情况(因为使用的是同一个符号),所以后置版本需要额外接受一个int形参(不过,这个仅仅是为了区分,并非真的参与运算)。这两个重载在汇编层面的语句就切切实实的分开了,于是后置在某些场景中的使用速度比前置要低。

时间: 2024-08-24 02:56:30

重读TCPL:从汇编语句浅析++i和i++的相关文章

在c语言中嵌入汇编语句,对于我来说相当难。

今天早上在csdn论坛上看到一个帖子http://topic.csdn.net/u/20120917/14/82f42e17-977a-4824-95bd-7b79db15d283.html:“C语言中嵌入汇编,究竟有何意义?” 其中看到一个例子是在c语言中插入一段汇编代码获取CPU的主频,制造商和型号的: //=====================================================================================/*      

1.1.3-构造汇编语句

最近打算把<老码识途>搞一遍,就先拿这个构造汇编语句开刀吧. 1.1.3 在c语言里嵌入汇编语句,构造mov和jmp的机器指令,从而让程序按自己指定的路径跑. 见代码: 1 #include <stdio.h> 2 #include <string.h> 3 #include <stdlib.h> 4 5 int gi; 6 void *address; 7 8 void *buildCode(); 9 10 int main(){ 11 void *cod

18.10 汇编语句

18.10.1 汇编语句中.equ的作用 1.定义静态符号 虽然数据段主要用于定义变量数据,但是也可以在这里声明静态数据符号. .equ命令用于把常量值设置为可以在文本段中使用的符号. 如: 01 .equ MEM_CTL_BASE, 0x48000000 02 .equ SDRAM_BASE, 0x30000000 经过设置之后,数据符号值是不能在程序中改动的. .equ命令可以出现在数据段中任何位置,但是好代码习惯,最好在定义其他数据之前或之后集中定义所有数据符号. 如: 01 ldr r2

&lt;&lt;ASM&gt;&gt; x86汇编指令浅析

学习研究系统/软件底层机制的朋友,汇编是必修课之一.由于汇编具有低级语言固有的特性,使得前期的学习掌握异常困难.本文将着重介绍一些常用而又关键的汇编指令,借此提携那些还在苦苦攀援的“初学者”们. 目前市场上主流的汇编教材中,王爽所著的<汇编语言(第二版)>比较受读者的青睐.笔者自学汇编所选的汇编教材则是杨继文所著的<80x86汇编语言程序设计教程>和<汇编语言程序设计——从DOS到Windows>张雪兰/谭毓安:根据书的厚度就可知其难易程度.读者若初次接触汇编,可以考虑

Select For update语句浅析 (转)

Select … for update语句是我们经常使用手工加锁语句.通常情况下,select语句是不会对数据加锁,妨碍影响其他的DML和DDL操作.同时,在多版本一致读机制的支持下,select语句也不会被其他类型语句所阻碍. 借助for update子句,我们可以在应用程序的层面手工实现数据加锁保护操作.本篇我们就来介绍一下这个子句的用法和功能. 下面是采自Oracle官方文档<SQL Language Reference>中关于for update子句的说明:(请双击点开图片查看) 从f

GCC在C语言中内嵌汇编 asm __volatile__ 【转】

转自:http://blog.csdn.net/pbymw8iwm/article/details/8227839 在内嵌汇编中,可以将C语言表达式指定为汇编指令的操作数,而且不用去管如何将C语言表达式的值读入哪个寄存器,以及如何将计算结果写回C 变量,你只要告诉程序中C语言表达式与汇编指令操作数之间的对应关系即可, GCC会自动插入代码完成必要的操作. 1.简单的内嵌汇编 例: __asm__ __volatile__("hlt"); "__asm__"表示后面的

GCC内嵌AT&amp;T汇编语法

一 基本语法 1 寄存器引用 引用寄存器要在寄存器号前加百分号%,如"movl %eax, %ebx". 80386有如下寄存器: 1.8个32-bit寄存器 %eax,%ebx,%ecx,%edx,%edi,%esi,%ebp,%esp: 2.8个16-bit寄存器,它们事实上是上面8个32-bit寄存器的低16位:%ax,%bx,%cx,%dx,%di,%si,%bp,%sp: 3.8个8-bit寄存器:%ah,%al,%bh,%bl,%ch,%cl,%dh,%dl.它们事实上是寄

C程序编译过程浅析【转】

转自:http://blog.csdn.net/koudaidai/article/details/8092647 前几天看了<程序员的自我修养——链接.装载与库>中的第二章“编译和链接”,主要根据其中的内容简单总结一下C程序编译的过程吧. 我现在一般都是用gcc,所以自然以GCC编译hellworld为例,简单总结如下. hello.c源代码如下: ?[Copy to clipboard] C 1 2 3 4 5 6 [c] view plaincopy <span style=&qu

GDB调试汇编堆栈

GDB调试汇编堆栈 准备工作 终端编译工具: 编译64位Linux版本32位的二进制文件,需要安装一个库,使用指令sudo apt-get install libc6-dev-i386 测试代码: test.c 分析过程 1.生成汇编代码:gcc -g gdbtest.c -o gdbtest -m32 2.调试:gdb test 3.设置断点,因为目的是分析而不是调试bug,所以我们将断点设置在main函数 4.开始gdb调试:r(un),如若想获取此时的汇编代码,可用指令:disassemb