由一篇帖子引起,自己写了点东西,望各位不吝指点。

sjjwind 2012-05-02 11:57:21
昨天在CSDN上看到这样一个帖子

// 普通青年
std::swap(a, b);

// 文艺青年
void swap(int* a, int* b)
{
int t = *a;
*a = *b;
*b = t;
}

// 2B青年
void swap(int* a, int* b){*a^=*b^=*a^=*b;}



首先庆幸一下自己属于文艺青年的范畴,嘿嘿
然后把这个给别人分享了一下,别人告诉我,第二种方法比第三种方法快,我第一反应就是,怎么可能,第三种用的是位

运算,应该会比普通的方法快。
我不相信,自己测试了一下,果然第二种方法比第三种方法快,时间差不多有个4:5的关系,然后我就很好奇,逆了一下

这两段代码,得出了下面的一个结论
下面的代码是第二个的汇编代码,我详细注释了一下


var_8= dword ptr -8
var_4= dword ptr -4

;定义两个变量5和4
mov [ebp+var_4], 5
mov [ebp+var_8], 4

;向函数传入两个地址
lea eax, [ebp+var_8] ;取4的偏移地址
mov [esp+4], eax ;将这个地址进栈
lea eax, [ebp+var_4] ;取5的偏移地址
mov [esp], eax ;将这个地址进栈

call sub_401390 ;调用普通方式的swap的函数

sub_401390 proc near ;实现过程

var_4= dword ptr -4
arg_0= dword ptr 8
arg_4= dword ptr 0Ch

push ebp ;保存ebp
mov ebp, esp ;将esp赋值给ebp
sub esp, 4 ;栈顶指针减4,开辟一个空间,就是上述代码中的tmp的空间
mov eax, [ebp+arg_0] ;将5的地址赋值给eax
mov eax, [eax] ;将eax指向的内容赋值给eax,取*a值的过程
mov [ebp+var_4], eax ;将*a的值赋值给新开辟的空间,即tmp
mov edx, [ebp+arg_0] ;用edx将4的地址保存
mov eax, [ebp+arg_4] ;将4的地址赋值给eax
mov eax, [eax] ;将eax指向的内容赋值给eax,取*b值的过程
mov [edx], eax ;将eax赋值给edx指向的内容,就*b = *a的过程
mov edx, [ebp+arg_4] ;将4赋值给edx
mov eax, [ebp+var_4] ;将tmp赋值给eax
mov [edx], eax ;将eax赋值给edx指向的空间,即*a = tmp的过程
leave
retn
sub_401390 endp


下面的是第三个函数的汇编代码

;同上
mov [ebp+var_4], 5
mov [ebp+var_8], 4

;同上
lea eax, [ebp+var_8]
mov [esp+4], eax
lea eax, [ebp+var_4]
mov [esp], eax
call sub_401390

sub_401390 proc near


var_10= dword ptr -10h
arg_0= dword ptr 8
arg_4= dword ptr 0Ch

push ebp ;保存ebp
mov ebp, esp ;将esp赋值给ebp

;下面保存三个寄存器保存,以便能下面使用这三个寄存器
push edi
push esi
push ebx

sub esp, 4 ;开辟一个空间
mov edi, [ebp+arg_0] ;取值5赋值给edi
mov eax, [ebp+arg_0] ;取值5赋值给eax
mov [ebp+var_10], eax ;将eax赋值给ebp-10的地方
mov ebx, [ebp+arg_4] ;将4地址赋值给ebx
mov esi, [ebp+arg_4] ;将4地址赋值给esi
mov ecx, [ebp+arg_0] ;将5地址赋值给ecx
mov edx, [ebp+arg_0] ;将5地址赋值给edx
mov eax, [ebp+arg_4] ;将4地址赋值给eax
mov eax, [eax] ;将eax指向的内容赋值给eax,就是eax = 4
xor eax, [edx] ;将edx指向的内容与eax进行按为异或,就是4^5的过程,结果eax=(100)^(101)=001
mov [ecx], eax ;将eax赋值给ecx指向的空间,此时ecx = 1
mov eax, [ecx] ;将ecx指向的空间赋值给eax
xor eax, [esi] ;将esi指向的空间和eax进行异或,即4^1=(100)^(001)=101=5;
mov [ebx], eax ;将eax赋值给[ebx],就是4的地址
mov eax, [ebx] ;将[ebx]赋值给eax
mov edx, [ebp+var_10] ;将最开始的eax赋值给edx,即5
xor eax, [edx] ;5与eax进行异或,即5^1=(101)^(001)=(100)=4
mov [edi], eax ;保存到[edi]中,edi最开始是5的地址,这样就保证了交换
add esp, 4 ;释放申请的空间
;下面是恢复ebx,esi,edi,ebp,清除现场
pop ebx
pop esi
pop edi
pop ebp

retn
sub_401390 endp


明显可以看到第二个比第三个的汇编代码少,差不多有4:5的比例,说一下第三段代码,虽然是用位运算做的,可是中间

还有很多的细节,而且同样申请空间去保存中间变量了,其中用的寄存器也比第二个多,时间基本上花费在保存中间变量

上了,相比第二个就不太好了,关于第一段代码,我就不解释了。
想起了赵老师经常说的一句话,
眼过千遍不如手过一遍!
书看千行不如手敲一行!
手敲千行不如单步一行!
单步源代码千行不如单步对应汇编一行!
想想非常有道理,实践是检验真理的唯一标准啊。
学汇编不久,肯定有不妥的地方,写的不好或不对的地方望各位大牛指点,非常感谢
...全文
155 16 打赏 收藏 转发到动态 举报
写回复
用AI写文章
16 条回复
切换为时间正序
请发表友善的回复…
发表回复
qq45000 2012-05-02
  • 打赏
  • 举报
回复
汇编表示很头痛。
dic_008 2012-05-02
  • 打赏
  • 举报
回复
汇编没学好的人
muyi66 2012-05-02
  • 打赏
  • 举报
回复
[Quote=引用 13 楼 的回复:]

怎么会是生搬了汇编里面的知识,求解释
[/Quote]汇编代码写在另一个帖子里了,你过去看看吧。
sjjwind 2012-05-02
  • 打赏
  • 举报
回复
怎么会是生搬了汇编里面的知识,求解释
sjjwind 2012-05-02
  • 打赏
  • 举报
回复
一篇帖子不知怎么变成两篇帖子,有点晕。。。
evencoming 2012-05-02
  • 打赏
  • 举报
回复
千万别用第三种,慎重,慎重.
如果编译器把第二种和第三种优化的一样,那么,这个编译器是不可靠的.
也可以扔掉
囧囧囧1024 2012-05-02
  • 打赏
  • 举报
回复
文艺青年~~~~~~~~
bird57521 2012-05-02
  • 打赏
  • 举报
回复
汇编代码怎样生成的? 如何单步汇编代码
muyi66 2012-05-02
  • 打赏
  • 举报
回复
我的5楼又没了......

所谓2B青年的写法,就是生搬了汇编语言里的技巧。
sjjwind 2012-05-02
  • 打赏
  • 举报
回复
谢谢,我编译优化了再试试
zc200711158 2012-05-02
  • 打赏
  • 举报
回复
在学习c++,希望早些能看懂——应该很有趣&成就感
muyi66 2012-05-02
  • 打赏
  • 举报
回复
所谓2B青年的写法,其实就是生搬硬套了汇编语言里的技巧。
Lactoferrin 2012-05-02
  • 打赏
  • 举报
回复
那就没用,发布出去的都是开优化的,调试版不是追求速度的地方
sjjwind 2012-05-02
  • 打赏
  • 举报
回复
[Quote=引用 2 楼 的回复:]
开优化没
[/Quote]
没有
Lactoferrin 2012-05-02
  • 打赏
  • 举报
回复
开优化没
bearzyj2011 2012-05-02
  • 打赏
  • 举报
回复
写的挺好,加油:)

64,652

社区成员

发帖
与我相关
我的任务
社区描述
C++ 语言相关问题讨论,技术干货分享,前沿动态等
c++ 技术论坛(原bbs)
社区管理员
  • C++ 语言社区
  • encoderlee
  • paschen
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
  1. 请不要发布与C++技术无关的贴子
  2. 请不要发布与技术无关的招聘、广告的帖子
  3. 请尽可能的描述清楚你的问题,如果涉及到代码请尽可能的格式化一下

试试用AI创作助手写篇文章吧