1. C语言的汇编表示

1.1 基本信息

  • 入口程序:程序开始执行的地方
  • return:执行结束

1.2 什么是函数?

函数是一些列指令的集合,用以完成某个会被重复使用的特定功能

  • C语言中函数格式:
    返回类型 函数名(参数列表){}

<1>返回类型、函数名不能省略
<2>参数列表可以省略

[!info] 函数名、参数名的命名规则

  1. 只能以字母、数字、下划线组成,且首字母不为数字
  2. 区分大小写
  3. 不能使用关键字

1.3 基本操作

  • 创建exe文件(F7)
  • 运行(F5)
  • 断点(F9)
  • 步过(F10)
  • 步入(F11)

1.4 函数的调用

  • 汇编中的函数调用:
    • CALL/JMP指令
  • C语言中的函数调用:
    • 函数名(参数1,参数2);

实际上:函数名就是编译器起的内存地址的别名

2. 参数传递与返回值

2.1 函数定义

1
2
3
4
5
6
7
8
9
10
返回类型 函数名(参数列表)

return;
}

举例:
int plus(int x,int y){
return x+y;
}

数据类型:
int – 4 Byte
short – 2 Byte
char – 1 Byte

进制标识:

  • 十六进制 – 通常h结尾,例如40h
  • 二进制 – 通常b结尾,例如1010b
  • 八进制 – 通常oq结尾,例如77o
  • 十进制 – 默认无后缀,或有时用d

2.2 堆栈图理解

画堆栈图可以方便我们从汇编角度理解C语言命令是怎么运行的

  • 堆栈缓冲区会填入CC
  • 堆栈使用后,需要平栈
  • 堆栈使用后,原先使用的堆栈并没有被清除,会被利用

2.3 参数传递

C语言:堆栈传参 从右向左

2.4 返回值存储

C语言:返回值存储在EAX中

3. 变量

3.1 声明变量

数据类型的宽度:

  • int – 4 Byte
  • short – 2 Byte
  • char – 1 Byte

[!info] 函数名、参数名的命名规则

  1. 只能以字母、数字、下划线组成,且首字母不为数字
  2. 区分大小写
  3. 不能使用关键字

3.2 全局变量

  1. 编译的时候就已经确定了内存地址和宽度,变量名就是内存地址的别名
  2. 如果不重写编译,全局变量的内存地址保持不变。游戏外挂中的找“基址”,就是找全局变量
  3. 全局变量中的值任何程序都可以修改,是公用的
    如:CE搜索基址

3.3 局部变量

  1. 局部变量是函数内部申请的,如果函数没有执行,那么局部变量没有内存空间。
  2. 局部变量的内存是在堆栈中分配的,程序执行时才分配。我们无法预知程序何时执行,所以我们无法确定局部变量的内存地址。
  3. 因为局部变量地址内存不确定,所以,局部变量只能在函数内部使用,其他函数不能使用

3.4 变量初始值

  1. 全局变量可以没有初始值而直接使用,系统默认初始值为0
  2. 局部变量在使用前必须要赋值

4. 变量与参数的内存空间

缓冲区:用于存储局部变量

EBP-4 局部变量
EBP 原EBP值
EBP+4 返回地址
EBP+8 参数区

5. 函数嵌套调用的内存布局

画堆栈图理解
函数与嵌套函数:在堆栈上是相邻完整的函数堆栈

6. 整数类型

6.1 C语言变量类型

  • 基本类型
    • 整数类型
    • 浮点类型
  • 构造类型
    • 数组类型
    • 结构体类型
    • 共用体(联合)类型
  • 指针类型
  • 空类型(void)

6.2 整数类型的宽度

char、short、int、long

char – 8 BIT – 1 Byte – 00xFF
short – 16 BIT –2 Byte – 0
0xFFFF
int – 32 BIT – 4 Byte – 00xFFFFFFFF
long – 32 BIT – 4 Byte – 0
0xFFFFFFFF

特别说明:int在16位(32位以上)计算机中与short(long)宽度一致

6.3 数据溢出

  1. char x=0xFF; //1111 1111
  2. char y=0x100; //0001 0000 0000
    数据溢出时舍弃高位

6.4 存储格式

有符号数补码存储
char x=1; //0000 0001(0x01)
char x=-1; //1111 1111(0xFF)

6.5 有符号与无符号数

signed、unsigned

  1. 数据存储时都为文本直接存储,使用时再判断类型
  2. 数据宽度变大时,存储在低位,有符号数补1/无符号数补0

7. 浮点类型

7.1 浮点类型的种类

float – 4 Byte
double – 8 Byte
long double – 8 Byte(某些平台的编译器可能是16字节)

建议:
float x=1.23F;
double d=2.34;
long double d=2.34L;

7.2 浮点类型的存储格式

float和double在存储方式上都遵从IEEE编码规范

float的存储方式:符号位(1) – 指数部分(8) – 尾数部分(23)
double的存储方式:符号位(1) – 指数部分(11) – 尾数部分(52)

7.2.1 十进制整数转二进制

整数部分转换二进制:

如:8d -> 1000b
8/2=4 ··· 0
4/2=2 ··· 0
2/2=1 ··· 0
1/2=0 ··· 1
————————————
1000b

存储方式:取上一次计算结果/2得到的余数部分,直到结果为0停止,从下往上读

9/2=4 ··· 1
4/2=2 ··· 0
2/2=1 ··· 0
1/2=0 ··· 1
————————————
1001b

总结:所有的整数一定可以完整转换成二进制

7.2.2 十进制小数转二进制

小数部分转换二进制:

如:0.25
0.25*2=0.5 – 0
0.5*2=1.0 – 1
01

存储方式:取上一次计算结果的小数部分*2得到的整数部分,直到小数部分为0停止,从上往下读

0.4*2=0.8 – 0
0.8*2=1.6 – 1
0.6*2=1.2 – 1
0.2*2=0.4 – 0
···

总结:二进制描述小数,不可能做到完全精准

7.2.3 科学计数法

[!info]
二进制数*2,左移一位
二进制数/2,右移一位(可移至小数点后)

存储方式

  • 符号位:+为0,-为1
  • 指数部分:
    • 科学计数法时小数点左(右)移,最高位为1(0)
    • 指数-1得到的数转换为二进制,从最低位填入指数部分
  • 尾数部分:小数部分填入尾数部分的最高位
  • 其余空处补0
  • 最后将32位的二进制数转为16进制存储

[!example]
8.25 -> 1000.01 -> 1.00001*2^3
1.00001*^3
0 10000010 00001000000000000000000
0100 0001 0000 0100 0000 0000 0000 0000
41040000h

7.3 浮点类型的精度

float和double的精度是由尾数的位数决定的
float:2^23=8388608,共7位,故最多有7位有效数字
double:2^52=4503599627370496,共16位,故最多有16位有效数字

8. 字符与字符串

8.1 字符的使用

1
2
3
int x='A'; int y='B';

putchar(x) //#include <stdio.h>

ASCII表

8.2 字符类型

ASCII表最后一位为127位(7F),因此一个字节即可存储
于是同常使用char x='A'来存储字符
实际上char是一个只能存储1字节的整型,而不是字符类型

8.3 转义字符

转义字符:\

1
2
char i='n'; //输出n
char i='\n'; //输出换行

8.4 printf函数的使用

字符串:一堆字符的ASCII拼在一起

1
2
3
4
5
6
7
printf("Hello_World!\n"); //打印字符串

printf("%d %u %x\n); //打印整型
//%d有符号数,%u无符号数,%x十六进制

printf("%6.2f\n",f); //打印浮点数
//6位,其中小数点后2位

9. 中文字符

  • 拓展ASCII码表后,将两个大于127的字符连在一起,表示一个汉字,这种编码规则就是GB2312或GB2312-80
  • 在这些编码里,连在ASCII里本来就有的数字、标点、字母都重新编了两个字节长的编码,这就是常说的全角字符,而原来的127号之前的就叫做半角字符

弊端:
<1>两种编码可能使用相同的数字代表两个不同的符号
因此,出现了Unicode编码

10. 运算符与表达式

10.1 运算符与表达式

表达式的结果:
char –> short –> int –> float –> double

10.2 算数运算符

+ - * / % ++ –
加 减 乘 除 取余 自增 自减
++x; //
x++; //
1050

10.3 关系运算符

< <= > >= == !=

  • 关系运算符的值只能是0和1
  • 关系运算符的值为真(假)时,结果值都为1(0)

10.4 逻辑运算符

! && ||
逻辑非 逻辑与(且) 逻辑或(或)