【C 语言】指针学习笔记:从底层原理到实战应用

前言

指针是 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)

大会官网:https://ais.cn/u/rIf2Yf

大会时间: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 越界访问

  • 原因:指针偏移超出数组 / 内存块的范围。
  • 解决方案:严格控制指针的偏移范围,遍历数组时检查索引 / 指针边界。

四、指针的进阶应用场景

掌握基础后,指针在实际开发中有很多核心用途:

  1. 动态内存管理 :通过malloc/calloc/realloc/free实现灵活的内存分配(比如动态数组)。
  2. 函数返回多个值:利用指针参数让函数返回多个结果(替代全局变量)。
  3. 处理字符串:C 语言没有字符串类型,字符串本质是 char 数组,常用指针操作(如 strcpy、strlen 底层都是指针)。
  4. 实现数据结构:链表、栈、队列等数据结构必须依赖指针实现。

五、学习指针的建议

  1. 多画图:把内存地址和指针指向关系画出来,抽象问题变直观。
  2. 多敲代码:从简单的变量指针,到数组指针、函数指针,逐步递进练习。
  3. 调试看内存:用 VSCode、GDB 等工具调试,查看指针的地址和指向的值,理解底层逻辑。
  4. 总结错误:把踩过的指针错误(如野指针、越界)记录下来,避免重复踩坑。

总结

  1. 指针的核心是「存储内存地址的变量」,&取地址、*解引用是最核心的两个操作。
  2. 指针的核心价值:实现传址调用、动态内存管理、高效操作数组 / 字符串。
  3. 新手避坑关键:指针必须初始化、动态内存释放后置空、避免越界访问。

指针不是 "花里胡哨" 的语法,而是 C 语言高效、灵活的核心。理解指针的底层逻辑,才能真正掌握 C 语言的精髓。如果这篇笔记对你有帮助,欢迎点赞、收藏,也欢迎在评论区交流你的学习问题~

相关推荐
小范自学编程2 小时前
算法训练营 Day38 - 动态规划part07
算法·动态规划
自动化和Linux2 小时前
C语言_scanf(),strlen(),size()的特性和各自的区别
c语言·开发语言
努力努力再努力...2 小时前
学习Multipath多路径
学习
小郝 小郝2 小时前
51 与32 单片机LED控制详解
c语言·开发语言·经验分享·学习·51单片机
星空露珠2 小时前
迷你世界UGC3.0脚本Wiki全局函数
开发语言·数据库·算法·游戏·lua
小王不爱笑1322 小时前
排序算法 Java
数据结构·算法·排序算法
无敌憨憨大王2 小时前
二叉树的最短路径长度(BFS+DFS)
算法·深度优先·宽度优先
金山几座2 小时前
C#学习记录-类(Class)
开发语言·学习·c#
CHENJIAMIAN PRO2 小时前
3D Tiles 2.0 技术审查整理笔记
笔记·3d