C 指针基础
更新时间:2026-08-26。本文是
languages/c/主题入门层第 33 篇,指针专题开篇。指针是 C 最出名的概念,也是劝退点。但把它的模型想清楚,一切就顺了:指针就是一个"存着别人地址"的变量。你家里有门牌号,指针就是写着门牌号的纸条。
本文要回答的问题
- 指针到底存的是什么?
&和*各是什么操作?- 为什么声明
int *p还要带类型?
一、指针 = 存地址的变量
c
int x = 42;
int *p = &x; // p 是一个变量,里面存的是 x 的地址text
内存布局:
变量 x(地址 0x100): [ 42 ]
变量 p(地址 0x200): [ 0x100 ] ← p 存的是 x 的地址| 写法 | 含义 |
|---|---|
&x | 取 x 的地址(产生一个"指向 int 的指针"值) |
int *p | p 是"指向 int 的指针"变量 |
*p | 解引用:通过地址访问 p 指向的那个 int |
二、解引用:顺着地址找东西
c
int x = 42;
int *p = &x;
printf("%p\n", p); // 0x100(地址)
printf("%d\n", *p); // 42(解引用:读 p 指向的值)
*p = 100; // 解引用后可以写!
printf("%d\n", x); // 100:x 被 p 改了*p 在赋值左边(*p = 100)表示"往那个位置写",在右边(int y = *p)表示"从那个位置读"。解引用是 C 里最强大的操作之一——拿到地址就能读写那块内存。
三、指针的类型:决定"步长"和"怎么解释"
c
int *ip; // 指向 int:解引用读到 4 字节的整数
double *dp; // 指向 double:解引用读到 8 字节的浮点
char *cp; // 指向 char:解引用读到 1 字节指针的类型决定两件事:
- 解引用时读多少字节、怎么解释:
*ip按 int 的规则读 4 字节; p + 1前进多少字节:int* + 1前进 4 字节,double* + 1前进 8 字节。
所以 int *p 的 int 不是摆设——编译器全靠它决定偏移和解释。类型错了(比如把 int* 存进 double*),解引用读出来就是垃圾。这就是类型转换里"强制转换要小心"的指针版。
四、NULL 与未初始化:两个不同的危险
c
int *p = NULL; // 明确"不指向任何地方"
int *q; // 未初始化:垃圾值,指向哪全凭运气!| 状态 | 含义 | 解引用 |
|---|---|---|
p = NULL | 明确不指向任何对象 | 段错误(可控、可检测) |
q 未初始化 | 垃圾地址 | 随机崩溃(最危险) |
p = &x | 指向有效变量 | 正常 |
NULL 解引用虽然也崩,但崩溃是"可预期"的,if (p == NULL) 能拦住;未初始化指针的崩溃是随机的,调试非常痛苦。所以:
指针声明时一律初始化:要么给地址,要么给 NULL。别让指针"裸奔"。
五、指针的声明写法:向右读
多级指针和 const 会让声明绕口,入门先记从变量名向右读:
c
int *p; // p 是 int 的指针
int **pp; // pp 是指向(int 指针)的指针**pp 的模型:pp 存的是"p 的地址",*pp 得到 p,**pp 得到 x。多级指针在"函数要改指针"时出现(传地址与指针的指针),入门能看懂就够。
六、常见坑对照
| 坑 | 现象 | 对策 |
|---|---|---|
| 未初始化指针解引用 | 随机崩溃 | 声明即初始化 |
忘 & | 把值当地址 | 传地址时写 &x |
忘 * | 改的是地址不是值 | *p = ... |
| NULL 解引用 | 段错误 | 解引用前判空 |
| 类型混用 | 读到垃圾 | 指针类型严格对应 |
七、与本站主线衔接
- 指针运算的完整规则,见指针运算;
- 指针在汇编里就是"寄存器存地址 + 间接寻址",见与汇编、性能剖析衔接;
- 悬垂指针与内存管理的坑,见内存管理入门。
一句话总结
指针是存地址的变量:& 取地址、* 解引用、类型决定步长与解释方式;声明即初始化(给地址或 NULL),NULL 崩溃可预期、未初始化崩溃是抽奖——把这个模型刻进脑子,指针世界就不再神秘。