1. 指针的本质:内存地址与指针变量的关系
指针这东西,说白了就是地址的“快递员”。
我刚开始学C语言那会儿,总觉得指针特别玄乎。后来在项目里调试一个内存泄漏问题,折腾了三天,才真正搞明白——指针就是个变量,只不过它存的是地址,不是普通数值。
1.1 内存地址是什么?
你想想看,计算机的内存就像一栋巨大的公寓楼。每个房间都有唯一的门牌号,这个门牌号就是内存地址。
- 每个字节(8个比特)都有一个地址
- 地址通常用十六进制表示,比如
0x7ffeefbff5a8 - 变量就住在这个“房间”里,地址就是它的门牌号
核心概念:内存地址是数据在内存中的位置编号。指针变量就是用来存这个编号的变量。
1.2 指针变量的声明与初始化
声明指针,其实就是在告诉编译器:“嘿,我要一个变量,它专门用来存地址。”
int *p; // p是一个指针,指向int类型的数据
char *cp; // cp是一个指针,指向char类型的数据
double *dp; // dp是一个指针,指向double类型的数据
嗯,这里要注意:int *p 里的 * 只是声明符号,不是解引用。我见过不少新手在这里犯迷糊。
初始化呢?就是给这个指针变量赋一个具体的地址值:
int a = 42;
int *p = &a; // p现在存的是变量a的地址
个人习惯:我每次声明指针都会立即初始化,要么给有效地址,要么给 NULL。野指针这东西,坑过我好几次。
1.3 取地址符 & 与解引用符 *
这两个符号,是操作指针的左右手。
| 符号 | 名称 | 作用 | 示例 |
|---|---|---|---|
& |
取地址符 | 获取变量的内存地址 | &a → 得到a的地址 |
* |
解引用符 | 访问指针指向的内存中的数据 | *p → 得到p指向的值 |
int a = 10;
int *p = &a; // & 取a的地址,赋给p
printf("a的值: %d\n", a); // 输出 10
printf("a的地址: %p\n", &a); // 输出类似 0x7ffeefbff5a8
printf("p的值: %p\n", p); // 输出和上面一样
printf("*p的值: %d\n", *p); // 输出 10,解引用得到a的值
*p = 20; // 通过指针修改a的值
printf("a的新值: %d\n", a); // 输出 20
我曾经犯过一个低级错误:声明了指针却没初始化,直接解引用。结果程序崩溃,查了半天才发现是野指针在作怪。记住:未初始化的指针,解引用就是未定义行为。
1.4 指针的图解
光说可能不够直观,我画个图帮你理解:
1.5 常见误区与避坑指南
- 混淆声明和解引用:
int *p;里的*是声明,*p = 10;里的*才是解引用。 - 忘记初始化:声明指针后一定要赋值,否则就是野指针。
- 类型不匹配:
int *p = &a;要求 a 是 int 类型,不能把 double 的地址赋给 int 指针。
我建议:写代码时养成习惯——声明指针的同时初始化。如果暂时没有目标地址,就赋 NULL。这样至少解引用时会立刻崩溃,而不是偷偷摸摸地破坏数据。
1.6 小结
指针就是个存地址的变量。& 取地址,* 解引用。就这么简单。
但简单不代表容易。我在项目里见过太多因为指针没初始化、类型不匹配导致的诡异bug。记住:指针是工具,不是玩具。用好了如虎添翼,用不好就是自掘坟墓。
一句话总结:指针变量存地址,取地址用 &,拿数据用 *,初始化别偷懒。