C结构体基础(二)
C结构体基础二结构体数组结构体指针指向结构体数组的指针结构体作为函数参数1. 结构体作为函数参数2. 结构体数组的动态分配与扩容结构体数组一个结构体变量可以保存一个学生的多项信息这些信息可以具有不同的数据类型同一个结构体类型又可以定义出多个结构体变量用来保存不同学生的信息。数组正好适合存放相同类型的数据因此可以把这些同类型的结构体变量组织到一个数组中从而实现对多个学生信息的统一管理而不是零散地逐个处理。这里我们引出结构体数组。简单来讲结构体数组就是以结构体类型作为元素类型的数组数组中的每一个元素都是一个结构体变量。例如struct Student students[3];就定义了一个包含 3 个Student结构体变量的数组可以通过students[0]、students[1]、students[2]分别访问这些结构体变量再使用.运算符访问其中的成员。这可以作为一道结构体数组的练习题给定 n 名学生每名学生包含姓名和成绩两个信息请输出成绩最高的学生姓名和成绩。参考示例#includestdio.hstructStudent{charname[50];intscore;};intmain(){intn,max,j0;scanf(%d,n);structStudentstus[n];for(inti0;in;i){scanf(%s,stus[i].name);scanf(%d,stus[i].score);}maxstus[0].score;for(inti0;in;i){if(stus[i].scoremax){ji;maxstus[i].score;}}printf(name:%s score:%d,stus[j].name,max);return0;}下面结合这个例子简要梳理结构体数组的核心操作定义结构体类型Student中保留姓名name和成绩score两个成员。创建结构体数组struct Student stus[n];定义一个长度为n的数组stus[0]到stus[n-1]每个元素都是结构体变量。输入数据循环中使用stus[i].name、stus[i].score分别访问第i个学生的姓名和成绩。查找最高分遍历数组通过stus[i].score比较成绩记录最高分及对应下标。输出结果最后用stus[j].name和max输出最高分学生信息。可见操作结构体数组的关键是先定位到数组元素再用.访问其中的成员。同时也可以这样写#includestdio.hstructStudent{charname[50];intscore;};intmain(){intn,max,j0;scanf(%d,n);structStudentstus[n];for(inti0;in;i){structStudentstu;scanf(%s,stu.name);scanf(%d,stu.score);stus[i]stu;}maxstus[0].score;for(inti0;in;i){structStudentstustus[i];intScorestu.score;if(Scoremax){ji;maxScore;}}printf(name:%s score:%d,stus[j].name,max);return0;}第二种写法与第一种功能相同区别在于第一种直接访问stus[i].score进行比较第二种先通过struct Student stu stus[i];把数组元素整体赋给临时结构体变量再取出stu.score存入普通变量Score后比较。它借助了同类型结构体变量可以整体赋值的特性体现了“先取出当前元素再单独处理成员”的不同思路。结构体指针所谓结构体指针就是指向结构体变量的指针。前面我们讲过结构体变量和成员都可以取地址既然可以取地址自然也就可以定义一个指针来保存这块地址。把取出的地址交给结构体指针就可以通过这个指针间接访问结构体变量及其成员。定义结构体指针的方式与普通指针类似。例如先定义一个Student变量再定义一个指向它的指针structStudentstu;structStudent*pstu;这里p就是一个结构体指针它保存的是结构体变量stu的地址。有了结构体指针之后访问成员时通常使用-运算符p-id20260101;strcpy(p-name,张三);p-score125.5;p-id等价于(*p).id意思是先从指针p回到它所指向的结构体变量再访问其中的id成员。两者功能相同但-的写法更简洁、更常用。简单区分一下结构体变量访问成员使用.结构体指针访问成员使用-。例如假设已经定义好struct Student stu;和struct Student *p stu;那么下面的写法都是合法的stu.id20260101;// 通过结构体变量 stu 直接访问成员p-id20260102;// 通过结构体指针 p 间接访问成员(*p).id20260103;// 先解引用指针再访问成员其中stu.id是“结构体变量名 . 成员名”表示通过变量stu直接访问它里面的id成员。p-id是“结构体指针 - 成员名”表示通过指针p间接访问它指向的那个结构体的id成员。它等价于(*p).id(*p)表示先取出指针p所指向的结构体变量然后再用.访问成员。需要注意的是使用-前指针p必须已经指向一个有效的结构体变量如果指针没有初始化直接写p-id就会发生错误。指向结构体数组的指针前面我们学习的结构体指针通常用于指向一个单独的结构体变量。其实结构体指针也可以指向结构体数组用来保存结构体数组中某个元素的地址。由于数组名本身表示数组首元素的地址因此结构体数组与结构体指针可以很好地配合使用。下面的例子先用下标方式遍历结构体数组这是最常规的写法。#includestdio.hstructStudent{charname[10];intscore;}stus[3]{{张三,120},{李四,125},{王五,115}};intmain(){for(inti0;i3;i){printf(姓名%s分数%d\n,stus[i].name,stus[i].score);}return0;}那么能不能改用指针来实现同样的遍历功能呢答案是可以的。下面先看一种初学者较容易理解的写法。初学者写法先通过下标i找到当前元素再用p stus[i];让指针指向它。#includestdio.hstructStudent{charname[10];intscore;}stus[3]{{张三,120},{李四,125},{王五,115}};intmain(){structStudent*p;for(inti0;i3;i){pstus[i];printf(姓名%s分数%d\n,p-name,(*p).score);}return0;}更简洁的写法直接让指针从数组首地址开始向后移动一次访问一个元素。#includestdio.hstructStudent{charname[10];intscore;}stus[3]{{张三,120},{李四,125},{王五,115}};intmain(){for(structStudent*pstus;pstus3;p){printf(姓名%s分数%d\n,p-name,(*p).score);}return0;}观察三种写法可以发现它们的输出结果完全相同区别只在于访问数组元素的方式。第一种使用下标stus[i]第二种先把下标对应的地址交给指针p再通过p-name、(*p).score间接访问第三种则直接利用“数组名是首元素地址”这一特点让指针p从stus开始逐步后移直到遍历完所有元素。其中p并不是让地址简单地加1而是按一个struct Student元素的大小向后移动一格这正体现了结构体数组在内存中连续存放的特点。相比前两种写法第三种不需要额外维护下标使用指针遍历结构体数组时更简洁、更常用。结构体作为函数参数结构体变量和结构体变量的指针都可以作为函数参数。当形参是结构体变量时函数内部得到的是实参的一份副本修改副本不会影响原数据当形参是结构体指针或结构体数组名时函数内部通过地址直接操作原数据既可以修改实参又避免复制整个结构体。下面以一个学生管理系统为例重点讲解结构体作为参数传入以及结构体数组的动态内存分配。#includestdio.h#includestdlib.hintupSize(intsize){if(size0){size;}returnsize;}intcheckIndex(intsize0){intindex;do{printf(请输入一个编号0~%d\n,size0-1);intindex;scanf(%d,index);if(index0||indexsize0-1){printf(输入的编号不存在请重新输入\n);}else{returnindex;}}while(1);}structStudent{charname[50];charstuID[50];intage;intscore;};voidshowInfo(structStudentstu){printf(【姓名%s 学号%s 学分%d 年龄%d】\n,stu.name,stu.stuID,stu.score,stu.age);}structStudent*addStu(int*size,structStudentstuList[],int*len){printf( 请输入姓名 学号 学分 年龄\n);structStudentstu;scanf(%s,stu.name);scanf(%s,stu.stuID);scanf(%d,stu.score);scanf(%d,stu.age);if(*size*len){intnewLen*len*2;structStudent*newStuList(structStudent*)malloc(sizeof(structStudent)*newLen);for(inti0;i*size;i){newStuList[i]stuList[i];}*lennewLen;stuListnewStuList;printf(扩容完成当前的总容量升级为%d\n,*len);}stuList[*size]stu;*size*size1;returnstuList;}voidremoveStu(intsize0,structStudent*stuList,int*size){intncheckIndex(size0);structStudentoldStustuList[n];for(intin;i*size-n;i){stuList[i]stuList[i1];}*size*size-1;printf(学生管理系统已删除%d号位%s的所有信息\n,n,oldStu.name);}voidfindStu(intsize0,structStudentstuList[]){intncheckIndex(size0);printf(学生管理系统的%d号位储存的学生信息\n,n);structStudentstustuList[n];showInfo(stu);}intmaxAge(structStudentstuList[],intsize){structStudentstustuList[0];intn0,maxstu.age;for(inti0;isize;i){structStudentstustuList[i];if(stu.agemax){maxstu.age;ni;}}returnn;}intmaxScore(structStudentstuList[],intsize){structStudentstustuList[0];intn0,maxstu.score;for(inti0;isize;i){structStudentstustuList[i];if(stu.scoremax){maxstu.score;ni;}}returnn;}intmain(){system(chcp 65001);intsize0;intlen2;intsize0;structStudent*stuList(structStudent*)malloc(sizeof(structStudent)*len);while(1){printf( 学生管理系统 \n);printf( 101: 添加学生 \n);printf( 102: 删除学生 \n);printf( 103: 查找学生 \n);printf( 104: 筛选学生 \n);printf( 105: 显示所有学生 \n);printf( 106: 退出系统 \n);printf( 请输入功能编号\n);intcid;size0upSize(size);scanf(%d,cid);if(cid101){stuListaddStu(size,stuList,len);}elseif(cid102){removeStu(size0,stuList,size);}elseif(cid103){findStu(size0,stuList);}elseif(cid104){intcid;printf(\n);printf( 201: 筛出年纪最大的学生 \n);printf( 202: 筛出学分最高的学生 \n);printf( 请输入功能编号\n);scanf(%d,cid);if(cid201){intnmaxAge(stuList,size);structStudentstustuList[n];printf(年纪最大的学生是 %s\n,stu.name);showInfo(stu);}elseif(cid202){intnmaxScore(stuList,size);structStudentstustuList[n];printf(学分最高的学生是 %s\n,stu.name);showInfo(stu);}}elseif(cid105){for(inti0;isize;i){structStudentstustuList[i];showInfo(stu);}}elseif(cid106){printf(感谢使用学生管理系统);break;}}}1. 结构体作为函数参数在上面的代码中showInfo的形参是结构体变量voidshowInfo(structStudentstu){printf(【姓名%s 学号%s 学分%d 年龄%d】\n,stu.name,stu.stuID,stu.score,stu.age);}这种方式的本质是值传递。调用showInfo(stu)时编译器会把实参stu的所有成员复制一份到形参stu中函数内部访问的是副本因此只适合输出、查看等不需要修改原数据的场景。如果结构体很大复制整个结构体会带来一定的时间和空间开销。而addStu、removeStu、maxAge、maxScore等函数的形参则是结构体指针或结构体数组structStudent*addStu(int*size,structStudentstuList[],int*len);voidremoveStu(intsize0,structStudent*stuList,int*size);其中struct Student stuList[]作为形参时编译器会把它看成struct Student *stuList本质上都是把数组首地址传给函数。函数内部通过指针修改stuList[i]修改的是主函数中的原数组因此可以实现添加、删除等会改变原数据的操作。这也是为什么addStu需要把新数组指针返回给主函数而removeStu只需通过指针修改数组内容、不需要返回数组。2. 结构体数组的动态分配与扩容主函数中结构体数组并不是用固定长度定义而是使用malloc动态分配intlen2;structStudent*stuList(structStudent*)malloc(sizeof(structStudent)*len);malloc(sizeof(struct Student) * len)会在堆上申请一块连续内存大小正好能放下len个Student结构体。sizeof(struct Student)用于计算一个结构体变量占用的字节数包括它所有成员以及可能存在的内存对齐。返回的void *经过强制类型转换后赋给stuList此时stuList就可以像普通数组一样使用下标访问。当元素个数达到容量上限时addStu会进行扩容if(*size*len){intnewLen*len*2;structStudent*newStuList(structStudent*)malloc(sizeof(structStudent)*newLen);for(inti0;i*size;i){newStuList[i]stuList[i];}*lennewLen;stuListnewStuList;printf(扩容完成当前的总容量升级为%d\n,*len);}扩容的思路是申请一块更大的新空间把旧数组中的元素逐个复制到新数组再让stuList指向新空间。注意动态分配的内存使用完毕后通常应使用free释放以避免内存泄漏上面的示例为了保持简洁省略了释放旧空间的步骤实际项目中应妥善管理内存。

相关新闻

SiliconCloud智能体开发:Claude Code接入TaoToken的配置与验证

SiliconCloud智能体开发:Claude Code接入TaoToken的配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/11 3:50:56 阅读更多 →
IDEA中Ctrl+Shift+F失效?从快捷键冲突到全局热键占用的完整排查指南

IDEA中Ctrl+Shift+F失效?从快捷键冲突到全局热键占用的完整排查指南

上周有个同事抱着笔记本过来找我,说IDEA里按CtrlShiftF全文件搜索没反应,问我是不是装的社区版少了功能。我打开他的IDE试了试,快捷键确实纹丝不动,但从菜单栏手动点"Find in Files"却完全正常。这种"快捷键失灵但…

2026/10/11 9:56:51 阅读更多 →
别只算训练和推理成本:AI 评测正在变成新的算力账单,用 TaoToken 先把这 4 层预算拆开

别只算训练和推理成本:AI 评测正在变成新的算力账单,用 TaoToken 先把这 4 层预算拆开

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/11 8:49:32 阅读更多 →

最新新闻

头歌MySQL实训全关卡答案解析与避坑指南

头歌MySQL实训全关卡答案解析与避坑指南

简介:这是一份头歌MySQL数据库实训的答案整理文档,面向正在完成头歌平台实训作业的学生,也适合需要系统回顾MySQL核心操作的初学者。文档以PDF格式提供,共1个文件,压缩包大小433KB,配有目录结构&#xff0c…

2026/10/12 0:30:14 阅读更多 →
OpenClaw 下一代 AI 助手框架:让 AI 拥有记忆和工具,TaoToken 统一 Key 接入实战

OpenClaw 下一代 AI 助手框架:让 AI 拥有记忆和工具,TaoToken 统一 Key 接入实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/12 0:30:14 阅读更多 →
身份证识别OCR实战:从图像预处理到字段解析的完整流程

身份证识别OCR实战:从图像预处理到字段解析的完整流程

简介:这是一份面向图像识别与OCR入门者的身份证识别项目实践资源,聚焦从身份证图片中自动提取身份证号及其他字段的完整实现。项目基于百度开源的PaddleOCR,针对中文识别效果做了优化,并编译了Windows可执行版本,可通过…

2026/10/12 0:30:14 阅读更多 →
Hyperf 中使用 Elasticsearch:协程化客户端封装与连接池实战指南

Hyperf 中使用 Elasticsearch:协程化客户端封装与连接池实战指南

后端Web框架微服务RPC框架异步编程 【免费下载链接】hyperf 🚀 A coroutine framework that focuses on hyperspeed and flexibility. Building microservice or middleware with ease. 项目地址: https://gitcode.com/hyperf/hyperf 点击查看 免费下载 …

2026/10/12 0:30:14 阅读更多 →
指针仪表检测数据集实战:1000张图与三种标签格式的YOLO训练指南

指针仪表检测数据集实战:1000张图与三种标签格式的YOLO训练指南

简介:这份YOLO指针仪表目标检测数据集面向计算机、电子信息工程、数学等专业的学生与算法初学者,可用于课程设计、期末大作业和毕业设计中的目标检测训练与验证任务。压缩包共2000个文件,约20.25MB,包含1000张指针仪表图片&#x…

2026/10/12 0:30:14 阅读更多 →
基于深度学习的智慧教室:专注度分析与作弊检测实战

基于深度学习的智慧教室:专注度分析与作弊检测实战

简介:这份资源是面向计算机相关专业学生与项目实战学习者的智慧教室系统源码,核心围绕基于深度学习的课堂专注度分析与考试作弊检测两大功能展开,可作为毕业设计、课程设计或期末大作业的完整参考方案。压缩包共626个文件,约87.73…

2026/10/12 0:29:13 阅读更多 →

日新闻

复古胶片颗粒感噪点合成器:Canvas ImageData 像素高斯杂色注入算法

复古胶片颗粒感噪点合成器:Canvas ImageData 像素高斯杂色注入算法

在数码相机、高清显示屏与现代矢量图形技术高度发达的今天,画面可以做到绝对的锐利、平滑与无瑕。然而,当一张秋日手账插画或拍立得照片过于“平整无瑕”时,往往会散发出一种冰冷生硬的“数码塑料感(Digital Plasticity&#xff0…

2026/10/12 0:00:59 阅读更多 →
活字印刷古籍线装排版:Canvas 竖排文字与栏线自适应算法

活字印刷古籍线装排版:Canvas 竖排文字与栏线自适应算法

在现代网页与移动端设计中,横排(Horizontal Layout)早已经成为了绝对的主流。然而,当我们翻开泛黄的线装古籍、宋版木刻诗集,或是欣赏一张茶道雅集的手写便签时,那种**自上而下纵向书写、自右向左逐列铺展&…

2026/10/12 0:00:59 阅读更多 →
周日晚间的“精神松绑减震器”:无压力情绪倾倒箱与温和轻声陪伴

周日晚间的“精神松绑减震器”:无压力情绪倾倒箱与温和轻声陪伴

每到周日的晚上八点到十点,很多人心里都会悄悄亮起一盏警示灯。 在心理学上,这种现象有一个专门的称谓——“周日夜晚焦虑症(Sunday Scaries)”。明天又是周一,闹钟又要重新在七点响彻卧房;脑海里仿佛有一个…

2026/10/12 0:00:59 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/12 0:16:30 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/12 0:16:38 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/12 0:16:43 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/11 10:45:37 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/11 14:36:53 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/11 14:36:54 阅读更多 →