指针基础(上):指针的概念、指针变量的定义与初始化、取地址运算符(&)、解引用运算符(*)、指针的大小
大家好,我是你们的老朋友。今天咱们来聊聊C语言里一个绕不开的话题——指针。说实话,指针这东西,我当年学的时候也头疼过一阵子。但后来我发现,只要把它的本质搞清楚了,指针其实没那么可怕。它就像一把双刃剑,用好了能写出高效优雅的代码,用不好嘛……嗯,段错误(Segmentation Fault)就会找上门来。
一、指针到底是什么?
先问大家一个问题:你家里门牌号是干什么用的?对了,用来定位你家在哪。指针也是类似的道理——指针就是内存地址的“门牌号”。
我们写的每个变量,在程序运行时都会被分配到内存中的某个位置。这个位置有一个编号,就像门牌号一样,我们管它叫地址。而指针变量,就是用来存放这个地址的变量。
说白了,指针不直接存数据,它存的是数据在内存里的“住址”。你拿着这个住址,就能找到那个数据。
核心理解:指针 = 地址的容器。指针变量里装的是另一个变量的内存地址。
我在项目中遇到过不少新手,一上来就把指针和它指向的数据搞混。记住:指针是地址,不是数据本身。这个区分非常重要。
二、指针变量的定义与初始化
怎么定义一个指针变量呢?语法其实很简单:
类型名 *指针变量名;
这里的星号 * 是告诉编译器:“我要定义一个指针变量”。前面的类型名表示这个指针将来要指向什么类型的数据。
举个例子:
int *p; // p 是一个指向 int 类型数据的指针
char *cp; // cp 是一个指向 char 类型数据的指针
double *dp; // dp 是一个指向 double 类型数据的指针
定义完了,怎么初始化呢?我个人的习惯是:定义指针的同时就给它赋初值,避免出现“野指针”。
int a = 10;
int *p = &a; // 把变量 a 的地址赋给指针 p
这里用到了取地址运算符 &,我们马上就会讲到。
注意:如果定义指针时不知道指向哪里,建议先初始化为 NULL:
int *p = NULL; // 空指针,表示哪里都不指向
我曾经因为忘记初始化指针,在项目里排查了一整天才找到 bug……从那以后,我养成了“指针必初始化”的习惯。
三、取地址运算符(&)
& 运算符的作用很简单:获取一个变量的内存地址。
用法:
int x = 100;
printf("x 的地址是:%p\n", &x); // %p 用来打印地址
输出结果类似这样(每次运行地址可能不同):
x 的地址是:0x7ffd5a3b4c2c
你想想看,这个地址就是变量 x 在内存中的“门牌号”。有了它,我们就能通过指针来间接操作 x 了。
取地址运算符只能用于变量,不能用于常量或表达式。比如 &100 或者 &(a+b) 都是非法的——常量没有固定的内存地址。
四、解引用运算符(*)
如果说 & 是“找地址”,那 * 就是“通过地址找数据”。
解引用运算符用在指针前面,表示访问指针所指向的那个变量。
int a = 10;
int *p = &a; // p 保存了 a 的地址
printf("%d\n", *p); // 输出 10,*p 就相当于 a
*p = 20; // 等价于 a = 20
printf("%d\n", a); // 输出 20
看到了吗?通过 *p,我们就能直接操作 a 这个变量。这就是指针的威力——间接访问。
小技巧:定义指针时的 * 和 解引用时的 * 是两回事。定义时是“声明这是一个指针”,使用时是“访问指针指向的内容”。我刚开始学的时候也经常搞混,后来记住一句话就清楚了:“定义看类型,使用看上下文”。
五、指针的大小
指针变量本身占多大内存?这取决于你的系统架构:
| 系统架构 | 指针大小 | 说明 |
|---|---|---|
| 32位系统 | 4 字节(32位) | 地址范围 0 ~ 2^32-1 |
| 64位系统 | 8 字节(64位) | 地址范围 0 ~ 2^64-1 |
不管指针指向的是 int、char 还是 double,指针本身的大小都是一样的。为什么呢?因为地址的长度是固定的,跟地址里存的是什么类型的数据无关。
你可以用 sizeof 来验证:
printf("int* 的大小:%zu 字节\n", sizeof(int*));
printf("char* 的大小:%zu 字节\n", sizeof(char*));
printf("double* 的大小:%zu 字节\n", sizeof(double*));
在64位系统上,这三个输出都是 8。我当年第一次验证这个结果时还挺惊讶的——原来指针的大小跟它指向的类型没关系!
重要结论:指针的大小由系统地址总线的宽度决定,与指针指向的数据类型无关。32位系统指针占4字节,64位系统指针占8字节。
六、知识体系总览
为了让大家更直观地理解本章的知识结构,我画了一张图:
七、避坑指南与经验总结
学指针,有几个坑是绕不开的。我把自己踩过的坑分享给大家:
- 野指针:指针定义后没有初始化,它指向的内存地址是随机的。操作野指针会导致不可预料的后果。我曾经在嵌入式项目里因为野指针把整个系统的内存写乱了,最后只能重启设备……
- 空指针解引用:对 NULL 指针使用
*操作符,程序会直接崩溃。所以解引用前最好检查一下指针是否为 NULL。 - 类型不匹配:
int *p = &a;中 a 必须是 int 类型。如果 a 是 double 类型,编译器会报警告。
我的建议:
- 指针定义时立即初始化,不确定就赋 NULL。
- 解引用前检查指针是否为 NULL。
- 指针指向的变量生命周期结束后,及时将指针置为 NULL。
- 多画图理解指针和内存的关系,比死记硬背强得多。
好了,指针的基础知识就讲到这里。记住三个核心操作:定义指针、取地址、解引用。把这三点搞明白了,指针的大门就算正式打开了。
公众号:蓝海资料掘金营,微信deep3321