想必很多同学都在C语言中使用过强制类型转换,不知道有没有碰到什么问题。如果谨慎使用强制类型转换,大多数时候都不会出错。当然,前提是对C语言的内存布局比较熟悉,否则有时候看着语法和逻辑都没有问题,却有可能出现意想不到的结果。
看下面这段代码,猜猜输出结果是什么:
1 #include <stdio.h>
2
3 void *a = 0;
4
5 void set(void *p) {
6 a = p;
7 printf("a has been set to: %d\n", a);
8 }
9
10 void get(void **p) {
11 if (p) {
12 *p = a;
13 printf("get value from a: %d\n", *p);
14 }
15 }
16
17 int main(void) {
18 char *s = "hello, world";
19 printf("string s: %s\n", s);
20
21 int data = 1;
22 printf("save data: %d\n", data);
23 set((void *)data);
24
25 data = 2;
26 printf("change data to: %d\n", data);
27
28 get((void **)&data);
29 printf("restore data to: %d\n", data);
30
31 printf("string s: %s\n", s);
32
33 return 0;
34 }
运行结果如下:
string s: hello, world
save data: 1
a has been set to: 1
change data to: 2
get value from a: 1
restore data to: 1
string s: (null)
程序的意图很简单,就是想用一个全局变量 a 来存储一个 int 类型的变量值,因为该全局变量是指针类型,一般来说,指针类型变量能存放的下 int 类型的值。从运行结果的前6行打印来看,程序执行正常,变量data的值被正确存储和还原了。但最后一行的输出就很怪异了,整个程序中并没有显式的改变指针s的值,但为什么最后s的值为NULL了?不应该存的是"hello, world"字符串常量的地址吗?
第5行的set函数是设置值,其中用到了强制类型转换,这个操作没有问题,但第28行在调用get函数时所用到的强制类型转换就可能有问题了,因为在get函数中,变量p中存放的是main函数中data变量的地址,也就是说,(*p)就是data,给(*p)赋值就是给data赋值,然而,在get函数中,(*p)的类型是指针类型。在上述代码的运行环境中,(*p)占用8个字节,data是int类型,占用4个字节,此时如果给(*p)赋值一个指针,就会影响data变量邻近的其他4个字节。
如果在main函数中添加几行打印,就能看的更清晰明了了。
char *s = "hello, world";
int data = 1;
char *c = (char *)&s;
printf("s addr: %p, s size: %d, s content: 0x%0x, s content's first byte: 0x%0x\n", &s, sizeof(s), s, *c);
printf("data addr: %p, data size: %d, data content: %d\n", &data, sizeof(data), data);
运行结果如下:
s addr: 0x7ffe25b5d530, s size: 8, s content: 0x40077d, s content's first byte: 0x7d
data addr: 0x7ffe25b5d52c, data size: 4, data content: 1
从运行结果来看,我们可以画出如下示意图:
与变量data邻近的就是变量s,在对(*p)进行赋值一个指针后,不仅变量data 的4个字节被改变了,连变量s的部分4个字节也被改变了,对于值为1的指针来说,低4个字节的值是1,高4个字节的值是0,因此当变量s的低4个字节被改变为0后,s的值就为NULL了。