结构体引入:
为什么要引入结构体?
如果没有结构体的话就只能 int char float 单独去定义,不是一个整体,利用结构体定义数据类型可以将这些数据都融合到一起(类似一个集合),就可以模拟实现多一的数据类型。(类比数组,数组是相同的数据类型集合到一起,结构体是不同的数据类型集合到一起)
定义一个结构体:
编程练习:如何定义一个结构体
#include <stdio.h>
//定义一个结构体
// struct 结构体函数
// 表示一个名为学生消息(student)的结构体
struct Student{
int student_name; //学号
char mane{32]; // 学生名
int age; // 年龄
double score; // 成绩
};
// struct 结构体函数
// 定义了一个年月日的结构体
struct Day{
int year; // 年
int month; // 月
int day; // 日
};
int main(){
return 0;
}
编程练习:结构体变量的定义和访问
#include <stdio.h>
#include <string.h>
//定义一个结构体
// struct 结构体函数
// 表示一个名为学生消息(student)的结构体
struct Student
{
int student_name; //学号
char mane[32]; // 学生名
int age; // 年龄
double score; // 成绩
};
// struct 结构体函数
// 定义了一个年月日的结构体
struct Day
{
int year; // 年
int month; // 月
int day; // 日
};
int main(){
struct Student stu1; //struct Student就相当于int stu1 相当于一个变量。
struct Student stu2 = {2,"王二麻子",24,87.9};
stu1.student_name = 1; //学号
strcpy(stu1.mane,"liu1xiaglong"); //给字符串赋值用字符串拷贝 姓名
stu1.age = 10; //点运算符来访问结构体中的成员变量(域)
stu1.score=98.5;
printf("学生的学号是%d,学生姓名%s,学生的年龄%d,学生的成绩%lf\n",stu1.student_name,stu1.mane,stu1.age,stu1.score);
printf("学生的学号是%d,学生姓名%s,学生的年龄%d,学生的成绩%lf\n",stu2.student_name,stu2.mane,stu2.age,stu2.score);
return 0;
}
编程练习:输入两个学生的名字,学号,成绩等输出成绩高的学生的信息
#include <stdio.h>
#include <string.h>
// 定义一个名为Student的结构体用于存储学生信息
struct Student {
int student_number; // 学号
char student_name[32]; // 学生姓名
char student_home[50]; // 家庭地址
int age; // 年龄
double score; // 成绩
};
int main() {
// 声明两个Student结构体变量用于存储两个学生的信息
struct Student stu1;
struct Student stu2;
// 声明一个Student结构体变量用于存储成绩较高的学生信息
struct Student score1;
// 提示用户输入第一个学生的详细信息
printf("请输入学生1的:学号 ,姓名,家庭地址,年龄 ,成绩\n");
// 获取第一个学生的数据
scanf("%d%s%s%d%lf", &stu1.student_number, stu1.student_name, stu1.student_home, &stu1.age, &stu1.score);
// 提示用户输入第二个学生的详细信息
printf("请输入学生2的:学号 ,姓名,家庭地址,年龄 ,成绩\n");
// 获取第二个学生的数据
scanf("%d%s%s%d%lf", &stu2.student_number, stu2.student_name, stu2.student_home, &stu2.age, &stu2.score);
// 输出第一个学生的详细信息
printf("学生号%d,学生姓名%s,家庭地址%s,学生年龄%d,学生成绩%lf\n",
stu1.student_number, stu1.student_name, stu1.student_home, stu1.age, stu1.score);
// 输出第二个学生的详细信息
printf("学生号%d,学生姓名%s,家庭地址%s,学生年龄%d,学生成绩%lf\n",
stu2.student_number, stu2.student_name, stu2.student_home, stu2.age, stu2.score);
// 比较两个学生的成绩
if(stu1.score < stu2.score) {
// 如果第二个学生的成绩较高,输出他们的详细信息
printf("成绩好的学生是:学生号%d,学生姓名%s,家庭地址%s,学生年龄%d,学生成绩%lf\n",
stu2.student_number, stu2.student_name, stu2.student_home, stu2.age, stu2.score);
} else {
// 如果第一个学生的成绩较高或相等,输出他们的详细信息
printf("成绩好的学生是:学生号%d,学生姓名%s,家庭地址%s,学生年龄%d,学生成绩%lf\n",
stu1.student_number, stu1.student_name, stu1.student_home, stu1.age, stu1.score);
}
// 初始将第一个学生的信息赋值给score1
score1 = stu1;
// 再次比较成绩以更新score1为成绩较高的学生
if(stu1.score < stu2.score) {
score1 = stu2;
}
// 输出存储在score1中的成绩较高的学生的详细信息
printf("成绩好的学生是:学生号%d,学生姓名%s,家庭地址%s,学生年龄%d,学生成绩%lf\n",
score1.student_number, score1.student_name, score1.student_home, score1.age, score1.score);
return 0;
}
结构体数组:
它与前面讲的数值型数组基本上一模一样的,只不过需要注意的是,结构体数组的每一个元素都是一个结构体类型的变量,都包含结构体中所有的成员项。
定义结构体数组的方法很简单,同定义结构体变量是一样的,只不过将变量改成数组。或者说同前面介绍的普通数组的定义是一模一样的,如:
struct Student stu[10]; struct Student(结构体名) stu[10]; (数组名)
这就定义了一个结构体数组,共有 10 个元素,每个元素都是一个结构体变量,都包含所有的结构体成员。
结构体数组的引用与引用一个结构体变量在原理上是一样的。只不过结构体数组中有多个结构体变量,我们只需利用 for 循环来遍历结构体数组中的元素。
编程练习:结构体数组应用
#include <stdio.h>
#include <string.h>
//定义一个结构体数组
// struct 结构体函数
// 表示一个名为学生消息(student)的结构体
struct Student
{
int student_number; //学号
char student_mane[32]; // 学生名
char student_home[50]; //家庭地址
int age; // 年龄
double score; // 成绩
};
int main()
{
// 定义结构体数组
struct Student srry[3] = {
{20140656,"玛卡巴卡","西安",18,98.5},
{20140678,"唔西迪兮","成都",28,97.5},
{20140690,"依古比古","北京",19,95.5}
};
int i;
int len = sizeof(srry)/sizeof(srry[0]); //获取大小
// 下标法访问
for(i = 0;i<len;i++){
// 输出结构体
printf("学生号%d,学生姓名%s,家庭地址%s,学生年龄%d,学生成绩%lf\n",
srry[i].student_number,srry[i].student_mane,srry[i].student_home,srry[i].age,srry[i].score);
}
return 0;
}
编程练习:选票系统
#include <stdio.h>
#include <string.h>
struct XuanMin
{
char name[32];//定义了姓名
int tickets; // 票数
};
int main()
{
struct XuanMin xm[3]; // 结构体数组
struct XuanMin max; //记录最高票
int feiPiao = 0; // 记录废票数目
int i; // 定义循环变量 i;
int len; // 记录结构体数组xm的长度
int j; // 定义循环变量 j;
int mark = 0; //标记变量,判断是否投给了候选人。
int total = 5; // 共计5票
char tmpName[32]; // 临时存储选民姓名的字符串
//初始化选民信息
len = sizeof(xm)/sizeof(xm[0]); // 计算结构体数组xm的长度
for(i = 0; i<len; i++){ // 初始化选民信息的循环
xm[i].tickets = 0; // 将每位候选人的票数初始化为0;
printf("请输入第%d个选民的名字:\n",i+1);
scanf("%s",xm[i].name); //输入第`i+1`个选民的名字。
}
//唱票环节 投票环节的循环,共进行5次
for(i=0;i<5;i++){
mark = 0; //重置标记变量为0
printf("请输入你投给谁:\n");
//将全部字符赋值成\0;
memset(tmpName,'\0',sizeof(tmpName)); //每次清空一下 临时姓名变量。
scanf("%s",tmpName);//输入选中的选民名字,像拆开一个选票,看到名字一样
for(j = 0;j<len; j++){ //遍历选民数组,查找是否有匹配的候选人。
if(strcmp(tmpName, xm[j].name) == 0){ //如果输入的选民名字匹配候选人的名字
xm[j].tickets++; //该候选人得票数加1。
mark = 1; //标记为投票成功。
}
}
if(mark == 0){ //如果没有匹配的候选人。
printf("没有此候选人,放弃\n"); //输出提示信息。
feiPiao++; //废票数目加1。
}
}
//公布结果
for(i=0;i<len;i++){ //遍历选民数组,打印每位候选人的名字和得票数。
printf("名字:%s,票数:%d\n",xm[i].name,xm[i].tickets);
}
max = xm[0]; //默认第一位候选人为得票最多者。
for(i=1;i<len;i++){ //遍历选民数组,比较得票数找出最终获胜者。
if(max.tickets < xm[i].tickets){ //如果当前候选人的票数大于最高票数。
max = xm[i]; //更新最高票数的候选人信息。
}
}
printf("%s以%d票当选!!废票是%d\n",max.name,max.tickets,feiPiao);
}
结构体指针:
在C语言中,结构体(struct)的变量和普通变量一样,它们在内存中也是按照顺序存储的,因此它们的地址也是连续的。这与普通的指针概念密切相关,因为指针可以用来访问结构体变量的成员。
例如,考虑如下的结构体定义和变量声明:
struct Point {
int x;
int y;
};
struct Point pt1;
struct Point pt2;
假设 pt1 和 pt2 是 struct Point 类型的变量,它们在内存中的布局可能如下所示(这里只是示意,并非真实的内存布局):
yaml
Memory:
Address Variable Value
1000 pt1.x 0
1004 pt1.y 0
1008 pt2.x 0
1012 pt2.y 0
在这个例子中,假设 pt1 和 pt2 分别占据了连续的内存空间,且每个成员变量也是按顺序存放的。因此,pt1 的地址是 1000,pt2 的地址是 1008。这表明结构体的变量地址是连续的,正如普通变量的地址是连续的一样。
在处理结构体时,可以使用指针来访问结构体变量的成员,例如:
struct Point *ptr = &pt1;
这里,ptr 是一个指向 struct Point 类型的指针,它指向了 pt1 的地址。通过指针 ptr 可以访问和修改 pt1 的成员变量 x 和 y:
ptr->x = 10;
ptr->y = 20;
这与直接使用 pt1.x 和 pt1.y 是等效的,因为指针 ptr 指向了 pt1 的地址,可以通过 ptr 来间接地访问 pt1 的成员。
结构体变量在内存中是连续存储的,这使得它们的地址也是连续的。指针可以用来访问结构体变量的成员,这种方式与普通变量和指针的操作是一致的。
变量的访问有两种方式:1.变量名访问;2.通过地址来访问,结构体也一样,可以通过变量名和地址来访问结构体变量,因此引入结构体指针。
编程练习:结构体指针的定义及访问
/*
结构体指针的定义和应用
*/
#include <stdio.h>
struct Test
{
int indata;
char cdata;
};
int main()
{
int idata = 10;
int *pi = &idata; //之前定义指针的方法
char cdata = 'R';
char *pc = &cdata; //之前定义指针的方法
//变量类型 变量名字 变量值
struct Test T1 = {20,'E'}; //变量类型 变量名字 变量值 变量地址
//变量地址
struct Test *pt = &T1; //本质与普通指针无任何差别。结构体的变量地址也是连续的
printf("indata = %d,cdata = %c\n",T1.indata,T1.cdata);
//之前学到的用变量名访问结构体变量方法
printf("indata = %d,cdata = %c\n",pt->indata,pt->cdata);
//用结构体指针访问结构体变量的方法,用"->"取代点运算符
pt ->cdata = 'L' ;//通过指针改变结构体的内容
printf("indata = %d,cdata = %c\n",T1.indata,T1.cdata);
//之前学到的用变量名访问结构体变量方法
printf("indata = %d,cdata = %c\n",pt->indata,pt->cdata);
//用结构体指针访问结构体变量的方法,用"->"取代点运算符
return 0;
}
/*
用结构体指针来遍历数组
*/
#include <stdio.h>
struct Student
{
int num;
char name[32];
char sex[4];
char address[32];
int age;
double score;
};
int main()
{
struct Student stu[3] = { //定义一个结构体数组
{20140656,"玛卡巴卡","男","山西",18,98.5},
{20140678,"唔西迪兮","女","河北",18,97.5},
{20140690,"依古比古","男","北京",18,95.5}
};
struct Student *p; // 定义一个指针
p = stu; //指针赋值
int i;
int len = sizeof(stu)/sizeof(stu[0]);
// 通过循环访问结构体
for(i=0;i<len;i++)
{/* 下标输出法
printf("num:%d name:%s sex:%s address:%s age:%d score:%.1lf\n",
stu[i].num,stu[i].name,stu[i].sex,stu[i].address,stu[i].age,stu[i].score);
*/
printf("num:%d name:%s sex:%s address:%s age:%d score:%.1lf\n",
p->num,p->name,p->sex,p->address,p->age,p->score);
p++; // 看成父地址 ++向下移动
}
/* 使用指针遍历数组时,一次遍历指针会移到数组的尾巴,
所以当要再一次遍历时,要重新指向数组的首地址。
*/
p = stu; //一定要复位。
for(i=0;i<len;i++)
{
printf("num:%d name:%s sex:%s address:%s age:%d score:%.1lf\n",
p->num,p->name,p->sex,p->address,p->age,p->score);
p++;
}
return 0;
}
注意:如果使用结构体指针遍历数组后想要再次遍历数组的时候,要把指针再次指向数组首地址!!!要不然就会出现以下等问题:
编程练习:结构体指针应用02
#include <stdio.h>
#include <string.h>
// 定义候选人结构体
struct XuanPiao
{
char name[132]; // 姓名字段
int piao; // 票数字段
};
int main()
{
// 初始化变量
struct XuanPiao stu1[3]; // 声明候选人结构体数组,包括3个候选人
struct XuanPiao max; // 存储得票最高的候选人信息
struct XuanPiao *p = stu1; // 定义一个指针,指向结构体数组
int i;
int len;
int total = 5; // 投票总人数
int j;
char newname[125]; // 存储投票人姓名
int feipiao = 0; // 记录废票数
len = sizeof(stu1)/sizeof(stu1[0]); // 计算候选人数组长度
int bluck = 0; // 用于判断投票是否有效的标志
// 初始化候选人信息
for(i = 0; i < len; i++){
p->piao = 0; // 初始化候选人票数为0
printf("请输入第%d候选人姓名\n", i + 1);
scanf("%s", p->name); // 输入候选人姓名
p++; // 指针移动到下一个候选人
}
p = stu1; // 重置指针为数组首地址
// 进行投票
for(i = 0; i < total; i++){
bluck = 0; // 重置投票有效标志
printf("请输入候选人姓名\n");
memset(newname, '\0', sizeof(newname)); // 清空投票人姓名缓冲区
scanf("%s", newname); // 输入进行投票的候选人姓名
p = stu1; // 重置指针为数组首地址
for(j = 0; j < len; j++){
if(strcmp(newname, p->name) == 0){
p->piao++; // 对应候选人票数加一
bluck = 1; // 标记投票有效
}
p++; // 指向下一个候选人
}
if(bluck == 0){
printf("此人不是候选人\n");
feipiao++; // 统计废票数
}
}
p = stu1; // 重置指针为数组首地址
// 输出候选人得票情况
for(i = 0; i < len; i++){
printf("候选人%s,%d\n", p->name, p->piao);
p++; // 指向下一个候选人
}
max = stu1[0]; // 初始化得票最高的候选人为第一个候选人
p = stu1;
for(i = 0; i < len; i++){
if(max.piao < p->piao){
max = stu1[i]; // 找出得票最高的候选人
}
p++; // 指向下一个候选人
}
printf("%s以%d票当选,是最高票数\n", max.name, max.piao); // 输出得票最多的候选人信息
printf("废票有%d票\n", feipiao); // 输出废票数
return 0;
}
编程练习:结构体指针数组函数综合应用改写选票系统
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
//定义了一个结构体XuanMin,包含了一个名为name的是字符数组和一个整数piao。
struct XuanMin
{
char name[132];
int piao;
};
//定义了一个返回指向XuanMin结构体的函数指针的函数initXms。该函数用于动态分配内存以存储候选人信息。
struct XuanMin* initXms(struct XuanMin *p,int *len) //返回一个函数指针。
{
int i;
if(p == NULL){
printf("请输入有几个人参选\n");
scanf("%d",len);
// 用来计算需要分配的内存空间大小,(n乘以结构体的大小{sizeof(sttuct XuanMin)) }
p = (struct XuanMin*)malloc(*len*sizeof(struct XuanMin));
// (struct XuanMin*) malloc返回的void类型指针转换为指向XuanMin结构体类型的指针
}
for(i = 0;i<*len;i++){
// for循环
p->piao = 0; // 初始化候选人票数为0
printf("请输入第%d候选人姓名\n", i + 1); // 原样输出
scanf("%s", p->name); // 输入候选人姓名
p++; //父地址++,指针移动到下一个候选人
}
return p-*len; //因为前面那个for循环 P++ 到尾巴了,需要重置指针为数组首地址
}
void printfXms(struct XuanMin *p,int len)
{
int i;
for(i = 0;i<len;i++){
// for循环
// 初始化候选人票数为0
printf("候选人%s,%d\n", p->name, p->piao); // 输出每位候选人得票情况
p++; //父地址++,指针移动到下一个候选人
}
}
int doVot(struct XuanMin *p,int len)
{
int i;
int j;
int bluck;
int feipiao = 0;
char newname[125];
struct XuanMin *pp =p;
for(i = 0; i < 5; i++){ //for循环 定义了5张票数
bluck = 0; // 用于判断投票是否有效的标志
printf("请输入候选人姓名\n"); //原样输出
memset(newname, '\0', sizeof(newname)); // 清空投票人姓名缓冲区
scanf("%s", newname); // 输入进行投票的候选人姓名
p = pp; // 重置指针为数组首地址
for(j = 0; j < len; j++){ // for1循环
if(strcmp(newname, p->name) == 0){ //通过strcmp函数进行比较,投票人和候选人姓名相等,就等于0
p->piao++; // 对应候选人票数加一,就对当前候选人票数++
bluck = 1; // 标记投票有效
}
p++; //父地址++ 指针移动到下一个候选人
}
if(bluck == 0){ //如果投票人和候选人姓名不相等,就向下执行。
printf("此人不是候选人\n");
feipiao++; // 统计废票数
}
}
return feipiao; //返回废票数
}
// 获取得票最高的候选人
struct XuanMin* getMax(struct XuanMin *p, int len)
{
struct XuanMin* max;
max = p; // 初始化得票最高的候选人为第一个候选人
for(int i = 0; i < len; i++)
{
if(max->piao < p->piao)
{
max = p; // 找出得票最高的候选人
}
p++; // 指针移动到下一个候选人
}
return max; // 返回得票最高的候选人
}
// 主函数
int main()
{
struct XuanMin *Xm = NULL; // 定义候选人结构体指针并初始化为NULL
struct XuanMin *final; // 定义最终当选候选人指针
int total = 0; // 声明变量total用于存储候选人总数
Xm = initXms(Xm, &total); // 初始化候选人信息
printfXms(Xm, total); // 打印候选人信息
int feipiao = doVot(Xm, total); // 模拟投票
printfXms(Xm, total); // 再次打印候选人信息
final = getMax(Xm, total); // 获取得票最高的候选人
printf("%s以%d票当选,是最高票数\n", final->name, final->piao); // 输出当选候选人信息
printf("废票有%d票\n", feipiao); // 输出废票数
return 0; // 返回0表示程序正常结束
}
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct XuanPiao
{
char name[132];
int piao;
};
void inname(struct XuanPiao **pxm,int *pt)
{
//*pt = 10; //pt 指向的是toal的地址。 *pt就是把toal地址的值取出来
//*pxm = NULL; //pxm 保存的是xm的地址。 *pxm保存的是xm指针的指向的地址。
int i;
if(*pxm == NULL){ //pxm 保存的是xm的地址。 *pxm保存的是xm指针的指向的地址。
printf("请输入有几个人参选\n");
scanf("%d",pt);
//pxm 保存的是xm的地址。 *pxm保存的是xm指针的指向的地址。
*pxm =(struct XuanPiao*)malloc(*pt *sizeof(struct XuanPiao));
}
for(i = 0;i<*pt ;i++){
(*pxm)->piao =0;//pxm 保存的是xm的地址。 *pxm保存的是xm指针的指向的地址。
printf("请输入第%d候选人姓名\n", i + 1); // 提示用户输入第i位候选人姓名
scanf("%s", (*pxm)->name);//pxm 保存的是xm的地址。 *pxm保存的是xm指针的指向的地址。
(*pxm)++;
} //pxm 保存的是xm的地址。 *pxm保存的是xm指针的指向的地址。
*pxm = *pxm - *pt;
}
void prname(struct XuanPiao *p,int len)
{
for(int i = 0;i<len;i++){
printf("候选人%s,%d\n", p->name, p->piao); // 输出候选人姓名和得票情况
p++; // 指针移动到下一个候选人
}
}
int getname(struct XuanPiao *p,int len)
{ int i;
int feipiao = 0;
char newname[32];
int bluck= 0;
int j;
struct XuanPiao *stu1=p;
for(i = 0;i<5;i++){
bluck = 0; // 用于判断投票是否有效的标志
printf("投票人\n");
memset(newname, '\0', sizeof(newname));
scanf("%s", newname);
p = stu1;
for(j = 0; j < len; j++){ // for1循环
if(strcmp(newname, p->name) == 0){ //通过strcmp函数进行比较,投票人和候选人姓名相等,就等于0
p->piao++; // 对应候选人票数加一,就对当前候选人票数++
bluck = 1; // 标记投票有效
}
p++; //父地址++
}
if(bluck == 0){ //如果投票人和候选人姓名不相等,就向下执行。
printf("此人不是候选人\n");
feipiao++; // 统计废票数
}
}
return feipiao; //返回废票数
}
struct XuanPiao* Maxame(struct XuanPiao *p,int len){
struct XuanPiao* max ;
max = p;
for(int i = 0; i < len; i++)
{
if(max->piao < p->piao)
{
max = p; // 找出得票最高的候选人
}
p++; // 指针移动到下一个候选人
}
return max; // 返回得票最高的候选人
}
int main()
{
struct XuanPiao *Xm=NULL;
struct XuanPiao *max;
int toal = 0;
//struct XuanPiao **pxm = &Xm ;// 这里保存的是一级指针的地址,就是二级指针。
// int *pt = &toal;
//*pt = 10; //pt 指向的是toal的地址。 *pt就是把toal地址的值取出来
//*pxm = NULL; //pxm 保存的是xm(一级指针)的地址。 *pxm保存的是xm一级指针所指向的地址。
/*打个比方,这个一级指针 xm 保存的是一个整型数8的地址。
那么这个*pxm 就等于是这个整型数8的地址。*/
inname(&Xm,&toal);
prname(Xm,toal);
int feipiao = getname(Xm,toal);
prname(Xm,toal);
max = Maxame(Xm,toal);
printf("%s以%d票当选,是最高票数\n", max->name, max->piao); // 输出当选候选人信息
printf("废票有%d票\n", feipiao); // 输出废票数
return 0;
}
共用体/联合体引入:
共用体和结构体十分相似,是一种构造类型或复杂类型,它也可以包含多个类型不同的成员。
结构体和共用体的区别在于:结构体的各个成员会占用不同的内存,互相之间没有影响;而共用体的所有成员占用同一段内存,修改一个成员会影响其余所有成员。
注意:结构体占用的内存大于等于所有成员占用的内存的总和(成员之间可能会存在缝隙),共用体占用的内存等于最长的成员占用的内存。共用体使用了内存覆盖技术,同一时刻只能保存一个成员的值,如果对新的成员赋值,就会把原来成员的值覆盖掉。
编程练习:共用体定义和使用
// 结构体——共用体(联合体)
/*
共用体的定义及其注意事项
*/
#include <stdio.h>
struct TestT
{
int indata;
char cdata;
double ddata;
};
union TestU
{
int indata;
char cdata;
double ddata;
};
int main()
{
struct TestT ti;
union TestU ui;
printf("结构体大小是sizeof = %d\n",sizeof(ti)); //内存大于或等于所有成员的内存
printf("共用体大小是sizeof = %d\n",sizeof(ui)); //内存大小由最大成员的内存决定
ti.indata = 10;
ti.cdata = 'a';
ti.ddata = 20;
//结构体正常输出
//结构体成员的每一个都有单独的地址
printf("indata %p,%d\n",&ti.indata,ti.indata);
printf("indata %p,%c\n",&ti.cdata,ti.cdata);
printf("indata %p,%lf\n",&ti.ddata,ti.ddata);
printf("-------------");
ui.indata = 10;
ui.cdata = 'a';
//ui.ddata = 20;
// 共同体输出占用内存空间最大的,并且将其他直全部覆盖
//共用体所有成员都共用一个地址
printf("indata %p,%d\n",&ui.indata,ui.indata); // 输出asic码表中a的值。
printf("indata %p,%c\n",&ui.cdata,ui.cdata); /// 正常输出。
//printf("indata %p,%lf\n",&ui.ddata,ui.ddata);
//因为结构体成员每一个都有独立的内存空间,所以它的每一个成员都可以打印出来。互不干扰;
//因为共用体成员每一个都是公用同一个内存空间,所以它的值会被之后一个输入的成员覆盖掉。
return 0;
}
共用体覆盖问题
// 结构体——共用体(联合体)
/*
共用体的定义及其注意事项
*/
#include <stdio.h>
struct TestT
{
int indata;
char cdata;
double ddata;
};
union TestU
{
int indata;
char cdata;
double ddata;
};
int main()
{
struct TestT ti;
union TestU ui;
printf("结构体大小是sizeof = %d\n",sizeof(ti)); //内存大于或等于所有成员的内存
printf("共用体大小是sizeof = %d\n",sizeof(ui)); //内存大小由最大成员的内存决定
ti.indata = 10;
ti.cdata = 'a';
ti.ddata = 20;
//结构体正常输出
//结构体成员的每一个都有单独的地址
printf("indata %p,%d\n",&ti.indata,ti.indata);
printf("indata %p,%c\n",&ti.cdata,ti.cdata);
printf("indata %p,%lf\n",&ti.ddata,ti.ddata);
printf("-------------");
ui.indata = 10;
ui.cdata = 'a';
//ui.ddata = 20;
// 共同体输出占用内存空间最大的,并且将其他值全部覆盖,只输出内存空间最大的值。
//共用体所有成员都共用一个地址
printf("indata %p,%d\n",&ui.indata,ui.indata); // 输出asic码表中a的值。
printf("indata %p,%c\n",&ui.cdata,ui.cdata); /// 正常输出。
//printf("indata %p,%lf\n",&ui.ddata,ui.ddata);
//因为结构体成员每一个都有独立的内存空间,所以它的每一个成员都可以打印出来。互不干扰;
//因为共用体成员每一个都是公用同一个内存空间,所以它的值会被之后一个输入的成员覆盖掉。
return 0;
}
编程练习:共用体开发案例
#include <stdio.h> // 包含标准输入输出函数库
#include <stdlib.h> // 包含标准库函数,如 exit() 函数
struct Person
{
char name[32]; // 姓名,用于存储用户输入的姓名,最大长度为 31 个字符加上结尾的空字符
int num; // 数字,未被使用
char sex[4]; // 性别,未被使用
char zhiYe; // 职业信息,用于存储用户输入的职业信息 't' 或 's'
union
{
char class[8]; // 共用体中的班级信息,用于存储学生的班级名称,最大长度为 7 个字符加上结尾的空字符
char subject[4]; // 共用体中的科目信息,用于存储老师教授的科目名称,最大长度为 3 个字符加上结尾的空字符
} mes; // 共用体变量
};
int main()
{
struct Person p[2]; //定义一个包含两个Person结构体对象的数组
int len = sizeof(p)/sizeof(p[0]); // 计算数组p的长度,这里是2
for(int i =0;i<len;i++ )
{
printf("请输出你的身份: t or s!!\n"); // 输出提示信息,要求用户输入职业信息 't' 表示老师, 's' 表示学生
scanf("%c",&(p[i].zhiYe)); // 读取用户输入的职业信息到相应结构体成员变量zhiYe
if(p[i].zhiYe == 't'){ // 如果职业信息是老师
printf("你好,老师!,请说出你的姓名\n"); // 提示老师输入姓名
scanf("%s",p[i].name); // 读取老师的名字到对应成员变量name
printf("请老师输入所教的科目\n"); // 提示老师输入所教科目
scanf("%s",p[i].mes.subject); // 读取老师所教科目到共用体中的成员变量subject
}
else if(p[i].zhiYe =='s') // 如果职业信息是学生
{
printf("请学生输入名字\n"); // 提示学生输入姓名
scanf("%s",p[i].name); // 读取学生的名字到对应成员变量name
printf("请学生输入所在班级\n"); // 提示学生输入班级信息
scanf("%s",&(p[i].mes.class)); // 读取学生班级信息到共用体中的成员变量class
}
else // 如果职业信息既不是 't' 也不是 's'
{
printf("你的输入有误\n"); // 输出提示信息,输入有误
}
getchar(); // 读取并丢弃输入缓冲中的一个字符,防止影响后续的输入
}
for(int i=0;i<len;i++) // 遍历结构体数组,根据职业类型输出相应的信息
{
if(p[i].zhiYe == 't') // 如果职业类型是老师
{
printf("老师的名字是%s,所教的科目是%s\n",p[i].name,p[i].mes.subject); // 输出老师的姓名和所教科目
}
else if(p[i].zhiYe =='s') // 如果职业类型是学生
{
printf("学生的名字是%s,所在班级是%s\n",p[i].name,p[i].mes.class); // 输出学生的姓名和所在班级
}
else
{
exit(-1); // 如果职业信息既不是 't' 也不是 's',异常退出程序
}
}
return 0; // 主函数返回结束
}
这段代码的功能是让用户输入老师或学生的信息,包括姓名、职业类型(老师或学生)、所在班级或所教科目,并最终将这些信息输出显示出来。共用体mes在不同情况下可以存储不同类型的数据,在本例中用来区分老师和学生的附加信息。
枚举类型
在实际编程中,有些数据的取值往往是有限的,只能是非常少量的整数,并且最好为每个值都取一个名字,以方便在后续代码中使用,比如一个星期只有七天,一年只有十二个月,一个班每周有六门课程,这时候就要用到枚举类型。
编程练习:如何定义枚举类型且使用
/*
枚举类型 如果一个变量只有几种可能的值,比如星期几,就可以用枚举
*/
#include <stdio.h>
// 1.值默认从0开始,枚举元素不能被赋值,虽然看着象变量名
enum Month
{
Feb, Mar, Apr, May, Jun, Jul, Aug, Sept, Oct, Nov, Dec
};
// 2.可以指定列表中枚举数的值
enum Month1
{
Jan = 5, Feb1, Mar2, Apr3, May4, Jun5, Jul6, Aug7, Sept8, Oct9, Nov0
};
// 3.可以直接忽略枚举类型名,直接定义枚举变量
enum
{
Jan1 = 3, Feb11, Mar22, Apr33, May44, Jun55, Jul66, Aug77, Sept88, Oct99, Nov00
} m1, m2;
int main()
{
// 1.值默认从0开始
enum Month w;
w = Feb;
printf("w = %d \n", w); // 输出0;
// 2.可以指定列表中枚举数的值
enum Month1 w1;
w1 = Feb1;
printf("w = %d \n", w1); // 输出6;
// 3.可以直接忽略枚举类型名,直接定义枚举变量
m1 = Feb11; // 4
m2 = Nov00; //13
if (m1 < m2) // 可以进行比较
{
printf("我看你是钵钵鸡\n");
}
printf("m1 = %d m2 = %d\n", m1, m2);
return 0;
/*
1.值默认从0开始,枚举元素不能被赋值,虽然瞅着象变量名
2.可以指定列表中枚举数的值
3.可以直接忽略枚举类型名,直接定义枚举变量
*/
}
这段代码演示了枚举类型的基本用法,包括默认从0开始的枚举值、指定枚举数的值、以及直接定义枚举变量。枚举类型能够更直观地表示一系列相关的常量,并且可以进行比较操作。
typedef 关键字
C语言允许用户使用 typedef 关键字来定义自己习惯的数据类型名称,来替代系统默认的基本类型名称、数组类型名称、指针类型名称与用户自定义的结构型名称、共用型名称、枚举型名称等。一旦用户在程序中定义了自己的数据类型名称,就可以在该程序中用自己的数据类型名称来定义变量的类型、数组的类型、指针变量的类型与函数的类型等。(方便用户使用)
#include <stdio.h>
// 为基本数据类型定义别名
// 给已有的变量类型重新起一个名字。
typedef int zhengshu; // 定义 int 的别名 zhengshu
typedef char zifu; // 定义 char 的别名 zifu
typedef double xiaoshu; // 定义 double 的别名 xiaoshu
int main()
{
zhengshu a = 10; // 使用 zhengshu 定义变量 a,并赋值为 10
printf("输出整数: a = %d \n", a); // 输出变量 a 的值,格式为整数
zifu c = 'H'; // 使用 zifu 定义变量 c,并赋值为 'H'
printf("输出字符: c = %c \n", c); // 输出变量 c 的值,格式为字符
xiaoshu X = 99.87; // 使用 xiaoshu 定义变量 X,并赋值为 99.87
printf("输出小数: X = %.2lf \n", X); // 输出变量 X 的值,格式为保留两位小数的浮点数
return 0; // 返回 0,表示程序正常结束
}
编程练习:typedef 使用
定义了一些基本数据类型的别名,以及结构体的别名,并展示了如何使用这些别名来声明变量和访问结构体的成员变量。
#include <stdio.h>
// 为结构体定义别名
struct Test {
int date;
int date2;
};
typedef struct Test T; // 给 struct Test 定义别名 T
// 定义一个函数 printIndo,参数为 T 类型的结构体变量 t
void printIndo(T t){
printf("%d\n",t.date2);
}
// 定义一个匿名结构体并给它定义别名 Demo
typedef struct
{
int data1;
int data2;
} Demo;
int main()
{
/* 正常输出 */
struct Test t1; // 声明一个名为t1的结构体变量
t1.date = 10; // 为结构体变量t1的成员变量x赋值为10
printf("%d\n", t1.date); // 输出结构体变量 t 的成员变量 date2 的值
T p1; // 等同于 struct Point p1;
p1.date2 = 100; // 为结构体变量p1的成员变量date2赋值为100
printIndo(p1); // 调用函数printIndo,并传入结构体变量p1
Demo d; // 声明一个名为d的结构体变量
d.data1 = 33; // 为结构体变量d的成员变量data1赋值为33
d.data2 = 44; // 为结构体变量d的成员变量data2赋值为44
printf("%d\n", d.data1); // 输出结构体变量d的成员变量data1的值
return 0;
}
typedef和结构体的案列。
#include <stdio.h> // 包含标准输入输出库
// 定义一个名为Person的结构体以及一个指向Person结构体的指针类型pPerson
typedef struct
{
int num; // 学号
char name[32]; // 名字,最多31个字符加上一个终止符
char sex; // 性别,'b'表示男,'g'表示女
} Person, *pPerson;
// 定义一个函数print1,传入一个Person结构体变量
void print1(Person p) // 传入一个名为p的结构体变量
{
printf("num:%d name:%s sex:%c\n", p.num, p.name, p.sex); // 打印结构体的字段
}
// 定义一个函数print3,传入一个Person结构体指针
void print3(Person *p) // 传入一个名为p的指针,指向Person结构体
{
printf("num:%d name:%s sex:%c\n", p->num, p->name, p->sex); // 打印结构体的字段
}
// 定义另一个函数print2,传入一个pPerson类型的指针
void print2(pPerson pp) // 传入一个名为pp的指针,类型为pPerson(即Person*)
{
printf("num:%d name:%s sex:%c\n", pp->num, pp->name, pp->sex); // 打印结构体的字段
}
int main()
{
// 初始化两个Person结构体变量
Person p1 = {2024001, "张三", 'b'}; // 定义并初始化结构体变量p1
Person p2 = {2024005, "李四", 'g'}; // 定义并初始化结构体变量p2
// 调用函数print1,传入结构体变量p1作为参数
print1(p1); // 打印p1的字段
// 调用函数print1,传入结构体变量p2作为参数
print1(p2); // 打印p2的字段
// 声明并初始化指向结构体的指针
pPerson pp1 = &p1; // 声明并初始化指针pp1,使其指向结构体变量p1
pPerson pp2 = &p2; // 声明并初始化指针pp2,使其指向结构体变量p2
// 调用函数print2,传入指向结构体p1的指针pp1作为参数
print2(pp1); // 打印pp1指向的结构体p1的字段
// 调用函数print2,传入指向结构体p2的指针pp2作为参数
print2(pp2); // 打印pp2指向的结构体p2的字段
// 声明并初始化指向结构体的指针(使用标准指针类型)
Person *pp3 = &p1; // 声明并初始化指针pp3,使其指向结构体变量p1
Person *pp4 = &p2; // 声明并初始化指针pp4,使其指向结构体变量p2
// 调用函数print3,传入指向结构体p1的指针pp3作为参数
print3(pp3); // 打印pp3指向的结构体p1的字段
// 调用函数print3,传入指向结构体p2的指针pp4作为参数
print3(pp4); // 打印pp4指向的结构体p2的字段
return 0; // 返回0表示程序成功执行
}