1. 指针本质:内存地址、指针变量、取地址符&、解引用符*、指针大小
好,咱们直接切入正题。指针这东西,我见过太多初学者被它绕晕了。说实话,我当年学C语言时,指针也让我头疼了好一阵子。但后来我发现,只要抓住一个核心——内存地址,一切就豁然开朗了。
1.1 内存地址——计算机的“门牌号”
你想想看,我们写程序时定义的每个变量,最终都要存放在内存里。内存就像一栋巨大的公寓楼,每个房间都有一个唯一的门牌号。这个门牌号,就是内存地址。
核心概念:内存地址是内存中每个字节的唯一编号。32位系统下地址范围是0x00000000~0xFFFFFFFF,64位系统则更大。
举个例子:
int a = 10; // 编译器给a分配了4个字节(32位int)
// 假设起始地址是0x7ffeefbff5a8
变量a的值是10,但它在内存中占据的起始位置是0x7ffeefbff5a8。这个地址,就是我们要找的“门牌号”。
1.2 指针变量——专门存地址的变量
普通变量存数值,指针变量存什么呢?存地址。说白了,指针变量就是用来存放另一个变量的内存地址的变量。
int a = 10;
int *p = &a; // p是一个指针变量,存的是a的地址
这里p的类型是int *,读作“指向int的指针”。我个人习惯把星号紧贴变量名写,这样更清晰——int *p表示“声明一个指针p,它指向int类型”。
小技巧:声明指针时,星号两边的空格其实无所谓。但建议统一风格,我一般写int *p,而不是int* p。为什么?因为int* p, q这种写法容易让人误以为q也是指针,实际上q只是普通int。
1.3 取地址符&——找到变量的门牌号
取地址符&,它的作用就是获取一个变量的内存地址。用法很简单:
int b = 20;
printf("b的地址是:%p\n", &b); // 输出类似 0x7ffeefbff5a4
我在项目中遇到过一个问题:有人试图对常量取地址,比如&100,这是非法的。因为常量没有固定的内存位置,只有变量才有地址。
注意:取地址符只能用于左值(lvalue),也就是能放在赋值号左边的、有明确内存位置的表达式。像&(a + b)这种就是错误的。
1.4 解引用符*——通过门牌号找到房间
有了地址,怎么拿到地址里存的值?用*运算符。这叫解引用,也叫间接访问。
int c = 30;
int *ptr = &c;
printf("c的值是:%d\n", c); // 直接访问,输出30
printf("ptr指向的值是:%d\n", *ptr); // 间接访问,也输出30
*ptr = 40; // 通过指针修改c的值
printf("c的新值是:%d\n", c); // 输出40
你看,*ptr就相当于c本身。通过指针,我们不仅能读,还能写。这就是指针的强大之处——间接操作数据。
嗯,这里要注意:声明指针时的*和使用指针时的*,虽然符号一样,但含义不同。声明时表示“这是一个指针”,使用时表示“解引用”。
1.5 指针大小——到底占几个字节?
这个问题我经常被问到。指针变量本身占多大内存?答案是:取决于系统架构。
| 系统架构 | 指针大小 | 地址总线宽度 |
|---|---|---|
| 32位系统 | 4字节(32位) | 32位 |
| 64位系统 | 8字节(64位) | 48位(实际使用) |
| 16位系统(古董) | 2字节(16位) | 16位 |
不管指针指向什么类型——int *、char *、double *,甚至void *——在同一个系统下,它们的大小都是一样的。为什么?因为地址的长度是固定的。
printf("int* 大小:%zu 字节\n", sizeof(int *));
printf("char* 大小:%zu 字节\n", sizeof(char *));
printf("double* 大小:%zu 字节\n", sizeof(double *));
// 64位系统下,全部输出8
我曾经在做一个嵌入式项目时,把32位代码移植到64位平台,结果发现所有指针变量占用的内存翻了一倍。当时没注意,导致结构体对齐出了问题,排查了好久。所以,跨平台开发时一定要留意指针大小。
避坑指南:不要假设指针大小是4字节。永远用sizeof(指针)来获取实际大小。另外,不要将指针强制转换为int类型,在64位系统下int只有4字节,装不下8字节的地址。
知识体系总览
下面这张图,把本章的核心逻辑串起来了:
说白了,指针就是一层间接关系。你直接操作变量,是“找到房间直接住进去”;你用指针,是“先记下房间号,再通过房间号进去”。这种间接性,给了我们极大的灵活性——可以动态分配内存、可以修改函数外的数据、可以实现复杂的数据结构。
我的建议:刚开始学指针时,多画图。把内存地址、指针变量、指向关系画出来,比死记硬背强一百倍。我当年就是靠画图把指针搞明白的。
好了,这一章的内容就到这里。记住:地址是门牌号,指针变量是存门牌号的纸条,&是查门牌号,*是通过门牌号进门。把这四个概念理清了,指针的大门就为你打开了。