1. 引言
在 C 语言的学习过程中,指针与结构体是两个绕不开的核心概念。指针让我们能够直接操作内存地址,而结构体则允许我们将不同类型的数据组合成一个整体。当这两者结合时,便诞生了 C 语言中最强大、也最让初学者头疼的用法——结构体指针。
本文将带你从基础出发,逐步深入理解指针与结构体的关系,掌握结构体指针的定义、初始化、访问方式,以及它在链表、动态内存分配等场景中的典型应用。
2. 结构体基础回顾
在讨论指针之前,我们先快速回顾一下结构体的基本用法。
2.1 结构体的定义
structStudent{charname[50];intage;floatscore;};2.2 结构体变量的声明与初始化
structStudentstu1={"张三",20,89.5};structStudentstu2;2.3 通过点运算符访问成员
printf("姓名:%s\n",stu1.name);printf("年龄:%d\n",stu1.age);printf("成绩:%.1f\n",stu1.score);3. 指针基础回顾
指针是存储变量地址的变量。理解指针,首先要理解地址与取地址运算符。
3.1 指针的声明与赋值
inta=10;int*p=&a;// p 存储变量 a 的地址3.2 解引用运算符
printf("%d\n",*p);// 输出 10,*p 表示 p 指向的变量的值4. 结构体指针
结构体指针,就是指向结构体变量的指针。它存储的是结构体变量在内存中的起始地址。
4.1 结构体指针的声明
structStudent*pStu;4.2 结构体指针的初始化
structStudentstu1={"张三",20,89.5};structStudent*pStu=&stu1;4.3 通过指针访问结构体成员
访问结构体指针所指向的成员,有两种方式:
方式一:先解引用,再使用点运算符
(*pStu).age=21;printf("年龄:%d\n",(*pStu).age);方式二:使用箭头运算符(推荐)
pStu->age=21;printf("年龄:%d\n",pStu->age);注意:
pStu->age等价于(*pStu).age。箭头运算符->是 C 语言专门为结构体指针提供的语法糖,书写更简洁,也更不容易出错。
5. 结构体指针作为函数参数
将结构体指针作为函数参数传递,可以避免结构体整体拷贝带来的性能开销,同时允许函数直接修改原结构体的内容。
5.1 传递结构体变量(值传递)
voidprintStudent(structStudentstu){printf("姓名:%s\n",stu.name);}这种方式会拷贝整个结构体,当结构体很大时效率较低。
5.2 传递结构体指针(引用传递)
voidupdateStudent(structStudent*pStu){pStu->age+=1;// 直接修改原结构体}intmain(){structStudentstu1={"张三",20,89.5};updateStudent(&stu1);printf("年龄:%d\n",stu1.age);// 输出 21return0;}5.3 传递 const 指针保护数据
如果函数只需要读取结构体内容而不需要修改,可以加上const修饰,防止意外修改:
voidprintStudent(conststructStudent*pStu){printf("姓名:%s\n",pStu->name);}6. 结构体指针与动态内存分配
结构体指针最常见的应用之一,就是配合malloc在堆上动态分配结构体内存。
#include<stdio.h>#include<stdlib.h>#include<string.h>structStudent{charname[50];intage;};intmain(){structStudent*pStu=(structStudent*)malloc(sizeof(structStudent));if(pStu==NULL){printf("内存分配失败\n");return1;}strcpy(pStu->name,"李四");pStu->age=22;printf("姓名:%s,年龄:%d\n",pStu->name,pStu->age);free(pStu);// 释放内存return0;}注意:使用
malloc分配的内存必须用free释放,否则会造成内存泄漏。
7. 结构体指针数组
结构体指针数组,即数组的每个元素都是一个指向结构体的指针。这在管理多个动态分配的结构体时非常有用。
structStudent*students[3];students[0]=(structStudent*)malloc(sizeof(structStudent));students[1]=(structStudent*)malloc(sizeof(structStudent));students[2]=(structStudent*)malloc(sizeof(structStudent));// 使用students[0]->age=20;students[1]->age=21;students[2]->age=22;// 释放for(inti=0;i<3;i++){free(students[i]);}8. 结构体指针与链表
链表是结构体指针最经典的应用场景。每个节点是一个结构体,其中包含一个指向下一个节点的指针。
structNode{intdata;structNode*next;};8.1 创建链表节点
structNode*createNode(intdata){structNode*newNode=(structNode*)malloc(sizeof(structNode));if(newNode==NULL){returnNULL;}newNode->data=data;newNode->next=NULL;returnnewNode;}8.2 在链表头部插入节点
voidinsertAtHead(structNode**head,intdata){structNode*newNode=createNode(data);if(newNode==NULL)return;newNode->next=*head;*head=newNode;}注意:这里使用了二级指针
struct Node **head,因为我们需要修改头指针本身的值。
8.3 遍历链表
voidprintList(structNode*head){structNode*current=head;while(current!=NULL){printf("%d -> ",current->data);current=current->next;}printf("NULL\n");}9. 常见错误与注意事项
9.1 未初始化指针就使用
structStudent*pStu;// 未初始化,指向未知地址pStu->age=20;// 错误!野指针正确做法:要么指向一个已存在的结构体变量,要么用malloc分配内存。
9.2 忘记释放内存
structStudent*pStu=(structStudent*)malloc(sizeof(structStudent));// 使用完后忘记 free(pStu);这会导致内存泄漏。务必在不再使用时调用free。
9.3 释放后继续使用(悬空指针)
free(pStu);pStu->age=20;// 错误!pStu 已成为悬空指针释放后应将指针置为NULL:
free(pStu);pStu=NULL;9.4 混淆->与.
- 结构体变量用
.访问成员:stu.age - 结构体指针用
->访问成员:pStu->age
10. 总结
指针与结构体的结合是 C 语言进阶的重要里程碑。通过本文的学习,我们掌握了:
- 结构体指针的声明、初始化与成员访问(
->运算符) - 结构体指针作为函数参数的高效传参方式
- 结构体指针配合
malloc进行动态内存分配 - 结构体指针数组与链表的典型应用
- 常见错误与规避方法
掌握这些内容后,你就能更自如地编写高效、灵活的 C 程序,为后续学习数据结构与算法打下坚实基础。