计算机内存可看作连续的“存储单元”,每个单元有唯一编号(地址),用于存放数据。指针的本质是存储另一个变量地址的变量——简单来说,指针就是“地址的变量”。
比如,定义一个整型变量 int a = 10;,系统会给 a 分配一块内存(假设地址为 0x12345678),a 的值是 10,而指向 a 的指针变量 p 存储的就是 0x12345678。此时,p 被称为“指向 a 的指针”。
指针声明需指定“指向的数据类型”(决定指针解引用时的操作粒度)和“指针变量名”,语法为:
数据类型 *指针变量名; * 表示“这是一个指针”;数据类型 限定指针指向的变量类型(如 int * 指向整型,char * 指向字符型)。例:
int *p; // 声明一个指向整型的指针p
char *str; // 声明一个指向字符型的指针str 未初始化的指针是“野指针”(指向随机地址),操作它会导致程序崩溃或数据错误。必须通过以下方式初始化:
指向已存在的变量:用 &(取地址符)获取变量地址,赋值给指针。
int a = 10;
int *p = &a; // p指向a的地址,此时*p等价于a(*是解引用符,获取指针指向地址的值) 指向动态分配的内存(后续讲malloc时会提)。
用 *指针变量名 可获取指针指向地址中存储的值(即“解引用”)。
int a = 10;
int *p = &a;
printf("%d", *p); // 输出10(等价于printf("%d", a))
*p = 20; // 修改指针指向地址的值,等价于a=20
printf("%d", a); // 输出20 指针支持 +、-、++、-- 运算,但步长由指向的类型决定(即类型占用的字节数)。
int *p 指向 int(占4字节),p+1 会跳到下一个 int 的地址(地址值+4);char *q 指向 char(占1字节),q+1 地址值+1。int arr[3] = {1, 2, 3};
int *p = arr; // 数组名arr是首元素地址,等价于&arr[0]
printf("%d", *p); // 输出1(arr[0])
p++; // p指向arr[1],地址+4
printf("%d", *p); // 输出2(arr[1]) 数组名是数组首元素的地址(指针常量,不可修改指向),因此指针可灵活操作数组:
for(int *p = arr; p < arr+3; p++) printf("%d ", *p);p[i] 等价于 *(p+i)(与数组下标 arr[i] 逻辑一致)。C语言提供 malloc、free 等函数在“堆”上动态分配内存(程序结束后不自动释放),指针是操作动态内存的关键。
mallocvoid* malloc(size_t size) 分配 size 字节的内存,返回指向该内存的指针(失败返回NULL)。需用强制类型转换匹配指针类型。
int *p = (int*)malloc(3 * sizeof(int)); // 分配3个int的内存(12字节)
if(p != NULL) { // 必须检查是否分配成功
p[0] = 1; p[1] = 2; p[2] = 3; // 像数组一样使用
} free动态内存需手动释放,否则会导致“内存泄漏”(程序持续占用内存不释放)。free(p) 释放 p 指向的内存,释放后应将指针置为NULL(避免野指针)。
free(p);
p = NULL; // 防止误操作已释放的内存 int *p; *p = 10;)指向随机地址,操作会崩溃。free(p); *p = 20;)可能指向已回收的内存,导致数据错误。数组或动态内存操作时,指针超出有效范围(如 int arr[3]; int *p = arr; p[5] = 10;)会破坏其他数据,引发不可预测的错误。
NULL指针不可解引用NULL 是指针的“无效地址”(通常为0),解引用 NULL(如 *p 当 p=NULL)会直接导致程序崩溃。
指针的学习关键是“理解地址与值的映射关系”,建议多写代码练习:从简单的变量指针,到数组指针、动态内存指针,逐步体会指针的灵活性。记住:指针本身不可怕,可怕的是对内存操作的失控——规范使用,它能成为你写出高效、优雅C代码的利器。