二 C语言的流程与处理

1) Debug与Release的区别:前者称调试版,后者称发行版。调试版基本不优化,而发行版会经过编译器的极致优化,往往与优化前的高级语言执行流程会大相径庭,但是实现的功能是等价的。


2) 如下for循环语句:

 

int MyFunction(int a,int b)
{
    int c = a + b;
    int i;
    for ( i = 0 ; i < 50 ; i ++ )
    {
           c = c + i;
    }
    return c;
}

Debug版汇编后代码为:

 

int MyFunction(inta,int b)
{
	//省略现场保护代码
	int c = a + b;
0040D4B8	mov	eax,dword ptr [ebp+8]		;取参数1
0040D4BB	add	eax,dword ptr [ebp+0Ch]		;取参数2
0040D4BE	mov	dword ptr [ebp-4],eax		;存放局部变量c
	int i;
	for ( i = 0 ; i < 50 ; i ++ )
0040D4C1	mov	dword ptr [ebp-8],0		;局部变量i
0040D4C8	jmp	MyFunction+33h (0040d4d3)	;跳至第一次循环
0040D4CA	mov	ecx,dword ptr [ebp-8]		;改变循环变量
0040D4CD	add	ecx,1				;i自增操作
0040D4D0	mov	dword ptr [ebp-8],ecx		;保存
0040D4D3	cmp	dword ptr [ebp-8],32h		;循环条件比较
0040D4D7	jge	MyFunction+44h (0040d4e4)	;大于等于则跳
	{
		c = c + i;
0040D4D9	mov	edx,dword ptr [ebp-4]		;读变量c到edx
0040D4DC	add	edx,dword ptr [ebp-8]		;加上i
0040D4DF	mov	dword ptr [ebp-4],edx		;保存
	}
0040D4E2	jmp	MyFunction+2Ah(0040d4ca)	;继续循环
	return c;
0040D4E4	mov	eax,dwordptr [ebp-4]		;取结果到eax
}
//省略现场恢复代码

大体结构为:

	MOV	<循环变量>	<初始值>	;给循环变量赋初值
	JMP		B			;跳至第一次循环
A:	(改动循环变量)			;修改循环变量
	…
B:	CMP	<循环变量>	<限制变量>	;检查循环条件
	JGE		跳出循环
	(循环体)
	…
	JMP		A			;跳回去继续修改循环变量

3) 上述的for循环改为以下do循环:

int i = 0;
do{
    c = c + i;
}while( ++i <50 );

Debug版本汇编:

int i = 0;
0040D4C1	mov	dword ptr [ebp-8],0		;循环变量i初始化
do{
	c = c + i;
0040D4C8	mov	ecx,dword ptr [ebp-4]
0040D4CB	add	ecx,dword ptr [ebp-8]
0040D4CE	mov	dword ptr [ebp-4],ecx		;相加并保存
}while( ++i<= 50 );
0040D4D1	mov	edx,dword ptr [ebp-8]
0040D4D4	add	edx,1
0040D4D7	mov	dword ptr [ebp-8],edx		;改变循环变量
0040D4DA	cmp	dword ptr [ebp-8],32h		;比较循环条件
0040D4DE	jl	MyFunction+28h (0040d4c8)	;小于则跳,继续循环


4) 上述的do循环改为以下while循环:

int i = 0;
while( i++ < 50)
{
     c= c + i;
}

Debug版本汇编:

int i = 0;
0040D4C1	mov	dword ptr [ebp-8],0		;循环变量i初始化
while( i++ < 50)
0040D4C8	mov	ecx,dword ptr [ebp-8]		;自增前的循环变量
0040D4CB	mov	edx,dword ptr [ebp-8]
0040D4CE	add	edx,1
0040D4D1	mov	dword ptr [ebp-8],edx		;循环变量自增并保存
0040D4D4	cmp	ecx,32h				;比较(自增前的)
0040D4D7	jge	MyFunction+44h (0040d4e4)	;不小于则跳转,退出循环
{
	c = c + i;
0040D4D9	mov	eax,dword ptr [ebp-4]
0040D4DC	add	eax,dword ptr [ebp-8]
0040D4DF	mov	dword ptr [ebp-4],eax		;相加并保存
}
0040D4E2	jmp	MyFunction+28h (0040d4c8)	;绝对跳转,集训循环


5) 如下if-else判断分支:

int i = 10,j;
if ( i <= 0 )
{
         j= 1;
}
else if ( i > 0 && i <= 10)
{
         j= 2;
}
else
{
         j= 3;
}


Debug版本汇编:

int i = 10,j;
0040D4B8	mov	dword ptr [ebp-4],0Ah		;仅声明的变量不分配内存
if ( i <= 0 )
0040D4BF	cmp	dword ptr [ebp-4],0		;比较
0040D4C3	jg	MyFunction+2Eh (0040d4ce)	;大于则跳下一分支
{
	j = 1;
0040D4C5	mov	dword ptr [ebp-8],1		;赋值
}
else if ( i > 0 && i <= 10)
0040D4CC	jmp	MyFunction+4Ah (0040d4ea)	;结束整个分支
0040D4CE	cmp	dword ptr [ebp-4],0		;else-if分支开始
0040D4D2	jle	MyFunction+43h (0040d4e3)	;不大于0则转入else
0040D4D4	cmp	dword ptr [ebp-4],0Ah		;判断第二个条件
0040D4D8	jg	MyFunction+43h (0040d4e3)	;大于则转入else
{
	j = 2;
0040D4DA	mov	dword ptr [ebp-8],2		;赋值
}
else
0040D4E1	jmp	MyFunction+4Ah (0040d4ea)	;结束整个分支
{
	j = 3;
0040D4E3	mov	dword ptr [ebp-8],3		;赋值
}
0040D4EA	…


If-else语句使用CMP加绝对跳转指令实现(跳转到下一分支或者整个分支的结束位置),从上可知要排列好比较条件的顺序,以达到最少的比较次数的效果。

 

6) 如下switch-case判断分支:

int i = 10,j;
switch( i ){
case 0:
         j= 0;
case 1:
         j= 1;
         break;
default:
         j= 3;
}


Debug版本汇编:

int i = 10,j;
0040D4B8	mov	dword ptr [ebp-4],0Ah		;赋初值
switch( i )
{
0040D4BF	mov	eax,dword ptr [ebp-4]       
0040D4C2	mov	dword ptr [ebp-0Ch],eax		;转移内存单元
0040D4C5	cmp	dword ptr [ebp-0Ch],0		;与0比较
0040D4C9	je	MyFunction+33h (0040d4d3)	;等于则转
0040D4CB	cmp	dword ptr [ebp-0Ch],1		;与1比较
0040D4CF	je	MyFunction+3Ah (0040d4da)
0040D4D1	jmp	MyFunction+43h (0040d4e3)	;绝对跳转到default
case 0:
	j = 0;
0040D4D3	mov	dword ptr [ebp-8],0		;无break转入下一分支
case 1:
	j = 1;
0040D4DA	mov	dword ptr [ebp-8],1
break;
0040D4E1	jmp	MyFunction+4Ah (0040d4ea)	;break,绝对跳转到结束
default:
	j = 3;
0040D4E3	mov	dword ptr [ebp-8],3
}
0040D4EA	…


期间对i进行转储是Debug版本的特点,目的并不明确。每一个case对应一个cmp与跳转指令je,最后的default要是没有,则跳转到结束处。Case中有break则跳转到结束,没有则继续往下执行。

 

7) 如下C语言结构体和数组:

typedef struct {
	float a;
	char b;
	int c;
}mystruct;
 
int MyFunction(void)
{
	unsigned char *buf[100];
	mystruct* strs = (mystruct *)buf;
	int i;
	for(i = 0 ; i < 5 ; i++)
	{
		strs[i].a= 1.2;
		strs[i].b= 'a';
		strs[i].c= 0;
	}
	return 0;
}


Debug版本汇编MyFunction函数如下:

int MyFunction(void)
{
00401020	push	ebp
00401021	mov	ebp,esp
00401023	sub	esp,1D8h			;1D8H = 472字节   
00401029	push	ebx
0040102A	push	esi
0040102B	push	edi
0040102C	lea	edi,[ebp-1D8h]
00401032	mov	ecx,76h
00401037	mov	eax,0CCCCCCCCh
0040103C	rep stos	dword ptr [edi]		;标准现场保护
	unsigned    char *buf[100];
tystruct *strs =(mystruct *)buf;
0040103E	lea	eax,[ebp-190h]         
00401044	mov	dword ptr [ebp-194h],eax	;将ebp – 190h送入ebp – 194h
							;ebp– 190h为缓存区地址首址(char)
							;ebp– 194h为str指针(tystruct *strs)
	int i;
	for (i = 0 ; i< 5 ; i++)
0040104A	mov	dword ptr [ebp-198h],0
00401054	jmp 	MyFunction+45h (00401065)
00401056	mov	ecx,dword ptr [ebp-198h]
0040105C	add	ecx,1
0040105F	mov	dword ptr [ebp-198h],ecx
00401065	cmp	dword ptr [ebp-198h],5
0040106C	jge	MyFunction+91h (004010b1)	;标准for循环结构
	{
		strs[i].a = 1.2;
0040106E	mov	edx,dword ptr [ebp-198h] 	;ebp – 198h为循环变量i
00401074	imul	edx,edx,0Ch              	;结构体长度为常量0CH,得偏移
00401077	mov	eax,dword ptr [ebp-194h] 	;取str中的指针值
0040107D	mov	dword ptr [eax+edx],3F99999Ah
							;得strs[i].a地址,并赋值
		strs[i].b = 'a';
00401084	mov	ecx,dword ptr [ebp-198h]
0040108A	imul	ecx,ecx,0Ch
0040108D	mov	edx,dword ptr [ebp-194h]
00401093	mov 	byte ptr [edx+ecx+4],61h	;相对与之上多偏移4个字节
		strs[i].c = 0;
00401098	mov	eax,dword ptr [ebp-198h]
0040109E	imul	eax,eax,0Ch
004010A1	mov	ecx,dword ptr [ebp-194h]
004010A7	mov	dword ptr [ecx+eax+8],0		;相对与之上,再偏移4个字节
	}
004010AF	jmp	MyFunction+36h (00401056)
	return 0;
004010B1	xor	eax,eax
}
//省略现场恢复


第一点:系统预分配的临时变量堆栈区可变且足够(暂时不知道怎么计算的);第二点:Struct的大小编译以后作为常数保存,对结构体中的变量的访问(点运算符)汇编后采用的偏移量的形式;第三点:偏移量的大小为结构体定义时各个成员变量于系统字长对齐后大小的叠加(mystruct第二个成员为char类型,理论上来说只占用一个字节,但是与4字节对齐,实际占用4个字节)

 

8) 如下C语言共用体和枚举类型:

 

typedef enum {
         ENUM_1= 1,
         ENUM_2= 2,
         ENUM_3,
         ENUM_4,
}myenum;
 
typedef  struct{
         inta;
         intb;
         intc;
}mystruct;
 
typedef union{
         mystructs;
         myenume[3];
}myunion;
 
int MyFunction(void)
{
         unsigned char buf[100] = { 0 };
         myunion* uns = (myunion *)buf;
         int i;
         for(i = 0 ; i < 5 ; i++)
         {
                   uns[i].s.a= 0;
                   uns[i].s.b= 1;
                   uns[i].e[2]= ENUM_4;
         }
         return0;
}


Debug版本汇编MyFunction函数如下:

int MyFunction(void)
{
	//现场保护代码省略
	unsigned charbuf[100] = { 0 };
0040103E	mov	byte ptr [ebp-64h],0		;ebp – 64的1个字节置0
00401042	mov	ecx,18h				;循环18H(24)次
00401047	xor	eax,eax				;eax置0
00401049	lea	edi,[ebp-63h]			;从ebp – 63位置开始清
0040104C	rep stos	dword ptr [edi]		;内存清0(24*4=96字节)
0040104E	stos	word ptr [edi]			;继续清2个字节
00401050	stos	byte ptr [edi]			;继续清1个字节
							;合计100字节
	myunion *uns =(myunion *)buf;
00401051	lea	eax,[ebp-64h]
00401054	mov	dword ptr [ebp-68h],eax		;ebp-68h为myunion *uns指针变量
	int i;
	for (i = 0 ; i< 5 ; i++)
00401057	mov	dword ptr [ebp-6Ch],0		; ebp-6Ch为局部变量i
0040105E	jmp	MyFunction+49h (00401069)
00401060	mov	ecx,dword ptr [ebp-6Ch]
00401063	add	ecx,1
00401066	mov	dword ptr [ebp-6Ch],ecx
00401069	cmp	dword ptr [ebp-6Ch],5
0040106D	jge	MyFunction+83h (004010a3)	;标准for循环
	{
		uns[i].s.a = 0;
0040106F	mov	edx,dword ptr [ebp-6Ch]
00401072	imul	edx,edx,0Ch			;0CH为共用体大小
00401075	mov	eax,dword ptr [ebp-68h]
00401078	mov	dword ptr [eax+edx],0		;偏移,赋值
		uns[i].s.b = 1;
0040107F        mov	ecx,dword ptr [ebp-6Ch]
00401082	imul	ecx,ecx,0Ch
00401085	mov	edx,dword ptr [ebp-68h]
00401088	mov	dword ptr [edx+ecx+4],1		;相对上面多偏移4字节
		uns[i].e[2]= ENUM_4;
00401090	mov	eax,dword ptr [ebp-6Ch]
00401093	imul	eax,eax,0Ch
00401096	mov	ecx,dword ptr [ebp-68h]
00401099	mov	dword ptr [ecx+eax+8],4		;相对上面再偏移4字节
	}
004010A1	jmp	MyFunction+40h (00401060)
	return 0;
004010A3	xor	eax,eax
}
//省略现场恢复代码


第一:从100字节缓存区的初始化可知,Debug版本的代码没经过优化,很stupid;第二:这两种类型的汇编访问方式同数组与结构体如出一辙,并没有什么不同。

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值