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 的值是一样的,都是地址。*pa 的值也是一样的,都是那个地址里存的数据。这就是指针的对称美。

指针的常见误区

误区 说明 正确做法
混淆 &* 以为 & 是取值,* 是取地址 & 取地址,* 取内容
未初始化就使用 指针指向随机地址,操作危险 初始化或赋 NULL
类型不匹配 int * 指向 double 变量 指针类型必须与指向类型一致

避坑指南:我曾经在项目里犯过一个低级错误——把一个 int * 指向了一个 float 变量。编译没报错,但运行时数据全乱了。因为解引用时,编译器按 int 的大小去读内存,而实际存的是 float 的字节布局。嗯,调试了一下午才发现。

知识体系图

下面这张图帮你理清指针的核心逻辑:

指针核心知识体系 指针 概念与本质 存放地址的变量 定义与初始化 int *p = &a; 或 NULL & 与 * 操作符 取地址 / 解引用 本质:地址的容器 不存数据,存位置 类型 *变量名; 必须初始化或赋NULL &:取变量地址 *:通过地址访问数据

指针这东西,说白了就是一层间接性。你直接操作变量,是一步到位;通过指针操作变量,是绕了个弯。但这个弯,给了你极大的灵活性——动态内存分配、数组操作、函数参数传递……都离不开它。

我建议你学指针的时候,多画图。把内存想象成一排带编号的格子,指针就是一张写着格子编号的纸条。这样理解起来,比死记硬背语法要快得多。

个人经验:刚开始学指针,别急着搞复杂的链表、树。先把 &* 这两个操作符练熟,写几个小程序感受一下「通过地址改值」的过程。基础打牢了,后面什么二级指针、函数指针都不在话下。


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