指针是 C/C++ 等编程语言的核心概念,本质是存储内存地址的变量。通过指针,我们可以间接操作内存中的数据,实现灵活的内存管理、函数传参、动态数据结构(如链表、树)等功能。
一、核心概念:内存地址与指针变量
-
内存地址:计算机内存被划分为一个个字节的存储单元,每个单元有唯一的编号(即地址),类似酒店房间号。例如:变量
int a = 10;会占用 4 个字节(int 默认 4 字节),系统会为这 4 个字节分配一个起始地址(如0x7ffeefbff5c4)。 -
指针变量:专门用来存储内存地址的变量,其类型对应指向数据的类型(需匹配,否则可能导致内存访问错误)。语法:
数据类型 *指针变量名;
二、指针的基本使用
1. 定义与初始化
c
运行
#include <stdio.h>
int main() {
// 普通变量
int a = 10;
// 指针变量p:存储a的地址(&为取地址符)
int *p = &a;
printf("变量a的值:%d\n", a); // 输出:10
printf("变量a的地址:%p\n", &a); // 输出:0x7ffeefbff5c4(示例地址)
printf("指针p的值(a的地址):%p\n", p); // 输出:0x7ffeefbff5c4
printf("指针p指向的值(*p解引用):%d\n", *p); // 输出:10
return 0;
}
&a:取变量 a 的地址(地址运算符)。*p:解引用运算符,通过指针 p 访问其指向的内存中的数据。
2. 指针修改目标变量的值
指针的核心用途之一:通过指针间接修改变量值(即使变量在不同作用域)。
c
运行
#include <stdio.h>
void changeValue(int *p) {
// 修改指针指向的内存值
*p = 20;
}
int main() {
int a = 10;
printf("修改前:%d\n", a); // 10
// 传递a的地址给指针参数
changeValue(&a);
printf("修改后:%d\n", a); // 20
return 0;
}
三、指针的关键特性
1. 指针的大小
指针的大小由操作系统位数决定(与指向的数据类型无关):
- 32 位系统:指针占 4 字节(地址范围 0~2³²-1)。
- 64 位系统:指针占 8 字节(地址范围 0~2⁶⁴-1)。
验证代码:
c
运行
#include <stdio.h>
int main() {
int *p1;
char *p2;
double *p3;
// 64位系统下均输出8
printf("int*大小:%zu\n", sizeof(p1));
printf("char*大小:%zu\n", sizeof(p2));
printf("double*大小:%zu\n", sizeof(p3));
return 0;
}
2. 空指针(NULL)
表示指针不指向任何有效的内存地址,避免野指针(指向随机地址的指针)导致的崩溃。
c
运行
// 定义空指针(C11后推荐用NULL或nullptr(C++))
int *p = NULL;
// 判空后再使用
if (p != NULL) {
*p = 10; // 不会执行,避免访问非法内存
}
3. 野指针(危险!)
- 定义:未初始化、指向已释放内存、越界的指针。
- 示例(禁止运行):
c
运行
// 未初始化的野指针 int *p; // 访问随机地址,可能导致程序崩溃/内存破坏 *p = 10; - 避免:
- 指针定义时初始化为 NULL;
- 动态分配内存后检查是否成功;
- 释放内存后将指针置为 NULL。
四、常见指针类型
1. 普通指针(指向基本类型)
c
运行
char ch = 'a';
char *pc = &ch;
double d = 3.14;
double *pd = &d;
2. 数组指针(指向数组)
数组名本质是数组首元素的地址(常量,不可修改):
c
运行
int arr[5] = {1,2,3,4,5};
// p指向数组首元素(等价于p = arr)
int *p = &arr[0];
// 通过指针遍历数组
for (int i = 0; i < 5; i++) {
// 等价于*(p+i) 或 arr[i]
printf("%d ", p[i]);
}
3. 函数指针(指向函数)
用于回调函数、动态调用函数:
c
运行
// 定义函数
int add(int a, int b) {
return a + b;
}
// 定义函数指针(格式:返回值类型 (*指针名)(参数列表))
int (*fp)(int, int) = add;
// 调用:输出3
printf("%d\n", fp(1,2));
4. 二级指针(指向指针的指针)
用于修改指针本身(如动态分配内存、函数传指针的指针):
c
运行
int a = 10;
// 一级指针:指向a
int *p = &a;
// 二级指针:指向p
int **pp = &p;
// 输出:10(等价于*p 或 a)
printf("%d\n", **pp);
五、指针的核心应用场景
- 函数传参(修改外部变量):如交换两个整数(必须用指针,否则仅修改形参):
c
运行
void swap(int *x, int *y) { int temp = *x; *x = *y; *y = temp; } int main() { int a = 1, b = 2; // 交换后a=2,b=1 swap(&a, &b); return 0; } - 动态内存分配(malloc/free):运行时按需分配内存(堆内存):
c
运行
// 分配10个int的内存(堆区) int *arr = (int *)malloc(10 * sizeof(int)); if (arr == NULL) { printf("内存分配失败\n"); return 1; } // 使用动态数组 for (int i = 0; i < 10; i++) { arr[i] = i; } // 释放内存(避免内存泄漏) free(arr); // 置空,避免野指针 arr = NULL; - 实现复杂数据结构:链表、二叉树、栈、队列等均依赖指针实现节点间的关联。
- 提高程序效率:传递大对象(如结构体)时,传递指针仅需拷贝 8 字节(64 位),而非整个对象,减少内存拷贝开销。
六、指针的常见误区
- 混淆
*的两种含义:- 定义时:
int *p表示 p 是指针变量; - 使用时:
*p表示解引用,访问指向的数据。
- 定义时:
- 数组名与指针的区别:数组名是常量地址(不可修改,如
arr++报错),指针变量是变量(可修改,如p++合法)。 - 越界访问:指针访问超出数组 / 内存块的范围,导致内存破坏(如
arr[10]当数组长度为 5 时)。 - 内存泄漏:动态分配的内存未释放(
malloc后未free),导致系统资源耗尽。
总结
指针是一把 “双刃剑”:
- 优势:灵活操作内存、提升程序效率、实现复杂逻辑;
- 风险:易出现野指针、内存越界、内存泄漏等问题,需严格遵循规范。
核心口诀:
&取地址,*解引用;- 指针初始化,用完置 NULL;
- 动态内存要释放,越界访问不可取。
717

被折叠的 条评论
为什么被折叠?



