第10章 指针(一):指针的概念、指针变量的定义与初始化、取地址符&与解引用符*、指针与数组
指针,是C语言的灵魂,也是很多初学者觉得“难啃”的地方。说实话,我当年学指针时也绕了好一阵子。但一旦你理解了它的本质,你会发现——指针其实就是地址,没什么神秘的。
10.1 指针的概念
先问一个问题:变量存在哪里?
答案是内存。每个变量在内存中都有一个地址,就像你家的门牌号。指针,就是用来存放这个地址的变量。
我习惯这样理解:普通变量存的是“值”,指针变量存的是“地址”。
举个例子:
int a = 10; // a 存的是 10
int *p = &a; // p 存的是 a 的地址
这里 p 就是指针变量。它不直接存 10,而是存了 a 在内存中的位置。
核心理解:指针就是地址,指针变量就是存放地址的变量。
10.2 指针变量的定义与初始化
定义指针变量,语法很简单:
类型 *变量名;
比如:
int *p; // 指向整型数据的指针
char *cp; // 指向字符型数据的指针
double *dp; // 指向双精度浮点型数据的指针
注意那个星号 *,它告诉编译器:这是一个指针变量,不是普通变量。
定义之后,一定要初始化。否则指针的值是随机的,乱用会出大问题。
初始化方式有两种:
- 用已有变量的地址初始化
- 赋值为 NULL
int a = 100;
int *p1 = &a; // 方式1:指向 a
int *p2 = NULL; // 方式2:指向空,表示不指向任何有效地址
警告:未初始化的指针被称为“野指针”。我曾经在项目中因为一个野指针导致系统随机崩溃,排查了整整两天。所以,定义指针后立即初始化,是个好习惯。
10.3 取地址符 & 与解引用符 *
这两个符号是操作指针的“左右手”。
- &:取地址符,用来获取变量的地址
- *:解引用符,用来访问指针指向的变量
看个例子:
int a = 10;
int *p = &a; // &a 取出 a 的地址,赋给 p
printf("%d\n", *p); // *p 解引用,得到 p 指向的值,即 a 的值 10
*p = 20; // 通过指针修改 a 的值
printf("%d\n", a); // 输出 20
说白了,& 是“找到门牌号”,* 是“走进门里看看里面放了什么”。
小技巧:我刚开始学的时候,把 *p 读作“p指向的那个变量”,这样就不容易混淆了。
10.4 指针与数组
数组和指针的关系非常密切。数组名,本质上就是指向数组第一个元素的指针常量。
int arr[5] = {1, 2, 3, 4, 5};
int *p = arr; // 等价于 int *p = &arr[0];
这里 arr 就是 &arr[0]。所以 p 指向数组的第一个元素。
然后,你可以用指针来遍历数组:
for (int i = 0; i < 5; i++) {
printf("%d ", *(p + i)); // 等价于 p[i]
}
为什么 *(p + i) 能访问第 i 个元素?
因为指针加整数,不是简单的数值加,而是“偏移 i 个元素”。p + 1 指向下一个元素,p + 2 指向下下个,以此类推。
你想想看,数组下标访问 arr[i],编译器其实也是把它转换成 *(arr + i) 来处理的。所以数组和指针,在底层是同一套机制。
重要区别:数组名是常量指针,不能修改。比如 arr++ 是错的。但指针变量可以,比如 p++ 是合法的。
我在项目中经常用指针操作数组,尤其是在处理通信协议的数据包时。用指针偏移来解析不同字段,比下标访问更灵活,效率也更高。
知识体系图
下面这张图帮你理清本章的核心逻辑:
本章小结
- 指针就是地址,指针变量存放地址
- 定义指针时用
类型 *变量名,一定要初始化 &取地址,*解引用,两者互为逆操作- 数组名是指向首元素的指针常量,可用指针遍历数组
- 指针加减整数,按元素大小偏移,不是简单加1
嗯,指针这部分内容,理解概念只是第一步。真正掌握它,需要多写代码、多调试。我建议你亲手敲几个例子,用 printf 把地址和值都打印出来看看,感受会更直观。