指鹿为马

第三课

只要敢玩,理论上你可以随意指定指针类型愚弄 C 编译器,让它相信你所说的都是真的,你说鹿是马,C 编译器便对此坚信不疑,我们来看看这门 “指鹿为马” 的艺术:

指 int 为 float

int i = 37;
float f = *(float *)&i;
// &i => 取 i 的地址,类型为 int *
// (float *)&i => 指鹿为马 -- 假装它是个 float *
// *(float *)&i => 取出 float* 所指的值,类型为 float
// 以上实现了指 int 为 float,因为本质上它们都是 4 个字节排列在一起
printf("%f \n", f); // 0.000000
// i: 00000000 00000000 00000000 00100101
// f: 00000000 00000000 00000000 00100101
// 1.00000000xxxxxx * 2^(-127) 约等于 0

指 float 为 short

float f = 7.0;
short s = *(short *)&f;
// &f => 取 f 的地址,类型为 float *
// (short *)&f => 指鹿为马 -- 假装它是个 short *
// *(short *)&f => 取出 short * 所指的值,类型为 short
printf("%s \n", s); // 输出结果视 endianness 而定
// Big Endian:
// f: 01000000 11100000 00000000 00000000
// s: 01000000 11100000
// Little Endian:
// f: 01000000 11100000 00000000 00000000
// s: 00000000 00000000

指 double 为 char

double d = 3.1416;
char ch = *(char *)&d;
// &d => 取 d 的地址,类型为 double *
// (char *)&d => 指鹿为马 -- 假装它是个 char *
// *(char *)&d => 取出 char * 所指的值,类型为 char
printf("%c \n", ch); // 输出结果视 endianness 而定
// Big Endian:
// d: 01000000 | 00001001 | 00100001 | 11111111 | 01001000 | 11101000 | 10100111
// ch: 01000000
// Little Endian:
// d: 01000000 | 00001001 | 00100001 | 11111111 | 01001000 | 11101000 | 10100111
// ch: 10100111

数组与指针算术(pointer arithmetic)

int array[10];
// array 等价于第一个元素的地址
array === &array[0]
array + k === &array[k]
*array === array[0]
*(array + k) === array[k]
// 指针算术是指,对指针做加减法时,会自动乘上指针所指数据类型所占的字节数
int array[5]; array[3] = 128;
((short *)array)[6] = 2;
// array => 数组起始地址,类型为 int *
// (short *)array => 指鹿为马 -- 假装它是个 short *,即 array 中的元素为 short 类型
// (short *)array[6]=> 将 short array 的第六个元素改成 2
// Big Endian:
// array: => [][][][00000000 00000000 00000001 00000000][]
// (short *)array[6]=2 => [][][][00000000 00000010 00000001 00000000][]
// 输出 768
// Little Endian:
// array: => [][][][00000000 00000000 00000001 00000000][]
// (short *)array[6]=2 => [][][][00000000 00000000 00000000 00000010][]
// 输出 2

结构体

struct student {
char *name;
char suid[8];
int numUnits;
};
int main(int argc, char **argv) {
struct student pupils[4]; // 初始化了长度为 4 的 student 结构体数组
pupils[0].numUnits = 21; // 赋予第一个 student 的 numUnits 字段
pupils[2].name = strdup("Adam"); // 将第三个 student 的 name 指向 heap 中初始化的一个字符串 "Adam\0"
pupils[3].name = pupils[0].suid + 6;// 将第四个 student 的 name 指向第一个 student 的 suid 往后移 6 位之处
strcpy(pupils[1].suid, "40415xx"); // 将字符串 "40415xx" 放入第一个 student 的 suid 中
strcpy(pupils[3].name, "123456"); // 将字符串 "123456" 放入第四个 student 的 name, 即第一个 student 的 suid 往后移 6 位之处
}

如下图所示:

参考资料