前言
指针是 C 语言的灵魂,也是新手学习的第一道坎。很多同学觉得指针抽象、难理解,要么死记硬背语法,要么一用就出野指针、空指针错误。这篇笔记结合我自己的学习经验,从底层逻辑到实际应用,把指针讲透,帮你真正理解指针的本质。
目录
[1.1 核心定义](#1.1 核心定义)
[1.2 内存可视化理解](#1.2 内存可视化理解)
[1.3 关键符号说明](#1.3 关键符号说明)
[2.1 指针的定义与初始化](#2.1 指针的定义与初始化)
[2.2 指针与数组(核心关联)](#2.2 指针与数组(核心关联))
[2.3 指针作为函数参数(传址调用)](#2.3 指针作为函数参数(传址调用))
[3.1 野指针(最常见)](#3.1 野指针(最常见))
[3.2 悬空指针](#3.2 悬空指针)
[3.3 越界访问](#3.3 越界访问)
论文投稿:
2026年电子器件与智能控制国际学术会议(EDIC 2026)
大会时间:2026年3月27-29日
大会地点:中国-厦门

一、指针的本质:到底什么是指针?
1.1 核心定义
指针的本质是变量,但它是一种特殊的变量:
- 普通变量存储的是「数据值」(比如 int 变量存 10、char 变量存 'a')
- 指针变量存储的是「内存地址」(即其他变量在内存中的编号)
1.2 内存可视化理解
假设我们在代码中定义:
#include <stdio.h>
int main() {
// 普通变量:存储值100
int a = 100;
// 指针变量:存储a的内存地址
int *p = &a;
printf("变量a的值:%d\n", a);
printf("变量a的地址:%p\n", &a);
printf("指针p的值:%p\n", p); // p存储的是a的地址
printf("通过p访问a的值:%d\n", *p); // *p表示"解引用",取地址对应的值
return 0;
}
运行结果示例(地址是随机的,仅作参考):
变量a的值:100
变量a的地址:0x7ffee3b5c8ac
指针p的值:0x7ffee3b5c8ac
通过p访问a的值:100
可以用一张图理解:
内存地址 存储内容
0x7ffee3b5c8ac 100 <-- 变量a
0x7ffee3b5c8b0 0x7ffee3b5c8ac <-- 指针变量p
1.3 关键符号说明
| 符号 | 作用 | 示例 |
|---|---|---|
& |
取地址符:获取变量的内存地址 | &a 得到变量 a 的地址 |
* |
解引用符:通过地址访问对应的值 | *p 得到 p 指向地址的内容 |
* |
定义指针时:表示变量是指针类型 | int *p 定义 int 类型指针 p |
二、指针的基础用法
2.1 指针的定义与初始化
错误示范:直接定义指针但不初始化(野指针)
// 危险!p是野指针,指向随机内存地址
int *p;
// 大概率崩溃:访问非法内存
*p = 10;
正确示范:初始化指针(3 种常见方式)
#include <stdio.h>
#include <stdlib.h>
int main() {
// 方式1:指向已有变量
int a = 10;
int *p1 = &a;
// 方式2:指向动态内存(malloc)
int *p2 = (int *)malloc(sizeof(int));
if (p2 != NULL) { // 必须检查malloc是否成功
*p2 = 20;
}
// 方式3:初始化为空指针(避免野指针)
int *p3 = NULL;
printf("p1指向的值:%d\n", *p1);
printf("p2指向的值:%d\n", *p2);
// 释放动态内存
free(p2);
p2 = NULL; // 释放后置空,避免悬空指针
return 0;
}
2.2 指针与数组(核心关联)
数组名本质上是「指向数组第一个元素的常量指针」,这是指针最常用的场景之一。
示例:指针遍历数组
#include <stdio.h>
int main() {
int arr[5] = {1,2,3,4,5};
// arr 等价于 &arr[0]
int *p = arr;
// 方式1:指针偏移(推荐)
for (int i = 0; i < 5; i++) {
// p+i 表示偏移i个int大小的地址
printf("%d ", *(p+i));
}
printf("\n");
// 方式2:指针自增
p = arr; // 重置指针位置
while (p < arr + 5) {
printf("%d ", *p++);
}
return 0;
}
运行结果:
1 2 3 4 5
1 2 3 4 5
2.3 指针作为函数参数(传址调用)
C 语言函数参数默认是「传值调用」(拷贝一份值传入),如果想修改原变量的值,必须用指针(传址调用)。
示例:交换两个整数
#include <stdio.h>
// 错误:传值调用,仅修改拷贝的临时变量
void swap_error(int x, int y) {
int temp = x;
x = y;
y = temp;
}
// 正确:传址调用,修改原变量
void swap_correct(int *x, int *y) {
int temp = *x;
*x = *y;
*y = temp;
}
int main() {
int a = 10, b = 20;
swap_error(a, b);
printf("swap_error后:a=%d, b=%d\n", a, b); // a=10, b=20
swap_correct(&a, &b);
printf("swap_correct后:a=%d, b=%d\n", a, b); // a=20, b=10
return 0;
}
三、指针常见坑与避坑技巧
新手用指针最容易踩坑,这里总结 3 个高频错误和解决方案:
3.1 野指针(最常见)
- 原因:指针定义后未初始化,指向随机内存地址。
- 解决方案:定义指针时立即初始化(要么指向有效变量,要么置为 NULL)。
3.2 悬空指针
- 原因:指针指向的内存被释放后,指针未置空,仍指向已释放的地址。
- 解决方案:free 动态内存后,立即将指针置为 NULL。
3.3 越界访问
- 原因:指针偏移超出数组 / 内存块的范围。
- 解决方案:严格控制指针的偏移范围,遍历数组时检查索引 / 指针边界。
四、指针的进阶应用场景
掌握基础后,指针在实际开发中有很多核心用途:
- 动态内存管理 :通过
malloc/calloc/realloc/free实现灵活的内存分配(比如动态数组)。 - 函数返回多个值:利用指针参数让函数返回多个结果(替代全局变量)。
- 处理字符串:C 语言没有字符串类型,字符串本质是 char 数组,常用指针操作(如 strcpy、strlen 底层都是指针)。
- 实现数据结构:链表、栈、队列等数据结构必须依赖指针实现。
五、学习指针的建议
- 多画图:把内存地址和指针指向关系画出来,抽象问题变直观。
- 多敲代码:从简单的变量指针,到数组指针、函数指针,逐步递进练习。
- 调试看内存:用 VSCode、GDB 等工具调试,查看指针的地址和指向的值,理解底层逻辑。
- 总结错误:把踩过的指针错误(如野指针、越界)记录下来,避免重复踩坑。
总结
- 指针的核心是「存储内存地址的变量」,
&取地址、*解引用是最核心的两个操作。 - 指针的核心价值:实现传址调用、动态内存管理、高效操作数组 / 字符串。
- 新手避坑关键:指针必须初始化、动态内存释放后置空、避免越界访问。
指针不是 "花里胡哨" 的语法,而是 C 语言高效、灵活的核心。理解指针的底层逻辑,才能真正掌握 C 语言的精髓。如果这篇笔记对你有帮助,欢迎点赞、收藏,也欢迎在评论区交流你的学习问题~