目录
0 前言
适逢研0假期,研究生导师暂未安排任务,鄙人自学之余,想起本科恩师叮咛:“嵌入式的能力不能丢。”,遂将嵌入式C语言也纳入自学范围之中,重新打基础。一日见网课老师程序示例,如下图所示。
于是临时起兴,在VSCode中进行指针的初级实验,敲入如下代码
#include <stdio.h>
int main()
{
int a = 0x12345678;
int *p;
*p = (int *) &a;
printf("the *p is %x.\n", *p);
printf("the p is %x.\n", p);
p = &a;
printf("the *p is %x.\n", *p);
printf("the p is %x.\n", p);
}
自信满满点击运行,结果喜提Warning:assignment to 'int' from 'int*' makes integer from pointer without a cast。输出结果则如下所示。
the *p is 5ffe94.
the p is 1b5d90.
the *p is 12345678.
the p is 5ffe94.
自行琢磨一会儿自觉没什么问题(鄙人粗陋基础可见一斑),遂请教高人,得启发,将此次所得进行记录。
1 这个警告是什么意思?
意思是我们将int类型的值赋给了一个int*类型的变量。
按图索骥,重新回看代码——不对啊,我明明加了强制类型转换不是吗?而且视频里老师也是这么写的,也没有报Warning呀?
2 深入思考
2.1 引起问题的错误理解
对比老师的示例代码,发现老师是在初始化指针变量的时候对其进行了赋值,于是我修改了代码,尝试也在初始化指针变量的时候对其进行赋值,如下所示。
int main()
{
int a = 0x12345678;
int *p = &a;
printf("the *p is %x.\n", *p);
printf("the p is %x.\n", p);
p = &a;
printf("the *p is %x.\n", *p);
printf("the p is %x.\n", p);
}
再看输出结果,很好,符合预期。
the *p is 12345678.
the p is 5ffe94.
the *p is 12345678.
the p is 5ffe94.
结合高人所述,当我们初始化指针变量时写的这句int *p实际上是定义了一个int*类型的变量p,更好理解就是(int*) p而不是int (*p)。
在初始化时也应该认为是(int*) p = &a,而不是int (*p = &a)。
即初始化的这一句,
int *p = &a;
其效果等价于我们写了以下这两句。
int *p;
p = &a;
2.2 警告出现的原因与解决方案探索
我们错误代码的输出到底代表了什么呢?
the *p is 5ffe94.
the p is 1b5d90.
the *p is 12345678.
the p is 5ffe94.
先看第一行,输出的是变量a存储的内存地址,这是不需要过多解释的,因为我们确实使指针指向的地址中存放了a的地址。但这也是导致警告出现的原因,*p指向的数据是int类型,而&a返回的是int*类型,这是不匹配的。
the *p is 5ffe94.
于是我想,那我强制类型转换成int类型不就完了,遂修改代码如下。
#include <stdio.h>
int main()
{
int a = 0x12345678;
int *p;
*p = (int) &a;
printf("the *p is %x.\n", *p);
printf("the p is %x.\n", p);
p = &a;
printf("the *p is %x.\n", *p);
printf("the p is %x.\n", p);
}
结果是,喜提新的Warning:cast to pointer from integer of different size。就是说赋的值和指针具有不同的大小,我们可以用sizeof看看*p和&a的大小,再进一步选择强制类型转换的类型。
printf("the length of *p is %d.\n", sizeof(*p));
printf("the length of &a is %d.\n", sizeof(&a));
得到的结果如下所示。
the length of *p is 4.
the length of &a is 8.
这意味着如果我们要实现把a的地址存放在内存中并能通过指针正常访问的功能,我们应当让计算机以8字节/单位去访问内存,而非我们定义的int *p(即以4字节/单位去访问内存)。那就得在定义指针变量时就进行修改——嚯!坑越挖越大!
于是再修改代码,long long int类型的变量正是8字节的,于是定义一个long long int*类型的指针,让计算机以8个字节/单位去访问内存。
int main()
{
int a = 0x12345678;
long long int *p;
printf("the length of *p is %d.\n", sizeof(*p));
printf("the length of &a is %d.\n", sizeof(&a));
*p = (long long int)&a;
printf("the *p is %x.\n", *p);
printf("the p is %x.\n", p);
}
这下得到的输出如下图所示,编译器也没有报错了。
the length of *p is 8.
the length of &a is 8.
the *p is 5ffe94.
the p is 1d5d90.
2.3 进一步思考
在2.1中,第二行输出的是一个不知道怎么来的地址,而且多次编译,运行后得到的还不一样!如下图所示是连续两次编译得到的输出结果,可以看到第一次输出的是9e5d90,第二次则是195d90。
多编译几次,还能得到更多不同的输出结果,有时会相同,但奇怪的是他们末尾都是‘5d90’。
这是因为如果我们没有在初始化时指定指针指向的地址,系统就会让其指向一段随机内存,这段内存是什么形式的(可读?可写?不可访问?合法的?非法的?)我们是不知道的,这就可能引起严重的问题,这种问题就是我们常说的“野指针”问题。
3 总结与唠嗑环节
这下对C语言指针的理解更深了!如文章内容有不妥或鄙人有理解错误的地方,还请路过的大佬不吝赐教!继续学习,继续热爱!