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

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

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