15、指针(一):指针的概念与本质、指针变量的定义与初始化、取地址符(&)与解引用符(*)
指针,是C语言里绕不开的一道坎。很多人学C学到指针就卡住了,觉得它玄乎其玄。其实说白了,指针就是一个存放地址的变量。嗯,就这么简单。
我记得刚入行那会儿,带我的老工程师跟我说过一句话:「指针就是C语言的灵魂,搞不懂指针,你就别跟人说你会C。」当时我不太服气,后来在项目里被指针坑过几次,才明白这话的分量。
指针的本质:地址的化身
咱们先想一个问题:程序里的变量存在哪里?内存里。内存就像一排带编号的柜子,每个柜子都有一个唯一的编号,这个编号就是地址。
指针变量,就是用来存这个编号的。它不存真正的数据,它只告诉你「数据在哪个柜子里」。
核心理解:指针 = 地址的容器。普通变量存的是值,指针变量存的是地址。
我在项目中遇到过这样一个场景:一个结构体特别大,如果每次函数调用都传整个结构体,内存开销巨大。用指针传地址,就只传一个4字节或8字节的地址,效率高得多。这就是指针最朴素的应用。
指针变量的定义与初始化
定义指针的语法很简单:
int *p; // p是一个指向int类型变量的指针
char *cp; // cp是一个指向char类型变量的指针
double *dp; // dp是一个指向double类型变量的指针
这里的 * 是类型说明符,告诉编译器「我要定义一个指针变量」。至于它指向什么类型,由前面的类型名决定。
定义完指针,得让它指向一个合法的地址。不然它就是野指针,乱指一气,后果很严重。
int a = 10;
int *p = &a; // 初始化:让p指向变量a的地址
我个人习惯在定义指针的同时就初始化,要么让它指向一个有效变量,要么赋值为 NULL:
int *p = NULL; // 空指针,安全
警告:未初始化的指针是野指针。我曾经在调试一个内存泄漏问题时,发现罪魁祸首就是一个没初始化的指针,它随机指向了一块内存,导致程序崩溃。从那以后,我定义指针必初始化。
取地址符(&)与解引用符(*)
这两个符号是指针操作的核心工具。
&:取地址
& 的作用是获取一个变量的内存地址。它后面跟的是变量名:
int x = 5;
int *px = &x; // px里存的是x的地址
你可以把 &x 理解成「x这个柜子的编号」。
*:解引用
* 的作用正好相反:通过地址找到它指向的变量。它后面跟的是指针变量:
int x = 5;
int *px = &x;
printf("%d\n", *px); // 输出5,*px等价于x
说白了,*px 就是「去px存的地址那个柜子里,把东西拿出来」。
小技巧:区分定义时的 * 和使用时的 *。定义时 int *p; 的 * 是类型说明符;使用时 *p = 10; 的 * 是解引用操作符。我刚开始学的时候也经常搞混,后来记住「定义看类型,使用看操作」就再也没错过。
一个完整的例子
#include <stdio.h>
int main() {
int a = 100;
int *p = &a; // p指向a
printf("a的值: %d\n", a); // 100
printf("a的地址: %p\n", &a); // 比如 0x7ffd1234
printf("p的值: %p\n", p); // 和上面一样,0x7ffd1234
printf("p指向的值: %d\n", *p); // 100
*p = 200; // 通过指针修改a的值
printf("修改后a的值: %d\n", a); // 200
return 0;
}
你想想看,p 和 &a 的值是一样的,都是地址。*p 和 a 的值也是一样的,都是那个地址里存的数据。这就是指针的对称美。
指针的常见误区
| 误区 | 说明 | 正确做法 |
|---|---|---|
混淆 & 和 * |
以为 & 是取值,* 是取地址 |
& 取地址,* 取内容 |
| 未初始化就使用 | 指针指向随机地址,操作危险 | 初始化或赋 NULL |
| 类型不匹配 | int * 指向 double 变量 |
指针类型必须与指向类型一致 |
避坑指南:我曾经在项目里犯过一个低级错误——把一个 int * 指向了一个 float 变量。编译没报错,但运行时数据全乱了。因为解引用时,编译器按 int 的大小去读内存,而实际存的是 float 的字节布局。嗯,调试了一下午才发现。
知识体系图
下面这张图帮你理清指针的核心逻辑:
指针这东西,说白了就是一层间接性。你直接操作变量,是一步到位;通过指针操作变量,是绕了个弯。但这个弯,给了你极大的灵活性——动态内存分配、数组操作、函数参数传递……都离不开它。
我建议你学指针的时候,多画图。把内存想象成一排带编号的格子,指针就是一张写着格子编号的纸条。这样理解起来,比死记硬背语法要快得多。
个人经验:刚开始学指针,别急着搞复杂的链表、树。先把 & 和 * 这两个操作符练熟,写几个小程序感受一下「通过地址改值」的过程。基础打牢了,后面什么二级指针、函数指针都不在话下。
公众号:蓝海资料掘金营,微信deep3321