文章目录
前言
C语言结构复习一下。。。
返回值得类型 函数名(参数类型 参数名,参数类型 参数名)
{
//如果前面是void,不用return
//如果有:
return 11;
}
一、C是如何变成汇编的?
1.裸函数
void __declspec(naked) Function()
{
}
上述结构叫做裸函数
键入上行代码,F7,F5之后,直接F11,一直按F11,发现直接没了
因为这个裸函数一行代码都没有生成,编译器不管你
裸函数应用:
如何变成二进制
2.C变成汇编
在处理缓冲区后
push 2
push 1
通过【ebp+8】来找到第一个数这些就是函数参数,此时入栈
局部变量存储在缓冲区里
而返回值一般存储在EAX里面
在VC6里面输入如下
//保留调用函数前的堆栈
push ebp
mov ebp,esp
sub esp,0x40
//保存现场
push ebx
push esi
push edi
//填充缓冲区
mov eax,0xccc
mov ecx,0x40
lea edi,dword ptr ds:[ebp-0x40]
rep stosd
//函数功效
mov eax,dword ptr ds:[ebp+8]
add eax.dword ptr ds:[ebp+c]
//恢复现场
pop edi
pop esi
pop ebx
//恢复栈堆
mov esp,ebp
pop ebp
ret
发现可以执行,说明了编译器把C变成汇编
3.调用约定
cdecl
int _cdecl plus1(int x, int y)
{
return x + y;
}
谁调用函数,谁平衡堆栈
C和的C++默认的调用约定
stdcall
int _stdcall plus1(int x, int y)
{
return x + y;
}
这里没有平衡堆栈,但是在之前有
相当于ret后,esp+8,在函数里面平衡,叫内平栈
fastcall
寄存器读写速度快,所以这里是fast,比之前堆栈(内存)快
int _fastcall plus1(int x, int y)
{
return x + y;
}
用寄存器存的,不涉及堆栈,所以没有弄堆栈平衡
但是超过两个数仍然push
所以别整太多参数,传两个才能保证传输效率
最后平衡堆栈是是ret 8
二、函数入口
1.调用堆栈
函数真正的入口是call stack(调用堆栈窗口)里显示的mainCRTstartup
mainCRTstartup做了很多事情,都是控制台程序必须要用的,所以不能替换
void plus(){
}
int main(int argc,char* argv[])
{
plus();
return 0;
}
此处main函数的窗口如图
VC6的编译器变成三个参数,调用main方法的函数进行了获取命令行,获取版本信息
2.找main函数方法
看到getcommandLineA,往下找三个参数的
因为最后平衡堆栈是ESP+8,所以是三个参数,main方法
三.数据类型
1.基本类型
有整数类型和浮点类型
整数类型
char short int long
如图,为整数类型的宽度对应及其字节大小
如图,为汇编
如果超出了宽度,则取最后两个字节输入如下代码
void Plus1()
{
char i = 0x12345678;
short x = 0x12345678;
int i = 0x12345678;
}
int main(int argc,char* argv[])
{
plus();
return 0;
}
栈堆中的结果28-char 24-short 20-int
整数类型区分为有符号和无符号
当什么都不写,默认有符号
void Plus1()
{
char i = 0xFF;
unsigned k = 0xFF;
}
int main(int argc,char* argv[])
{
plus();
return 0;
}
这里unsigned默认四个字节,如果unsign char就是一个字节
void Plus1()
{
char i = 0xFF;
unsigned k = 0xFF;
printf("%d",i);
printf("%d",k);
}
int main(int argc,char* argv[])
{
plus();
return 0;
}
这里打印出来,i和K的值分别是-1,255
也可以比较大小
void Plus1()
{
char i = 0xFF;
char k = 1;
if(i>k){
printf("i>k");
}
else{
printf("i<k");
}
}
int main(int argc,char* argv[])
{
plus();
return 0;
}
运行,结果是i<k
void Plus1()
{
unsigned char i = 0xFF;
unsigned char k = 1;
if(i>k){
printf("i>k");
}
else{
printf("i<k");
}
}
int main(int argc,char* argv[])
{
plus();
return 0;
}
运行结果是i>k
if语句编译:
void Plus1( char i ,char k = 1)
{
if(i>k){
printf("i>k");
}
else{
printf("i<k");
}
}
int main(int argc,char* argv[])
{
plus(0xFF,1);
return 0;
}
这种编译结果
void Plus1(unsigned char i ,unsigned char k)
{
if(i>k){
printf("i>k");
}
else{
printf("i<k");
}
}
int main(int argc,char* argv[])
{
plus(0xFF,1);
return 0;
}
这种编译结果:
浮点类型
float double
void Plus1()
{
float i = 12.5f
}
int main(int argc,char* argv[])
{
plus(0xFF,1);
return 0;
}
例子:
三个部分,前三步对应后半部分,456,对应前两个部分
duoble是这样存:
套用float的模板就行了
英文字符存储
void Plus()
{
char c = 'A';
}
int main(int argc, char*argv[])
{
plus();
}
转化位ascii编码存储,在汇编中是这样存的
编译器帮我们做了这些事情
中文字符存储
char* x ='啊';
char* y ='北';
相当于用两个ASCII码
将上面的代码改一下
void Plus()
{
char c = '中';
}
int main(int argc, char*argv[])
{
plus();
}
但是这样没有意义,因为char类型的字符变量只能存储一个字节
但是这样写,就能存进去中文:
void Plus()
{
char * c = '中';
}
int main(int argc, char*argv[])
{
plus();
}