Last Regrets

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; // 加*就是解引用,读里面的值, 不加*就只是读取内存地址

image.png

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

进入到call add进去看看,是不是内存值也是这:

image.png

恩,实际上就是这样.

累死了,不写了。