首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >C语言入门指南:指针

C语言入门指南:指针

作者头像
给东岸来杯冷咖啡
发布2026-01-12 20:12:34
发布2026-01-12 20:12:34
2410
举报

大家好!今天我们来聊聊C语言中一个让很多初学者“又爱又怕”的知识点——指针(Pointer)

你可能听说过:“学不会C语言,是因为没搞懂指针。” 也可能在调试时遇到过“段错误(Segmentation Fault)”,而罪魁祸首就是指针使用不当。

别担心,今天我们从最基础的开始,一步步揭开指针的神秘面纱!

一、指针到底是什么?——先打个比方

想象一下你在宿舍住,你的床位号是 305室。 别人不需要把你整个人搬过去,只要知道“305”这个地址,就能找到你。

在计算机内存中,每个变量也都住在某个“房间”里,这个“房间号”就是它的内存地址

👉 而指针,就是用来保存这个“房间号”(地址)的变量

✅ 简单说: 指针是一个存储内存地址的变量,通过它可以找到并操作对应的数据。

1、基本语法与核心符号

举个例子

代码语言:javascript
复制
int a = 100;
int *p = &a;  // p 是指向 a 的指针,&a 是取 a 的地址

这里:

  • a 是一个整型变量,值是 100。
  • &a 是变量 a 在内存中的地址(比如 0x7ffd1234)。
  • p 是一个指针变量,它存储的是 &a 这个地址。
  • *p 表示“通过指针 p 访问它指向的内容”,也就是 100。
  • int *p:声明一个指向 int 类型的指针 p

1.1 取地址符:&

每个变量在内存中都有一个“门牌号”,我们用 & 来获取它。

代码语言:javascript
复制
int age = 20;
printf("age 的地址是:%p\n", &age);  // %p 用于打印地址

运行一下

其中这里的 0000009FBF57FA14 就是 age 在内存中的“门牌号”。也就是内存地址

1.2 声明指针变量

我们想用一个变量来保存这个地址,就要声明一个指针变量

语法:数据类型 *指针名;

代码语言:javascript
复制
int    *p1;   // 指向整型的指针
char   *p2;   // 指向字符的指针
float  *p3;   // 指向浮点数的指针
double *p4;   // 指向双精度浮点数的指针

解读:

  • int *p:p 是一个指针,它将来要指向一个 int 类型的变量。
  • p = &age:把 age 的地址存到 p 中。

现在,p 就像一张“地图”,告诉你 age 在哪。

⚠️ 注意:

  • * 是声明的一部分,表示“这是一个指针”
  • 写成 int* p;int *p; 都可以,推荐后者更清晰
1.3 解引用符(取值运算符):*

有了地图(指针),怎么找到里面的内容呢?用 * 操作符。

* 操作符 可以通过地址访问它指向的内容。

代码语言:javascript
复制
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 指向的那个地址,把里面的值拿出来”

2.初始化指针

代码语言:javascript
复制
int a = 50;
int *p = &a;  // 初始化时指向 a 的地址

3.解引用指针

代码语言:javascript
复制
int a = 99;
int *p = &a;
printf("%d\n", *p);  // 输出 99
*p = 88;             // 修改 a 的值
printf("%d\n", a);   // 输出 88,a 被改变了!

三、指针常见的错误

❌ 错误1:未初始化就使用(野指针)

代码语言:javascript
复制
int *p;
*p = 100;  // 危险!p 没有指向任何合法地址 → 段错误!

✅ 正确做法:初始化为 NULL

代码语言:javascript
复制
int *p = NULL;  // 空指针,安全
if (p != NULL) {
    *p = 100;  // 使用前检查
}

❌ 错误2:使用已释放的内存(悬空指针)

代码语言:javascript
复制
int *p = malloc(sizeof(int));
free(p);
*p = 5;  // ❌ 错误!内存已被释放

✅ 修复方法:释放后设为 NULL

代码语言:javascript
复制
free(p);
p = NULL;  // 避免误用

❌ 错误3:越界访问

代码语言:javascript
复制
int arr[3] = {1,2,3};
int *p = arr;
*(p + 100) = 999;  // ❌ 越界 → 程序崩溃

❌ 错误4:类型不匹配

代码语言:javascript
复制
char c = 'A';
int *p = &c;   // ❌ 警告!类型不匹配

指针是有类型的!int* 只能指向 intchar* 指向 char

四、指针和数组的关系

1. 数组名就是指针?

是的!数组名本质上是一个指向第一个元素的指针

代码语言:javascript
复制
int nums[5] = {10, 20, 30, 40, 50};
int *p = nums;  // 等价于 p = &nums[0]

下面这些写法是等价的:

🎯 结论:数组可以通过指针访问,指针也可以像数组一样使用!

2. 指针算术(Pointer Arithmetic)

指针可以加减整数,但“步长”由类型决定!

我们来用最通俗易懂的方式解释一下:指针的步长到底是什么?

一句话概括:

指针的步长,是指这个指针每次移动一个单位(比如 +1)时,在内存中实际跨越的字节数

举个生活中的比喻

想象你在一条宿舍走廊上,每个房间住一个人:

  • 如果每间房宽 1米,你从301走到302,就是向前走了1米。
  • 但如果每间房宽 4米,那你走一步就是4米。

在C语言中:

  • “你” 就是指针
  • “房间大小” 就是数据类型的大小
  • “走一步” 就是 p + 1
  • “实际走了多远” 就是步长

看代码:

代码语言:javascript
复制
   int arr[] = {100, 200, 300};
   int *p = arr;

   p++;  // p 指向 arr[1],地址增加了 sizeof(int) 字节(通常是4字节)

不同类型指针补偿对照表

✅ 也就是说:

不是简单地“地址加1”,而是:

地址 + (1 × 该类型所占字节数)

五、指针与函数:传址调用

C语言默认是传值调用,函数内无法修改外部变量。 但我们可以用指针实现传址调用,让函数能修改实参!

示例:交换两个数
代码语言:javascript
复制
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;
}

运行结果:

核心思想: 通过传递地址,函数可以直接操作原始变量,而不是副本。

六、多级指针(指针的指针)

有时候我们需要一个指针,指向另一个指针。

代码语言:javascript
复制
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);     // 二级解引用

七、高级指针类型(了解即可)

1. 指针数组:多个指针组成的数组

代码语言:javascript
复制
char *fruits[] = {"苹果", "香蕉", "橙子"};
// fruits 是一个数组,每个元素是一个 char* 指针
for (int i = 0; i < 3; i++) {
    printf("%s ", fruits[i]);
}

2. 数组指针:指向整个数组的指针

代码语言:javascript
复制
int arr[3] = {1, 2, 3};
int (*p)[3] = &arr;  // p 指向一个包含3个int的数组
printf("%d\n", (*p)[1]); // 输出 2

3. 函数指针:指向函数的指针(很酷!)

代码语言:javascript
复制
int 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;
}

八、总结

🌈 最后一句话

指针不可怕,可怕的是不敢碰它。 只要你愿意动手画图、写代码、调试错误,指针终将成为你编程路上最锋利的武器!

下期预告:字符函数和字符串函数

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2025-10-12,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、指针到底是什么?——先打个比方
    • 1、基本语法与核心符号
      • 1.1 取地址符:&
      • 1.2 声明指针变量
      • 1.3 解引用符(取值运算符):*
    • 2.初始化指针
    • 3.解引用指针
  • 三、指针常见的错误
    • ❌ 错误1:未初始化就使用(野指针)
    • ❌ 错误2:使用已释放的内存(悬空指针)
    • ❌ 错误3:越界访问
    • ❌ 错误4:类型不匹配
  • 四、指针和数组的关系
    • 1. 数组名就是指针?
    • 2. 指针算术(Pointer Arithmetic)
  • 五、指针与函数:传址调用
    • 示例:交换两个数
  • 六、多级指针(指针的指针)
  • 七、高级指针类型(了解即可)
    • 1. 指针数组:多个指针组成的数组
    • 2. 数组指针:指向整个数组的指针
    • 3. 函数指针:指向函数的指针(很酷!)
  • 八、总结
    • 🌈 最后一句话
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档