语言基础程序 5_第1页
语言基础程序 5_第2页
语言基础程序 5_第3页
语言基础程序 5_第4页
语言基础程序 5_第5页
已阅读5页,还剩41页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

C语言程序设计项目化教程(AI助学)(微课版)项目7

高校新生入学信息管理系统情景导入九月高校迎新季,小辰作为志愿者协助整理新生信息时发现,学校仍用纸质表格登记新生信息。学号、身份证号等隐私数据随手记录易泄露,年龄超30岁、手机号非11位等无效数据众多,后续新生补录、退学需重新整理表,效率极低。作为计算机专业学生,小辰决定用C语言核心构造数据类型解决上述问题,例如用结构体类型数据实现信息结构化存储,避免数据零散存放;用枚举类型数据限定性别取值,保障信息规范,此外还将用链表支持动态增删适配信息变动。知识图谱学习要点掌握结构体、枚举、共用体、链表的使用能独立设计新生信息存储结构能设计链表节点结构具备数据严谨性、结构化存储意识与内存安全意识培养复杂数据处理场景的问题拆解能力实现新生入学信息登记任务7-1任务7-1实现新生入学信息登记设计并实现新生入学登记表程序,其核心功能为:通过结构体、枚举、共用体等数据类型变量存储新生基础信息,实现信息的输入、格式化输出,完成1名新生的信息登记与展示,程序需保留关键信息,效果如图7-2所示。任务描述图7-2新生入学登记表效果7.1.1结构体类型知识准备什么是结构体?(Struct)C语言中的构造数据类型,将描述同一对象的不同属性(如学号、姓名)打包成整体。

⚠️前提:在定义变量前,必须先定义结构体类型。结构体的构成:成员(Member)成员类型灵活多样:

①基本类型(int/char/float);②构造类型(数组/指针/其他结构体)。为什么需要结构体?避免零散变量的管理混乱,将相关联的数据“模块化”打包,让程序逻辑更清晰。7.1.1结构体类型知识准备1.结构体类型的定义▍定义语法:数据类型的“蓝图”定义结构体类型,本质上是为一组相关的数据创建一个新的“模板”或“蓝图”。通过这个模板,我们可以在程序中定义变量,来存储逻辑上相关联的不同类型的数据,比如学生的姓名、年龄、成绩等。▍核心要点解读01.结构体名:遵循C语言标识符规则,代表新类型的名称。02.成员构成:包含多个数据项,每项需指定具体类型与变量名。03.关键字struct:定义时必须保留,与结构体名共同构成完整类型。CodeExample/结构体定义模板struct结构体名{

数据类型

成员名1;//如intage;

数据类型

成员名2;//如charname[20];…//其他成员};7.1.1结构体类型知识准备1.结构体类型的定义【例7-1】结构体类型Student的定义。核心概念解析什么是struct?它是一种用户自定义的数据类型,像一个“蓝图”,将学号、姓名等7个不同类型的变量打包在一起,完整描述一个学生对象。关于内存分配请注意:定义结构体类型时,编译器只为“类型定义”分配极小空间,并未为任何具体的学生实体分配内存。

1.

struct

Student{

2.

int

no;

3.

char

name[20];

4.

char

sex;

5.

int

age;

6.

char

pno[19];

7.

char

addr[80];

8.

char

tel[12];

9.

};

7.1.1结构体类型知识准备2.结构体变量的定义方法一:先定义类型,再定义变量推荐structStudent{...};

structStudentstu1,stu2;//基于类型创建变量,逻辑清晰方法二:定义类型的同时定义变量structStudent{...}stu1,stu2;

//紧凑写法,类型定义与变量声明合并方法三:直接定义匿名结构体变量struct{...}stu1,stu2;

//注意:无类型名称,后续无法复用此类型创建新变量7.1.1结构体类型知识准备核心本质结构体类型(structStudent)定义数据的“蓝图”或“模板”,是一种抽象的数据结构定义。结构体变量(student1)根据蓝图创建的具体数据实例,代表实际存在的个体数据。内存分配结构体类型仅仅是类型定义,不分配实际的内存空间,不占用物理存储。结构体变量程序运行时,分配实际的内存空间,大小由成员变量决定。操作限制结构体类型只是一个模型,不能直接进行赋值、存取数据等实际操作。结构体变量是具体的实体,可以进行赋值、存取成员、参与运算等操作。核心区别:结构体类型vs结构体变量7.1.1结构体类型知识准备3.结构体变量的初始化与引用初始化结构体变量在定义结构体变量时,可以使用花括号`{}`按照成员顺序对其进行初始化赋值。//定义并初始化结构体变量boy1structStudentboy1={102,"ZhangPing",'M',78.5};引用结构体成员要访问结构体变量中的成员,必须使用成员引用运算符(.),实现赋值、读取或运算。核心语法:结构体变量名.结构体成员名student1.no=1001;//赋值printf("Name:%s",);//读取student1.score+=10;//运算7.1.1结构体类型知识准备3.结构体变量的初始化与引用终端运行输出结果number=102name=zhangpingsex=Mscore=78.500000

1.

#include

<stdio.h>

2.

void

main(){

3.

struct

stu{

4.

int

num;

5.

char

*name;

6.

char

sex;

7.

float

score

8.

}boy2,boy1={102,"zhangping",'M',78.5};

9.

boy2=boy1;10.

printf("number=%d\nname=%s\n",boy2.num,boy2.name);

11.

printf("sex=%c\nscore=%f\n",boy2.sex,boy2.score);

12.

}【例7-5】结构体变量boy1、boy2的初始化及结构体成员变量的引用。7.1.1结构体类型知识准备4.结构体数组核心概念结构体数组本质上是一个数组,其每个元素都是一个独立的结构体变量,兼具数组的批量管理特性与结构体的复合数据特性。📊逻辑模型示意:

stu[0]→{id:1,name:"Alice"}

stu[1]→{id:2,name:"Bob"}本质=数组+结构体定义方法与普通变量定义逻辑一致,只需将变量名改为数组名[长度]。推荐先定义结构体类型,再声明数组。//定义30个学生的数组

structStudent{intid;};

structStudentstu[30];💡长度可根据需求动态调整成员访问结合数组下标定位具体元素,使用点运算符(.)访问其内部属性。数组名[下标].成员名//访问第2个学生性别

stu[1].sex='F';

printf("Name:%s",stu[0].name);高频操作,需熟练掌握7.1.1结构体类型知识准备5.结构体指针01/什么是结构体指针?它是一个指针变量,专门用来存储结构体变量在内存中的起始地址,从而实现对结构体的间接访问。structStudents;structStudent*p=&s;//指针p指向变量s02/访问结构体成员的两种等价方式①圆点访问(需加括号):(*p).age②箭头访问(更简洁推荐):p->age💡提示:在实际开发中,“->”是访问结构体指针成员的标准写法。7.1.1结构体类型知识准备5.结构体指针代码实现·CLanguage#include<stdio.h>voidmain(){structstudent{charname[10];charsex;floatscore;};structstudentstu[3]={...},s1={...},*p,*q;//1.指针访问单个结构体变量p=&s1;printf("%s,%c,%5.1f\n",,(*p).sex,p->score);//2.指针遍历结构体数组(核心用法)for(q=stu;q<stu+3;q++)printf("%s,%c,%5.1f\n",q->name,q->sex,q->score);}程序运行结果·Output/*输出单个结构体变量student1*/Zhang,F,496.5/*遍历结构体数组stu[3]*/Wang,F,483.0Liu,M,503.0Song,M,471.57.1.2共用体类型知识准备💡核心定义共用体(Union)是C语言中的一种构造数据类型,它允许在同一块内存空间中存储不同数据类型的成员。这与结构体的内存分配方式有着本质的区别。本质总结:“变色龙”变量在不同时间可以扮演不同的数据类型角色,但始终占用同一块固定大小的内存,新赋值会覆盖旧数据。特性对比:StructvsUnion内存

分配为每个成员分配

独立的内存空间所有成员共享

同一块内存内存

总大小所有成员所占

字节数的总和等于最大成员

所占的字节数数据

有效性各成员的值

可以同时存在同一时间

仅一个成员有效1.共用体类型7.1.2共用体类型知识准备核心主体:共用体变量x作为访问共享内存的唯一入口,逻辑上包含多个不同类型的成员。空间规则:按“最大成员”分配内存总容量取决于占用字节数最多的成员(如floatb),而非各成员之和。地址特性:所有成员共享首地址x.a、x.b、x.c均从同一起始地址开始,仅占用长度不同的连续字节段。关键结论:赋值会发生内存覆盖对最大成员赋值会覆盖整个内存块,导致其他成员原有数据失效。1.共用体类型的概念7.1.2共用体类型知识准备2.共用体的定义与引用定义语法/Definitionunion共用体名{

类型1成员名1;//成员定义

类型2成员名2;//不同类型共享内存

};引用格式/Reference访问成员的唯一标准格式:共用体变量名.成员名代码示例/CodeExampleuniondata{inta;floatb;charc;};//定义共用体类型

intmain(){

uniondatax;x.a=100;printf("x.a=%d\n",x.a);//输出整数

x.b=3.14f;printf("x.b=%.2f\n",x.b);//输出浮点数(覆盖了a的内容)

return0;

}7.1.2共用体类型知识准备2.共用体的定义与引用核心机制共用体的所有成员共享同一块内存空间。这意味着,在同一时刻,这块内存中**只能保存一个成员**的数据。有效成员判定:

只有最后一次完成赋值存储的成员是有效的。若访问其他成员,将读取到内存中遗留的无意义“垃圾值”。CCode:ValueOverwriteMechanism#include<stdio.h>uniondata{inta;floatb;charc;};intmain(){uniondatax;//定义共用体变量

x.a=1;printf("a有效:%d\n",x.a);

x.b=3.6;printf("b有效:%.1f|a垃圾:%d\n",x.b,x.a);

x.c='H';printf("c有效:%c|b/a垃圾:%.1f%d\n",x.c,x.b,x.a);return0;}使用注意事项:值的覆盖7.1.2共用体类型知识准备2.共用体的定义与引用要点:地址相同共用体变量本身的地址与其所有成员的地址完全相同,共享同一块内存空间。printf("&x=%p\n",&x);

printf("&x.a=%p\n",&x.a);

//输出结果完全一致要点:不能直接操作除支持整体赋值外,无法直接对共用体变量进行赋值、初始化或直接打印输出。uniondatax={10};//错误

x=100;//错误

printf("%d",x);//错误要点:函数与嵌套共用体变量可以作为函数参数进行传递,也可以嵌套在结构体中使用,构建更灵活的数据结构。典型应用场景:

在网络协议解析、硬件寄存器映射等场景中,灵活处理不同类型的数据。使用注意事项:其他要点7.1.3枚举类型知识准备1.枚举类型的概念▍定义与核心特征枚举(Enumeration)是C语言中的一种基本数据类型,它允许我们为一组逻辑相关的整型常量,定义一个具有实际语义的自定义类型名称。取值范围严格受限变量只能取预先定义好的特定值,不可越界。“见名知意”的符号化用SUN、MON等语义符号代替0、1等无意义数字。▍为什么需要枚举?可读性逻辑直观

易于理解安全性编译检查

拒绝非法值易维护集中管理

一处修改▍代码直观对比❌魔法数字(Bad)if(day==0){...}//0代表什么?含义模糊✅使用枚举(Good)if(day==SUN){...}//SUN代表周日,一目了然7.1.3枚举类型知识准备2.枚举类型的定义📝基本语法结构enum枚举类型名{枚举元素1,枚举元素2,...,枚举元素n};💡核心讲解要点enum是C语言关键字自定义的类型名(如weekday)元素本质是整型常量默认从0开始递增赋值🖥️定义示例:表示星期(WeekdayEnum)//定义一个名为weekday的枚举类型,代表一周七天enumweekday{sun,mon,tue,wed,thu,fri,sat};//默认值:sun=0,mon=1,tue=2,...,sat=67.1.3枚举类型知识准备3.枚举变量的定义方式一:混合定义enumweekday{

sun,mon,tue

}today,tomorrow;核心特点写法紧凑,代码行数少,但将类型定义与变量定义混杂在一起,可读性一般。方式二:先型后量//1.定义类型

enumweekday{...};

//2.定义变量

enumweekdayw1,w2;推荐理由结构清晰,将类型抽象与变量实例化分离。最大优势是类型可复用,便于维护。方式三:匿名枚举//无类型名

enum{

RED,GREEN,BLUE

}color_var;适用场景仅适用于一次性、临时性的常量定义。由于没有类型名,后续代码中无法复用此枚举类型。7.1.3枚举类型知识准备4.枚举的核心特性枚举元素是常量枚举元素本质是符号常量,在程序运行期间其值不可被修改或重新赋值。sun=5;//❌错误(不可赋值)day=sun;//✅正确(赋常量值)默认值与自定义值若未手动赋值,枚举元素默认从0开始递增;也可显式指定特定整数值。//默认:sun=0,mon=1,tue=2//自定义:sun=7,mon=1,tue=2枚举本质是整数枚举类型在底层被编译为整数,因此可以像普通整数一样参与算术运算和比较。day1=mon;//day1=1(赋值)if(day1<wed){...}//比较运算7.1.3枚举类型知识准备5.常见误区:枚举不是字符串错误用法:直接格式化输出枚举元素`sun`本质是代表整数(如0)的符号常量,并非字符串"sun"。直接使用`%s`格式符打印,会尝试访问非法内存地址,导致不可预知的结果。enumweekdayday=sun;

//错误示范:%s期望字符串指针

printf("今天是:%s\n",day);

//后果:程序崩溃或输出乱码正确做法:条件判断映射通过`if-else`或`switch`语句进行显式映射。根据枚举变量的整数值,判断并输出其对应的字符串名称,这是最安全、通用的解决方案。enumweekdayday=sun;

if(day==sun)printf("星期天\n");

elseif(day==mon)printf("星期一\n");

//...其他分支判断7.1.4类型说明符typedef知识准备1.什么是typedef?核心定义`typedef`是C语言的关键字,其核心功能是为现有的数据类型创建一个新的名称(别名),让代码书写更灵活。⚠️关键认知提醒它并没有创造新的数据类型,仅仅是给已存在的类型提供了一个更具描述性或更简洁的“外号”。基本语法结构typedef<已有数据类型><自定义新别名>;💻代码实战示例//1.定义别名:将int重命名为INTEGER

typedefintINTEGER;INTEGERscore=95;//等价于intscore;💡核心价值:为复杂类型(如结构体、指针)赋予语义化别名,大幅提升代码的可读性与维护效率。7.1.4类型说明符typedef知识准备2.typedef的核心价值提升可读性使用有意义的别名(如COORDINATE)代替原始类型名,让代码的业务意图一目了然。//使用前:intx,y;

//使用后:

typedefintCOORDINATE;

COORDINATEx,y;//意图清晰简化书写对于结构体、指针等复杂类型,使用别名可以大幅减少重复的冗长代码,让声明更简洁。//前:structPointp1;

typedefstruct{

intx;inty;

}Point;//后:Pointp1;增强可维护性当底层数据类型需要变更时,只需在typedef定义处修改一次,所有引用别名的地方会自动同步更新。//只需修改这一处

typedefdoubleMONEY;

//程序中统一使用

MONEYprice,total;7.1.4类型说明符typedef知识准备3.typedef的典型应用场景场景一:简化结构体定义typedefstruct{intm;intd;inty;}DATE;DATEbirthday;//直接使用别名,替代struct{...}b;场景二:定义固定长度数组typedefintNUM_ARRAY[100];//别名代表100个int的数组NUM_ARRAYscores;//scores等价于intscores[100]场景三:简化复杂指针声明typedefchar*STRING;//为字符指针创建别名STRINGp="HelloWorld";//语义清晰,便于理解场景四:定义函数指针类型typedefint(*CalcFunc)(int,int);//定义函数指针类型CalcFuncfp=add;//指向具体函数,常用于回调设计任务7-1实现新生入学信息登记(1)设计数据类型,具体如下。①

学号:字符数组类型(长度为15)。②

姓名:字符数组类型(长度为20)。③

性别:枚举类型,仅有MALE(男)、FEMALE(女)两个成员。④

年龄:整型(取值范围为15~30,输入时强制校验)。⑤

手机号:字符数组类型(长度为11,输入时强制校验长度)。⑥

紧急联系人/导师:共用体类型(二选一存储,通过输入选择类型)。(2)设计输入函数voidinput_stu(STU*s)。该函数接收结构体指针变量,实现信息输入,仅对年龄(15~30中的整数)和手机号(长度为11)进行合法性校验,对其他信息仅提示,不校验。(3)设计输出函数voidoutput_stu(STUs)。该函数接收结构体变量,格式化输出所有字段,性别显示为“男/女”,联系人信息根据类型输出。(4)在主函数中定义结构体变量,调用输入、输出函数,完成1名新生信息的登记与展示。任务实施:任务要求实现新生入学登记表动态更新任务7-2任务7-1实现新生入学登记表动态更新基于简化的学生信息结构(无校验),开发新生入学登记表动态更新程序,保留核心动态管理功能。启动程序后显示“添加新生”“删除新生”“查看所有”“退出系统”菜单,支持用户选择。选择“添加新生”菜单后,输入学号、姓名、性别、年龄、手机号,通过尾插法插入链表。选择“删除新生”菜单后,输入学号,查找并删除对应节点。选择“查看所有”菜单后,遍历链表,格式化输出所有新生信息。选择“退出系统”菜单后,销毁链表并释放内存,避免泄漏,效果如图7-4所示。任务描述图7-4新生入学登记表动态更新程序效果7.2.1链表知识准备1.动态存储结构为了解决数组的固定长度限制,C语言提供了强大的动态内存管理工具,实现内存的按需分配。malloc()核心功能:堆区分配在内存的“堆”区中,分配一块指定字节大小的连续内存空间。分配后内存中的值是随机的。//分配100个字节的内存

char*pc=(char*)malloc(100);💡提示:需手动计算所需字节数calloc()核心功能:初始化分配功能与malloc类似,但会将分配的内存块中的所有字节自动初始化为零,无需手动赋值。//分配2个结构体大小并清零

structstu*ps=calloc(2,sizeof(stu));⚡优势:初始化一步到位,避免脏数据free()核心功能:内存释放释放由malloc或calloc分配的堆内存空间,将其归还系统,防止程序出现内存泄漏问题。//释放内存并置空指针

free(pc);pc=NULL;🛡️必须:释放后指针置空,避免野指针7.2.1链表知识准备2.链表的概念📝核心定义链表是一种动态的数据结构,由一系列节点(Node)组成。节点通过指针“串联”,形成链状结构,最后一个节点指针指向NULL。数据域(Data)存储实际业务数据,如整数、字符等。指针域(Next)存储下一个节点的内存地址,建立连接关系。💻C语言结构体定义:typedefstructNode{intdata;structNode*next;}Node;//data:数据域|next:指向下一个节点的指针域⚡️链表的核心优势相比于数组,链表在内存中不需要连续的存储空间,支持动态扩容,在插入和删除操作时效率更高(O(1))。7.2.1链表知识准备3.链表的操作C语言核心实现代码Node*creat(intn){Node*head=NULL,*pf=NULL,*pb=NULL;for(inti=0;i<n;i++){//1.分配内存,创建新节点pb=(Node*)malloc(sizeof(Node));//2.输入新节点的数据scanf("%d",&pb->num);//3.将新节点链接到链表中head=(i==0)?pb:head;//头节点if(i>0)pf->next=pb;//前驱节点指向新节点pf=pb;pb->next=NULL;}returnhead;}STEP01.分配内存空间使用malloc函数为每一个新节点动态分配内存,生成一个独立的节点实体。STEP02.填充节点数据通过输入函数(如scanf)将具体数值写入节点的数据域,完成节点的数据初始化。STEP03.构建节点链接修改前驱节点的指针域,使其指向新创建的节点,将孤立的节点串联成一个完整的序列。核心操作一:建立链表(Create)7.2.1链表知识准备3.链表的操作核心操作二:遍历与查找(Search)▍代码实现(CLanguage)Node*search(Node*head,intn){Node*p=head;//1.定位头节点//2.循环遍历:未到末尾且未找到目标while(p!=NULL&&p->num!=n){p=p->next;//3.指针后移一位}returnp;//找到则返回节点,否则返回NULL}▍核心逻辑解析双重条件判定循环利用while循环持续检查,确保在指针未到链表末尾(p!=NULL)且当前节点值不等于目标值时,继续执行遍历。指针持续向后迭代在循环体内执行p=p->next;操作,让指针“走”向下一个节点,逐步扫描链表中的每一个元素,直至满足退出条件。7.2.1链表知识准备3.链表的操作核心操作三:插入节点(Insert)▍代码实现(C语言)Node*insert(Node*head,Node*pi){Node*pf=NULL,*pb=head;//1.遍历链表,寻找插入位置while(pb!=NULL&&pi->num>pb->num){pf=pb;pb=pb->next;}//2.根据位置调整指针完成插入if(pf==NULL){pi->next=head;head=pi;}else{pi->next=pb;pf->next=pi;}returnhead;}▍核心逻辑拆解Step1.定位插入点利用`pf`(前驱)和`pb`(后继)两个指针一前一后遍历链表,直到找到`pi`应插入的位置。Step2.调整指针"搭桥"•头部插入:新节点指向原头节点,更新头指针。•中间/尾部:前驱指向新节点,新节点指向后继节点。7.2.1链表知识准备3.链表的操作核心操作四:删除节点(Delete)💻C语言实现逻辑Node*delete(Node*head,intnum){Node*pf=NULL,*pb=head;//1.遍历链表,寻找目标节点while(pb!=NULL&&pb->num!=num){pf=pb;pb=pb->next;}//2.调整指针并释放内存if(pb!=NULL){if(pf==NULL)head=pb->next;elsepf->next=pb->next;free(pb);//防止内存泄漏}returnhead;}STEP01·定位目标节点使用`pf`(前驱)和`pb`(当前)双指针遍历链表,直到找到数值匹配的节点或到达链表尾部。STEP02·调整指针指向若删除头节点则更新head;否则将前驱节点的`next`指向当前节点的后继节点,实现“跳过”删除。STEP03·释放节点内存调用`free(pb)`释放被删除节点占用的内存空间,这是防止程序出现内存泄漏的关键步骤。7.2.2typedef在链表中的巧妙应用知识准备1.回顾基础:问题的起点结构体(struct)允许我们将不同类型的数据组合在一起,构建复杂的数据结构基础。structStudent{

charname[20];

intid;

};类型别名(typedef)为现有类型创建一个“小名”,有效简化代码书写,提升可读性。typedefstructStuSTU;

//定义变量时简化

STUstu1;链表节点(Node)由数据域和指针域组成,通过自引用指针将多个节点串联成链状结构。structNode{

Studata;//数据域

Node*next;//指针域

};7.2.2typedef在链表中的巧妙应用知识准备2.原生定义的痛点:代码的“噩梦”冗长的代码冗余在不使用`typedef`的C语言开发中,链表节点的定义、指针声明及函数编写过程中,需要反复书写`structNode`这一完整类型名,导致代码结构极不优雅。💡核心痛点总结代码量臃肿庞大,视觉可读性差;重复书写易引发拼写错误,显著增加了开发与后期维护的成本。01.节点定义与变量声明//定义结构体节点

structNode{structStudentdata;structNode*next;};

//声明指针变量(每次都要写struct)

structNode*head=NULL;structNode*p=head;02.函数参数与返回值定义//函数返回值和参数类型冗长

structNode*createNode(structStudentstu){...}

voidprin

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

最新文档

评论

0/150

提交评论