C语言的秘密Word下载.docx
《C语言的秘密Word下载.docx》由会员分享,可在线阅读,更多相关《C语言的秘密Word下载.docx(16页珍藏版)》请在冰豆网上搜索。
SBYTE8位有符号整数
WORD16位无符号整数
SWORD16位有符号整数DDfloat(伪指令)
DWORD32位无符号整数DQdouble
SDWORD32位有符号整数DTlongdouble
FWORD48位整数
QWORD64位整数
inta0=1;
unsignedinta1=2;
inta2=3;
charb='
b'
;
floatc=1.0;
charb1[4]="
abc"
55:
inta0=1;
003BA1F0movdwordptr[a0],1//把1赋值给变量a0,a0在ASM中表示地址,用dwordptr取出对应的数,a0为int型,在ASM中就为dword
56:
unsignedinta1=2;
003BA1F7movdwordptr[a1],2//同上,这里的无符号整型在ASM中也是由dword类型
57:
inta2=3;
003BA1FEmovdwordptr[a2],3
58:
charb='
003BA205movbyteptr[b],62h//char类型在ASM中为byte
59:
floatc=1.0;
003BA209movssxmm0,dwordptrds:
[3BEBB0h]//浮点数在ASM中也是DWORD类型,浮点003BA211movssdwordptr[c],xmm0数在ASM中操作要比其他的要复杂一些
c语言中数据类型的定义是数据类型在变量前,变量名是可以是字母数字组合,可以单一是字母例如inta,a1;
相对于汇编语言里,声明数据类型的时候,是先写数据变量名,然后是数据类型,不仅可以初始赋值,还可以赋为空,例如valdword?
这里的?
就代表初始为一个空值。
c语言中的指针是个很好用的数据类型,但是也是非常容易出错的一个数据类型。
那么这种令人又爱又恨的指针在ASM中是怎么用的呢?
给出一个很简易的一个指针,在c++中的代码是这样的
int*b0=0;
//一个int指针
b0=&
a0;
cout<
<
*b0;
指针在汇编中的代码
62:
int*b0=0;
002E9B7Emovdwordptr[b0],0//b0作为指针,指向一个地址,初始赋值为0
63:
b0=&
002E9B85leaeax,[a0]//取出a0的地址,对应送入到eax中
002E9B88movdwordptr[b0],eax//将eax的值送给b0,达到将a0的值赋给b0指针对应的数据
64:
cout<
//指针对应数据的输出,用到输出函数
002E9B8Bmovesi,esp
002E9B8Dmoveax,dwordptr[b0]
002E9B90movecx,dwordptr[eax]
002E9B92pushecx
002E9B93movecx,dwordptrds:
[2F20B8h]
002E9B99calldwordptrds:
[2F2098h]
002E9B9Fcmpesi,esp
002E9BA1call__RTC_CheckEsp(02E137Ah)
三、c中的结构体、联合在汇编中的实现
结构体在很多语言里都有,比较是比较实用的一种类型;
c语言中结构体就是一个可以包含不同数据类型的一个结构,它是一种可以自己定义的数据类型。
结构体可以在一个结构中声明不同的数据类型,相同结构的结构体变量是可以相互赋值。
既然c中结构体有很多的优点,那么在ASM中是怎么样的呢?
typedefstructN//定义的一个结构体
{
inta_s;
//结构体中的两种数据类型
charb_s;
}N;
69:
n_sS;
70:
S.a_s=4;
002E9BA6movdwordptr[S],4//结构体在ASM中,结构体的数据就和一般数据类型一样操作了
71:
S.b_s='
S'
002E9BADmovbyteptr[ebp-78h],53h
ASM中的结构体的定义使用STRUC和ENDS伪指令定义,如果结构体的域有初始值,在定义结构体变量时这些初始值就成了结构体变量域的默认值。
结构中可以使用多种类型的初始值:
未定义:
使用“?
”表示域内容未定义。
字符串:
用引号包围的字符串。
整数:
整数常量和整数表达式
数组:
当域是一个数组时,可使用DUP操作符初始化数组元素
一个结构的例子:
ELESTRUCT
ABYTE"
000000000"
BBYTE30DUP(0)
CWORD
DWORD0
ELEENDS
.data
exampleELE<
2,2,>
声明结构变量identifierstructuretype<
initializer-list>
这里的example对应的结构变量中A=?
B=2C=2D=?
逗号作为了占位符。
在域成员的使用时直接使用“结构名.成员“即可例如:
movexample.A3就是把3赋值给了结构example的成员A。
联合是c语言中较为常用的一个数据类型,联合类似于结构体,在运用上和结构体很相似,功能也是很相似。
这里主要是ASM中的联合。
Union//定义一个简单的联合
{
inta_u;
charb_u;
}n_u;
49:
union//联合的定义在ASM中的不会改变
50:
{
51:
inta_u;
52:
charb_u;
53:
}n_u;
54:
72:
n_u.a_u=5;
//形如结构体,用联合与一般数据类型类似
001E9C11movdwordptr[n_u],5
73:
n_u.b_u='
U'
001E9C18movbyteptr[n_u],55h
联合在ASM中的声明
unionnameUNION
union-fields
unionnameENDS
联合嵌套结构则是:
StructurenameSTRUCT
Structure-fields
Unionunionname
Union-fields
ENDS
StructurenameENDS
宏在c语言中用处比较广泛,因此宏的定义就相当重要了。
宏定义是c提供的三种预处理功能的其中一种,这三种预处理包括宏定义、文件包含、条件编译。
宏定义又称为宏代换、宏替换。
例如:
宏定义的格式#define标识符字符串这里的标识符就是符号常量,就是“宏名”。
预处理(预编译)工作叫宏展开:
将宏名替换成为字符串。
宏在c语言中使用时应注意:
(1)宏名一般用大写
(2)使用宏可提高程序的通用性和易读性,减少不一致性,减少输入错误和便于修改。
数组大小常用宏定义
(3)预处理是在编译之前的处理,而编译工作的任务之一就是语法检查,预处理不做语法检查。
(4)宏定义末尾不加分号;
(5)宏定义写在函数的花括号外边,作用域为其后的程序,通常在文件的最开头。
(6)可以用#undef命令终止宏定义的作用域
(7)宏定义可以嵌套
(8)字符串"
"
中永远不包含宏
(9)宏定义不分配内存,变量定义分配内存。
带参数的宏定义:
(1)实参如果是表达式容易出问题
#defineS(r)r*r
area=S(a+b);
第一步换为area=r*r;
第二步被换为area=a+b*a+b;
正确的宏定义是#defineS(r)((r)*(r))
(2)宏名和参数的括号间不能有空格
(3)宏替换只作替换,不做计算,不做表达式求解
(4)函数调用在编译后程序运行时进行,并且分配内存。
宏替换在编译前进行,不分配内存
(5)宏的哑实结合不存在类型,也没有类型转换。
(6)函数只有一个返回值,利用宏则可以设法得到多个值
(7)宏展开使源程序变长,函数调用不会
(8)宏展开不占运行时间,只占编译时间,函数调用占运行时间(分配内存、保留现场、值传递、返回值
77:
cout<
Max(S.a_s,n_u.a_u);
//宏的调用
反汇编得到:
009E9D8Cmoveax,dwordptr[S]//ASM中的取出宏中的变量参数,实现宏的作用
009E9D92cmpeax,dwordptr[n_u]
009E9D95jlemain+235h(09E9DA5h)
009E9D97movecx,dwordptr[S]
009E9D9Dmovdwordptr[ebp-168h],ecx
009E9DA3jmpmain+23Eh(09E9DAEh)
009E9DA5movedx,dwordptr[n_u]
009E9DA8movdwordptr[ebp-168h],edx
009E9DAEmovesi,esp
009E9DB0moveax,dwordptr[ebp-168h]
009E9DB6pusheax
009E9DB7movecx,dwordptrds:
[9F20B8h]
009E9DBDcalldwordptrds:
[9F2098h]
009E9DC3cmpesi,esp
009E9DC5call__RTC_CheckEsp(09E137Ah)
ASM中的宏在使用之中是很方便的,首先宏可以在程序的头部定义,一般会在数据段之前,或者也可以放在单独的文本文件中。
在编译时调用宏都是把宏的代码插入到程序中。
mMACROpar-1,par-2
statement-list
ENDM
宏定义中参数,只是一个有名字的容器,以存放调用者传递给宏的文本参数。
宏参数实际上可以是整数、变量名和其他值,不过预处理器把各种参数统一视为文本进行处理。
宏的参数没有类型,因此预处理器并不检查实参的类型是否与形参的类型匹配。
宏的调用,比较简单,可以说简单的调用,宏的参数顺序必须与宏定义中的形式参数顺序相同,但是实际参数的数目不一定非要与宏定义中形式参数的个数完全一致。
对于传递的参数个数少于形式参数的个数,未传递的参数及为空。
四、常量变量表达式,逻辑运算底层实现
常量的比较:
c语言中常量的定义很简单,就是常量在程序中不能改变其值的量。
常量分为:
整形常量、实型常量、字符型常量、字符串常量、枚举常量
c语言中常量直接以以上的形式在代码中提现:
n_sS;
S.a_s=4;
//整型常量是4,十进制表示
S.b_s='
//字符型常量
n_u.a_u=20;
//20十进制表示
n_u.b_u='
而在ASM中,汇编是以16进制来存储这些常量的,对上述常量的反汇编是:
00EF9C06movdwordptr[S],4//这里的4已经不是十进制表示的4,而是16进制的
00EF9C10movbyteptr[ebp-84h],53h//对于字符串,存储的是其ASII码的16进制表示,S的ASII为83表示为16进制就是53h
n_u.a_u=20;
00EF9C17movdwordptr[n_u],14h//在这里就是很明显的了,20的16进制表示刚好就是14h
74:
00EF9C1Emovbyteptr[n_u],55h
变量及表达式的不同:
c++/c中的变量是随处可见的,也是最基本的程序语句。
c中变量类型的声明需要放在main函数内整个程序之前;
c++就没有如此严格的规定了,可以在需要使用这个变量的时候再在此定义使用即可。
在c/c++中,变量名只是告诉编译器某个类型的变量会被使用,不会为这个变量分配内存;
但是在汇编语言里,变量名就代表其所在的地址,编译器已经为其分配好了内存。
00EF9BBEmovdwordptr[a2],3//C语言中变量a2声明加上赋值,汇编语言中,dwordptr是数据类型,[a2]是取出地址a2对应的数据,所以a2就是代表内存地址,c语言中赋值语句,ASM中则是用mov指令来实现赋值
逻辑运算的底层实现:
首先了解一下在c/c++中的逻辑运算,c中的逻辑运算是:
与、或、非。
即为:
&
||!
这三种逻辑运算符号;
ASM中是用指令操作符来实现的,调用一些操作系统的中的一些函数。
boolanswer1,answer2,answer3,t1=true,t2=false;
answer1=t1&
t2;
//c语言中的与运算
answer2=t1||t2;
//c语言中的或运算
answer3=!
t1;
//c语言中的非运算
boolanswer1,answer2,answer3,t1=true,t2=false;
003DAA49movbyteptr[t1],1
003DAA50movbyteptr[t2],0
answer1=t1&
003DAA57movzxeax,byteptr[t1]//ASM中先检查t1是否为0,为0则与t2相与之后为0,直接跳转将0赋给answer1
003DAA5Etesteax,eax
003DAA60jemain+0E9h(03DAA79h)
003DAA62movzxecx,byteptr[t2]//对t2做类似的操作
003DAA69testecx,ecx
003DAA6Bjemain+0E9h(03DAA79h)
003DAA6Dmovdwordptr[ebp-1A4h],1
003DAA77jmpmain+0F3h(03DAA83h)
003DAA79movdwordptr[ebp-1A4h],0//判断得到相与的结果为0
003DAA83movdl,byteptr[ebp-1A4h]
003DAA89movbyteptr[answer1],dl
75:
answer2=t1||t2;
003DAA8Fmovzxeax,byteptr[t1]
003DAA96testeax,eax
003DAA98jnemain+121h(03DAAB1h)//ASM中检查t1是否为1,是1则t1与t2相或为1,跳转直接将answer2为1
003DAA9Amovzxecx,byteptr[t2]
003DAAA1testecx,ecx
003DAAA3jnemain+121h(03DAAB1h)
003DAAA5movdwordptr[ebp-1A4h],0
003DAAAFjmpmain+12Bh(03DAABBh)
003DAAB1movdwordptr[ebp-1A4h],1
003DAABBmovdl,byteptr[ebp-1A4h]
003DAAC1movbyteptr[answer2],dl
76:
answer3=!
003DAAC7movzxeax,byteptr[t1]
003DAACEtesteax,eax//检查t1与eax相与,为1则跳转将answer3赋值为0
003DAAD0jnemain+14Eh(03DAADEh)
003DAAD2movdwordptr[ebp-1A4h],1
003DAADCjmpmain+158h(03DAAE8h)
003DAADEmovdwordptr[ebp-1A4h],0
003DAAE8movcl,byteptr[ebp-1A4h]
003DAAEEmovbyteptr[answer3],cl
五、子程序的使用——c和ASM的异同
子程序的使用,在c/c++里就是我们熟知的各种函数,对于这些函数的使用,及注意事项这里就不用详细说了,主要描述一下子程序在ASM中的实现。
一个简易的c++的函数:
voidexample_f(inta)//函数的声明定义
a<
endl;
}
example_f(a1);
//函数在主程序中的调用
这些都是再简单不过的;
那看一下ASM中是怎么实现的。
84:
example_f(a1);
//这里仅仅是主程序中的调用
01309C74moveax,dwordptr[a1]//将变量放在寄存器eax里
01309C77pusheax//压栈
01309C78callexample_f(01301523h)//此时调用子程序example_f
01309C7Daddesp,4
47:
voidexample_f(inta)//通过内存地址,找到子程序的位置
48:
{//子程序的使用就是在堆栈里进行的
01304D60pushebp//主ebp进栈
01304D61movebp,esp//esp赋给ebp
01304D63subesp,0C0h//为子程序分配空间
01304D69pushebx//现场寄存器ebx压栈
01304D6Apushesi//现场寄存器esi压栈
01304D6Bpushedi//现场寄存器edi压栈
01304D6Cleaedi,[ebp-0C0h]
01304D72movecx,30h
01304D77moveax,0CCCCCCCCh
01304D7Crepstosdwordptres:
[edi]//将eax的值拷贝到edi
01304D7Emovesi,esp
01304D80push130143Dh
01304D85movedi,esp
01304D87moveax,dwordptr[a]//取出a的数据,赋给eax
01304D8Apusheax//将eax压栈
01304D8Bmovecx,dwordptrds:
[13120B8h]
01304D91calldwordptrds:
[1312098h]
01304D97cmpedi,esp
01304D99call__RTC_CheckEsp(0130137Ah)
01304D9Emovecx,eax
01304DA0calldwordptrds:
[1312094h]
01304DA6cmpesi,esp
01304DA8call__RTC_CheckEsp(0130137Ah)
01304DADpopedi//现场寄存器出栈
01304DAEpopesi
01304DAFpopebx
01304DB0addesp,0C0h//esp从参数地址返回到子程序的返回地址,释放内存空间
01304DB6cmpebp,esp
01304DB8call__RTC_CheckEsp(0130137Ah)
01304DBDmovesp,ebp
01304DBFpopebp
01304DC0ret//堆栈平衡
六、全局变量、局部变量(静态变量等)的汇编实现
全局变量在c/c++还是在ASM中都是很简单的:
C/c++在main函数中定义的就可以是全局变量,ASM只需要在数据段定义的变量也都是全局变量。
对于局部变量,在c语言中,一般就在函数中被定义和使用,当函数调用结束后,其内存就会被情况,数据也就没有了,所以局部变量的作用范围仅仅在它所在的函数域内。
在ASM中的实现,则略有不同
voidexample_f(inta)
intloc;
//函数中定义一个局部变量loc
loc=1;
loc<