【C语言进阶】打通指针与函数、动态内存与结构体
前言如果说 C 语言的语法是招式那么指针、内存管理和结构体就是 C 语言的内功心法。在后端开发和底层系统编程中很多代码一跑就崩段错误一查全是野指针和内存泄漏。那就需要用底层工程师的视角把这三大核心模块的运转逻辑一次性拆解明白一、 指针与函数的极限拉扯函数和指针结合在一起诞生了 C 语言中最灵活、也最容易让人头晕的语法。我们要重点区分三个核心概念1. 传值 vs 传址指针作为函数参数C 语言的函数参数传递默认是“值拷贝”。如果你把一个几百兆的巨大结构体直接传给函数系统会在栈区吭哧吭哧地复制一份一模一样的数据极其消耗内存和 CPU。极客做法传入数据的地址指针。系统只需要传递一个 8 字节64位系统下的指针函数内部直接顺着地址去操作原始数据效率拉满。这也是为什么在使用scanf(%d, num)录入基本数据类型时必须加取地址符的原因。2. 指针函数返回指针的函数本质上是一个函数只是它执行完之后吐出来的结果是一个内存地址指针。Cint* getArray() {// 致命错误返回局部变量的地址int temp 10;return temp;}避坑指南绝对不要返回生存在栈区的局部变量的地址函数一旦执行完毕栈帧被销毁你返回的那个地址就变成了“野指针”谁去访问谁就崩溃。正确的做法是返回全局变量、静态变量static或者是在堆区动态malloc出来的内存地址。3. 函数指针指向函数的指针本质上是一个指针它指向的不是普通变量而是一段可执行代码函数的首地址。声明语法int (*func_ptr)(int, int);注意括号的优先级这代表它是一个指针指向参数为两个 int返回值为 int 的函数。终极应用回调函数Callback。像 C 语言标准库中的qsort排序函数就是通过接收你传入的“函数指针”来决定到底是按升序还是降序排列实现了极致的代码解耦。二、 动态内存管理堆区生存指南栈区Stack虽然快但空间极小且由系统严格管控。想要自由掌控大规模的数据就必须去堆区Heap“开疆拓土”。1. 堆区“四大金刚”这四个核心函数全部驻扎在stdlib.h头文件里malloc(size)纯粹的苦力。只要你报出需要的字节数它就在堆区划出一块地。注意里面的数据是未初始化的脏数据。calloc(n, size)贴心管家。不仅按你的要求分配n个大小为size的坑位还会自动把所有内存清零。realloc(ptr, new_size)扩容/缩容神器。当你发现之前申请的数组不够用了它负责帮你无缝扩大地盘。如果原地扩容失败它会自动去远方找块更大的地把老数据搬过去然后销毁老房子。free(ptr)无情拆迁队。用完内存必须调用它归还系统。2. 内存泄漏与悬空指针野指针这是 C/C 程序员一生的宿敌。内存泄漏 (Memory Leak)malloc出来的内存用完后忘记free。就像你在图书馆借了书永远不还久而久之系统的内存就会被耗干OOM。悬空指针 (Dangling Pointer)c intp (int)malloc(100); free(p); // 此时 p 就是悬空指针p 里面依然存着那个地址但那块地已经不属于你了。 p NULL; // ✅ 防坑free 之后必须立刻将指针置空## 三、 结构体自定义数据类型的艺术基本类型int, char, float只能描述单一属性而在真实的业务系统中比如学生管理系统、银行账户系统我们需要把多种属性打包在一起这就是结构体struct。### 1. 结构体与 typedef 为了避免每次定义变量都要写冗长的 struct 关键字工程中最标准的写法是配合 typedef ctypedef struct Student {char name[20];int age;float score; } Student;// 此时 Student 就变成了一个像 int 一样好用的数据类型别名 Student stu1;// 直接使用干净利落2. 结构体指针与访问符.与-如果你手里拿着的是结构体实体变量就用点号.来访问成员stu1.age 18;如果你手里拿着的是结构体指针必须用箭头-顺着网线过去访问stu_ptr-age 18;3. 后端高频考点内存对齐 (Memory Alignment)这是结构体中最爱考的底层逻辑。结构体在内存中的总大小绝不是简单地把所有成员的大小加起来struct Node {char a; // 1 字节int b; // 4 字节char c; // 1 字节}; // sizeof(struct Node) 的结果是多少答案是 12为了配合 CPU 的高效读取CPU 喜欢每次读取 4 字节或 8 字节的整数倍编译器会在变量之间强行塞入毫无意义的“空气字节Padding”来进行占位对齐。这就是为什么在设计底层网络协议的结构体时我们必须要精打细算、合理排列成员顺序甚至使用#pragma pack(1)来取消对齐以节省宝贵的网络带宽。结语指针、内存和结构体三者常常交织在一起比如动态分配结构体数组、链表的指针嵌套。