这次依然是从汇编开始分析C语言里的一些语法实现,这次还是Windows平台上为主~
直接上最简单的:
1int z = 0;
2char s1 = (char)z;
3
4// ASM
5int* z = 0;
6// 这次就直接显示符号名吧,不看内存地址了
7// dword ptr就是数据类型声明
8mov dword ptr [z],0
9char* s1 = (char*)z;
10// movzx这个指令,下面讲解,总之就是把z移动到eax寄存器.
11movzx eax,byte ptr [z]
12// 直接粗暴的进行一个强转,al是AX的第八位,AX是EAX的低16位
13// mov指令要求操作数大小相等,byte ptr只占8个字节,所以这里用AL寄存器
14mov byte ptr [s1],al
movzx这个指令简单来说就是把小字节操作数移动到大字节寄存器,同时让大字节的高位补零。
为什么这么设计,我们从上面这个movzx这行代码说起,[z]是byte ptr类型,只占用8位也就是1字节。
eax是32位寄存器,能存放4字节,[z]放进eax只能占用低八位,高24位完全用不到,这里就需要用到高位补零了,就是因为怕高位还有字节没清零的情况,导致后续计算直接崩了。
接着看指针运算,这个其实早就学过了,这次只是温故一下,理解指针运算很重要的一点就是在C语言中,数据类型其实毫无意义,所有东西都可以当作内存来看,只是一个内存分配大小的问题。
1
2 // Q: 为什么1能转换成指针?
3 // A: 别惦记这你那逼数据类型了
4 char* cp = (char*)1;
5 // char* cp = 1
6
7 // cp被转化成int*了,指针指向位int类型,4字节,运算符在指针上的运算为位移,+2就意味着移动两格总计8字节
8 // 所以i1的值是9
9 // 简单来说类型转换,不会改变值,但是会改变运算方式
10 int i1 = (int)((int*)cp + 2);
11
12 // 这里也一样,i2为17
13 int* i2 = (int*)i1 + 2;
14
15 // short为2字节,但这里并非指针,只是普通的算术运算
16 // s = 95
17 short s = ((short)i2 + 2) * 5;
18
19 // 这里比较特别,i2被转化为2级指针,int**指向的类型是int*,指针类型占用的字节是8字节
20 // 所以是17 + 8 = 25
21 int** i3 = (int**)i2 + 1;
函数指针,这个东西主要就看看在汇编中是怎么实现的。
1
2add(1, 2);
3call add (07FF652F01311h)
4
5void* a = add;
6// 实际也没什么特别的,就是把add的函数地址给了进去
7lea rax,[add (07FF6F9311311h)]
8mov qword ptr [a],rax
9
10//我们也能做点其他的,比如把这个地址的数据读出来
11int* a = (int*)add;
12int as = *a; // 加*就是解引用,读里面的值, 不加*就只是读取内存地址

运行完后发现as的HEX值为0x00054ae9,我这台机器数值在内存中为小端存储,也就是e9 4a 05的酱紫存放.
进入到call add进去看看,是不是内存值也是这:

恩,实际上就是这样.
累死了,不写了。