指针的本质
double d = 3.1416;
char ch = *(char*)&d;
在&d操作中,会得到两部分信息,一是变量的首地址,二是指针的类型是double *(即在取值时, 会选择首地址后8bytes,作为该变量)。因此,我们可以通过修改指针的类型,对内存中的数据进行不同的解释,从而达到不同的效果。
在操作二中,我们将double *强转成了char *,因此在取值操作时,只会选择d变量首地址开始一个字节的数据作为char类型变量。接下来,我们在看一个例子。
short s = 45;
double d = *(double *)&s;
对于上述操作,变量s只占2个字节,但是因为将&s的指针类型强转为double *。因此,在取值操作时,会从s变量首地址后取8bytes解释为double类型数据。
大小端
大小端是指在内存中的字节序,大端是指将高位字节放在低地址处,小端是指将低位字节放在低地址处。举个例子。
注:老师上课讲的一般是大端表示,笔记中则采用小端表示
结构体
struct fraction {
int num;
int denum;
}
fraction pi;
pi.num = 22;
pu.denum = 7;
((fraction*)&(pi.denum))->num = 12;
分析:&(pi.denum)的类型是int *类型,将其强制转为fraction *类型后,就会将pi.denum变量的首地址后8bytes作为fraction类型,因此在对num赋值时就会覆盖pi.denum中的数据。
后面有多个例子,大家可以自己分析一下。
((fraction *)&(pi.denum))->denum = 33;
数组
int array[10];
以上式为例,数组的本质就是在内存中预留40字节的空间(即10个int类型)。而array实际上就是一个指向首元素的指针。如下图所示。
对于array[0]的操作本质上就是*(array + 0)的指针运算操作。同时,数组不会进行边界检查,诸如下列操作都是正确的。
array[0] = 44;
array[9] = 100;
array[-4] = 77;
*(array - 4) = 77;
int arr[3];
arr[3] = 128;
((short*)arr)[6] = 2;
cout << arr[3] << endl;
输出为2
举个例子给大家分析一下
int arr[5];
((short*)((char*)(&arr[1]) + 8))[3] == ?
数组和结构体
struct student {
char *name;
char suid[8];
int numUnits;
};
struct pupils[4];
pupils[3].name = pupils[0].suid + 6;
strcpy(pupils[3].name, "123456");