第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;  // 指向双精度浮点型数据的指针

注意那个星号 *,它告诉编译器:这是一个指针变量,不是普通变量。

定义之后,一定要初始化。否则指针的值是随机的,乱用会出大问题。

初始化方式有两种:

  1. 用已有变量的地址初始化
  2. 赋值为 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++ 是合法的。

我在项目中经常用指针操作数组,尤其是在处理通信协议的数据包时。用指针偏移来解析不同字段,比下标访问更灵活,效率也更高。

知识体系图

下面这张图帮你理清本章的核心逻辑:

指针 指针 = 地址 定义: 类型 *变量名; & 取地址 * 解引用 数组名 = 指向首元素的指针常量 p + i → 偏移 i 个元素 → *(p+i) 访问元素 ⚠ 野指针、数组名不可修改

本章小结

  • 指针就是地址,指针变量存放地址
  • 定义指针时用 类型 *变量名,一定要初始化
  • & 取地址,* 解引用,两者互为逆操作
  • 数组名是指向首元素的指针常量,可用指针遍历数组
  • 指针加减整数,按元素大小偏移,不是简单加1

嗯,指针这部分内容,理解概念只是第一步。真正掌握它,需要多写代码、多调试。我建议你亲手敲几个例子,用 printf 把地址和值都打印出来看看,感受会更直观。


公众号:蓝海资料掘金营,微信deep3321