
大家好!今天我们来聊聊C语言中一个让很多初学者“又爱又怕”的知识点——指针(Pointer)。
你可能听说过:“学不会C语言,是因为没搞懂指针。” 也可能在调试时遇到过“段错误(Segmentation Fault)”,而罪魁祸首就是指针使用不当。
别担心,今天我们从最基础的开始,一步步揭开指针的神秘面纱!
想象一下你在宿舍住,你的床位号是 305室。 别人不需要把你整个人搬过去,只要知道“305”这个地址,就能找到你。
在计算机内存中,每个变量也都住在某个“房间”里,这个“房间号”就是它的内存地址。
👉 而指针,就是用来保存这个“房间号”(地址)的变量。
✅ 简单说: 指针是一个存储内存地址的变量,通过它可以找到并操作对应的数据。
举个例子
int a = 100;
int *p = &a; // p 是指向 a 的指针,&a 是取 a 的地址这里:
a 是一个整型变量,值是 100。&a 是变量 a 在内存中的地址(比如 0x7ffd1234)。p 是一个指针变量,它存储的是 &a 这个地址。*p 表示“通过指针 p 访问它指向的内容”,也就是 100。int *p:声明一个指向 int 类型的指针 p&每个变量在内存中都有一个“门牌号”,我们用 & 来获取它。
int age = 20;
printf("age 的地址是:%p\n", &age); // %p 用于打印地址运行一下

其中这里的 0000009FBF57FA14 就是 age 在内存中的“门牌号”。也就是内存地址
我们想用一个变量来保存这个地址,就要声明一个指针变量。
语法:数据类型 *指针名;
int *p1; // 指向整型的指针
char *p2; // 指向字符的指针
float *p3; // 指向浮点数的指针
double *p4; // 指向双精度浮点数的指针解读:
int *p:p 是一个指针,它将来要指向一个 int 类型的变量。p = &age:把 age 的地址存到 p 中。现在,p 就像一张“地图”,告诉你 age 在哪。
⚠️ 注意:
* 是声明的一部分,表示“这是一个指针”int* p; 或 int *p; 都可以,推荐后者更清晰*有了地图(指针),怎么找到里面的内容呢?用 * 操作符。
* 操作符 可以通过地址访问它指向的内容。
int age = 20;
int *p = &age; // p 是一个指针,存的是 age 的地址
printf("*p = %d\n", *p); // 输出 20 —— *p 就等于 age!
*p = 25; // 修改 *p 其实就是修改 age
printf("age 现在是:%d\n", age); // 输出 25注意!:这里的 * 不是乘法符号
📌 关键理解:
p 存的是地址(比如 0x7ffd...)*p 的意思就是:“去 p 指向的那个地址,把里面的值拿出来”int a = 50;
int *p = &a; // 初始化时指向 a 的地址int a = 99;
int *p = &a;
printf("%d\n", *p); // 输出 99
*p = 88; // 修改 a 的值
printf("%d\n", a); // 输出 88,a 被改变了!int *p;
*p = 100; // 危险!p 没有指向任何合法地址 → 段错误!✅ 正确做法:初始化为 NULL
int *p = NULL; // 空指针,安全
if (p != NULL) {
*p = 100; // 使用前检查
}int *p = malloc(sizeof(int));
free(p);
*p = 5; // ❌ 错误!内存已被释放✅ 修复方法:释放后设为 NULL
free(p);
p = NULL; // 避免误用int arr[3] = {1,2,3};
int *p = arr;
*(p + 100) = 999; // ❌ 越界 → 程序崩溃char c = 'A';
int *p = &c; // ❌ 警告!类型不匹配指针是有类型的!int* 只能指向 int,char* 指向 char。
是的!数组名本质上是一个指向第一个元素的指针。
int nums[5] = {10, 20, 30, 40, 50};
int *p = nums; // 等价于 p = &nums[0]下面这些写法是等价的:

🎯 结论:数组可以通过指针访问,指针也可以像数组一样使用!
指针可以加减整数,但“步长”由类型决定!
我们来用最通俗易懂的方式解释一下:指针的步长到底是什么?
一句话概括:
指针的步长,是指这个指针每次移动一个单位(比如 +1)时,在内存中实际跨越的字节数。
举个生活中的比喻
想象你在一条宿舍走廊上,每个房间住一个人:
在C语言中:
p + 1看代码:
int arr[] = {100, 200, 300};
int *p = arr;
p++; // p 指向 arr[1],地址增加了 sizeof(int) 字节(通常是4字节)不同类型指针补偿对照表

✅ 也就是说:

不是简单地“地址加1”,而是:
地址 + (1 × 该类型所占字节数)
C语言默认是传值调用,函数内无法修改外部变量。 但我们可以用指针实现传址调用,让函数能修改实参!
void swap(int *a, int *b) {
int temp = *a;
*a = *b;
*b = temp;
}
int main() {
int x = 5, y = 10;
printf("交换前:x=%d, y=%d\n", x, y);
swap(&x, &y); // 传递地址
printf("交换后:x=%d, y=%d\n", x, y);
return 0;
}运行结果:

核心思想: 通过传递地址,函数可以直接操作原始变量,而不是副本。
有时候我们需要一个指针,指向另一个指针。
int a = 100;
int *p = &a; // p 指向 a
int **pp = &p; // pp 指向 p(指针的指针)
// 访问方式:
printf("a = %d\n", a); // 直接
printf("a = %d\n", *p); // 一级解引用
printf("a = %d\n", **pp); // 二级解引用char *fruits[] = {"苹果", "香蕉", "橙子"};
// fruits 是一个数组,每个元素是一个 char* 指针
for (int i = 0; i < 3; i++) {
printf("%s ", fruits[i]);
}int arr[3] = {1, 2, 3};
int (*p)[3] = &arr; // p 指向一个包含3个int的数组
printf("%d\n", (*p)[1]); // 输出 2int add(int a, int b) { return a + b; }
int main() {
int (*func)(int, int); // 声明函数指针
func = add; // 指向 add 函数
printf("%d\n", func(3, 4)); // 输出 7
return 0;
}
指针不可怕,可怕的是不敢碰它。 只要你愿意动手画图、写代码、调试错误,指针终将成为你编程路上最锋利的武器!
下期预告:字符函数和字符串函数