由一篇帖子引起,自己写了点东西,望各位不吝指点。
昨天在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;}
var_8= dword ptr -8var_4= dword ptr -4;定义两个变量5和4mov [ebp+var_4], 5mov [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 -4arg_0= dword ptr 8arg_4= dword ptr 0Chpush ebp ;保存ebpmov ebp, esp ;将esp赋值给ebpsub esp, 4 ;栈顶指针减4,开辟一个空间,就是上述代码中的tmp的空间mov eax, [ebp+arg_0] ;将5的地址赋值给eaxmov eax, [eax] ;将eax指向的内容赋值给eax,取*a值的过程mov [ebp+var_4], eax ;将*a的值赋值给新开辟的空间,即tmpmov edx, [ebp+arg_0] ;用edx将4的地址保存mov eax, [ebp+arg_4] ;将4的地址赋值给eaxmov eax, [eax] ;将eax指向的内容赋值给eax,取*b值的过程mov [edx], eax ;将eax赋值给edx指向的内容,就*b = *a的过程mov edx, [ebp+arg_4] ;将4赋值给edxmov eax, [ebp+var_4] ;将tmp赋值给eaxmov [edx], eax ;将eax赋值给edx指向的空间,即*a = tmp的过程leaveretnsub_401390 endp
;同上mov [ebp+var_4], 5mov [ebp+var_8], 4;同上lea eax, [ebp+var_8]mov [esp+4], eaxlea eax, [ebp+var_4]mov [esp], eaxcall sub_401390sub_401390 proc nearvar_10= dword ptr -10harg_0= dword ptr 8arg_4= dword ptr 0Chpush ebp ;保存ebpmov ebp, esp ;将esp赋值给ebp;下面保存三个寄存器保存,以便能下面使用这三个寄存器push edi push esipush ebxsub esp, 4 ;开辟一个空间mov edi, [ebp+arg_0] ;取值5赋值给edimov eax, [ebp+arg_0] ;取值5赋值给eaxmov [ebp+var_10], eax ;将eax赋值给ebp-10的地方mov ebx, [ebp+arg_4] ;将4地址赋值给ebxmov esi, [ebp+arg_4] ;将4地址赋值给esimov ecx, [ebp+arg_0] ;将5地址赋值给ecxmov edx, [ebp+arg_0] ;将5地址赋值给edxmov eax, [ebp+arg_4] ;将4地址赋值给eaxmov eax, [eax] ;将eax指向的内容赋值给eax,就是eax = 4xor eax, [edx] ;将edx指向的内容与eax进行按为异或,就是4^5的过程,结果eax=(100)^(101)=001 mov [ecx], eax ;将eax赋值给ecx指向的空间,此时ecx = 1mov eax, [ecx] ;将ecx指向的空间赋值给eaxxor eax, [esi] ;将esi指向的空间和eax进行异或,即4^1=(100)^(001)=101=5;mov [ebx], eax ;将eax赋值给[ebx],就是4的地址mov eax, [ebx] ;将[ebx]赋值给eaxmov edx, [ebp+var_10] ;将最开始的eax赋值给edx,即5xor eax, [edx] ;5与eax进行异或,即5^1=(101)^(001)=(100)=4mov [edi], eax ;保存到[edi]中,edi最开始是5的地址,这样就保证了交换add esp, 4 ;释放申请的空间;下面是恢复ebx,esi,edi,ebp,清除现场pop ebx pop esipop edipop ebpretnsub_401390 endp
xor eax,ebxxor ebx,eaxxor eax,ebx
[解决办法]
// 2B青年
void swap(int* a, int* b){*a^=*b^=*a^=*b;}
这种严重依赖于编译器的优化
写这种语法糖的代码是不可取的
[解决办法]
a和b是指针,如果a=b,或者a和b指向的int有重叠都不行
[解决办法]
#include <stdio.h>#define SWAP(a,b) do ((&(a))!=(&(b)))?((a)^=(b)^=(a)^=(b)):((a)=(a)); while (0)char *p1="1" ,*p2="2" ;char c1=1 , c2=2 ;short s1=1 , s2=2 ;int i1=1 , i2=2 ;__int64 I1=1i64, I2=2i64;float f1=1.0f, f2=2.0f;double d1=1.0 , d2=2.0 ;void main() { SWAP((int)p1,(int)p2); printf("char * %5s, %5s\n",p1,p2); SWAP(c1,c2); printf("char %5d, %5d\n",c1,c2); SWAP(s1,s2); printf("short %5d, %5d\n",s1,s2); SWAP(i1,i2); printf("int %5d, %5d\n",i1,i2); SWAP(I1,I2); printf("__int64 %5I64d,%5I64d\n",I1,I2); SWAP(*(int *)&f1,*(int *)&f2);printf("float %5g, %5g\n",f1,f2); SWAP(*(__int64 *)&d1,*(__int64 *)&d2);printf("double %5lg, %5lg\n",d1,d2); SWAP(c1,c1); printf("%d\n",c1);}//char * 2, 1//char 2, 1//short 2, 1//int 2, 1//__int64 2, 1//float 2, 1//double 2, 1//2
[解决办法]
void swap(int *a, int *b){ __asm { mov eax, [ebp+08h] push dword ptr [eax] mov ecx, [ebp+0ch] push dword ptr [ecx] pop dword ptr [eax] pop dword ptr [ecx] }}
[解决办法]
release 版本, 2B 青年的写法, 还是很不错的。
intel parallel studio
#include <stdio.h>int i1=1 , i2=2 ;// 2B青年void swap(int* a, int* b){*a^=*b^=*a^=*b;} void main() { printf("%d\n",i1); printf("%d\n",i2); swap(&i1,&i2); printf("%d\n",i1); printf("%d\n",i2);}}
[解决办法]
函数的结构还是被优化得一塌糊涂
这样的看法应该是合理的
算法注重点应该是渐进性态,比如把O(n*n)改成O(logn),现在的编译器都是很可以的,所以c语言编程在速度上不应该在swap2b,swapwy这样O(1)上计较,计较也没用,c代码完全是抽象,和生成的机器码会差很远,只是最终效果一样
对于void swap2B(int* a, int* b){*a^=*b^=*a^=*b;}
编译器完全可以预测输入的a,b是否重叠,如果可预测不重叠,就等价于交换,如果预测a=b,那么都成0,否则产生另一套保守的代码
真要速度,用汇编去