C语言的结构体, 共用体(联合体)

结构体的意义

之前学习的,整型浮点型,字符,数组,字符串等都是分散的数据,有时候我们需要很多类型的数来表示一个整体,比如学生信息包含整型的学号,字符串的名字,字符的性别,浮点型的分数等,这就是结构体存在的意义。结构体和数组的区别是,数组中的元素的类型一致,而结构体允许不同的类型的数据集合。

定义一个结构体:

struct Student //编程习惯要求结构体名字以大写开头
{
	int num;
	char name[32];
	char sex;
	int age;
	double score;
	char addr[32];
};    //不要忘记分号

以上的定义属于一个模板,在定义一个结构体时,一般不会先赋值。

结构体概念 

1. 每个成员都是结构体中的一个,也称为域表成员列表

2. 在申明一个结构体的时候在 结束大括号 和 分号 之间可以直接定义几个 属于这个结构体的变量 ,但是尽量不要这样。

3. 更好的定义和使用的方法:                                                                                                            

int main()
{
	int a =10;
	
	struct Student stu1;
	struct Student stu2;
	
    stu1.num = 1;//通过”.“运算符来访问结构体当中的成员(域)
	stu1.age = a;
	stu1.score = 98.5;
	strcpy(stu1.name,"majiaming"); //不能写成” stu1.name = "majiaming"; ”
	strcpy(stu1.addr,"上海");
	
	printf("学号:%d,年龄:%d,分数:%lf,名字:%s,地址:%s\n",
	stu1.num,stu1.age,stu1.score,stu1.name,stu1.addr);
		
	return 0;
}

或者使用更便利的方式:

	struct Student stu2 = {2,"张三",'g',12,99.5,"北京s"};

或者使用类似数组的结构:

int main()
{
	struct Student arr2[3] = 
	{{1,"张三",'g',17,99.5,"北京"},{2,"李斯",'m',14,69.5,"上海"},{3,"王五",'g',19,100,"深圳"}};
	
	int len = sizeof(arr2)/sizeof(arr2[0]); //len其实就是“struct Student arr2[3]”中的3
    
	for(int i=0; i<len; i++){
		printf("学号:%d,年龄:%d,分数:%lf,名字:%s,地址:%s\n",
		arr2[i].num,arr2[i].age,arr2[i].score,arr2[i].name,arr2[i].addr);
	}

	return 0;
}

使用结构体实现“选票系统”的应用例子:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

struct XuanMin
{
	char name[32];
	int tickets;
};    
	

int main()
{
	struct XuanMin xm[3];
	int i;
	int len = sizeof(xm)/sizeof(xm[0]);//len其实就是“XuanMin xm[3]”中的3
	int total = 5; //假设共有5人投票
	char tempName[32];
	int error = 0;
	
    //登记选民的名字(name)
	for(i=0; i<len; i++){       
		xm[i].tickets = 0;
		printf("请输入第%d个选民的名字:");
		scanf("%s",xm[i].name);
	}
	
	//登记投票情况(tickets)
	for(i=0; i<total; i++){ 
		printf("你是%d号投票人,请输入你要投的人名:(一人一票)\n",i);
		memset(tempName,'\0',sizeof(tempName)); //先将tempName变量内容清空
		scanf("%s",tempName);
		for(int j=0; j<len; j++){
			if(strcmp(tempName,xm[j].name) == 0){ //如果判断写的人名和候选人人名一致,则该候选人加一票
				xm[j].tickets++;
				break;
			}else{
				error++; //如果名字对不上则error增加1
			}
		}
		if(error == len){ //如果error和名字数量一样,则说明找不到对应的名字
			printf("没有找到对应候选人,你没有机会了,有请下一位投票者!\n");
		}
		error = 0; //error信号清零
	}

    printf("正在计算...\n");
	
	struct XuanMin max = xm[0];
	for(i = 0; i<len; i++){
		printf("候选人%d号姓名为:%s, 得%d票!\n",i, xm[i].name, xm[i].tickets);
		if(max.tickets < xm[i].tickets){
			max = xm[i];
		}else if(max.tickets == xm[i].tickets && i != 0){ //因为max一开始就等于xm[0],所以i=0的时候必然等于,没有意义!
			puts("检测到平票,本次投票结果作废,请重新投票");
			exit(-1);
		}
	}
	
	printf("最后宣布结果,名为%s的候选人以%d票位列第一,当选!",max.name, max.tickets);

	return 0;
}

 结构体指针  

通过结构体变量地址来访问该结构体需要一个变量来保持这个地址,这和之前说的指针其实是一样的,只不过指针的类型是结构体。

结构体指针定义方式和普通int型和char型指针定义类比,其实换汤不换药:

#include <stdio.h>

struct Test
{
	int idata;
	char cdata;
};

int main()
{
	int a;
	int *p = &a;
	
	char c;
	char *p = &c;
	
	struct Test t1;
	struct Test *ps = &t1; // “struct Test” 就相当于 “int" 或 “char”
	
	printf("t1的idata= %d\n",t1.idata);//变量名访问,用点运算符
	printf("t1的idata= %d\n",ps->idata);//变量名访问,用“->”运算符 !!!!
	
	
	return 0;
}

 那么也可以通过指针修改之前 Student 结构体的代码:

#include <stdio.h>
#include <string.h>

struct Student 
{
	int num;
	char name[32];
	char sex;
	int age;
	double score;
	char addr[32];
};    
	

int main()
{
	struct Student arr2[3] = 
	{{1,"张三",'g',17,99.5,"北京"},{2,"李斯",'m',14,69.5,"上海"},{3,"王五",'g',19,100,"深圳"}};
	
	struct Student *p;
	p = arr2;  //数组名依然是首地址,所以arr2不需要要取地址符&!!
	//struct Student *p = arr2; //也可以这样写
	
	int len = sizeof(arr2)/sizeof(arr2[0]);
    

	for(int i=0; i<len; i++){
		printf("学号:%d,年龄:%d,分数:%lf,名字:%s,地址:%s\n",
		p->num,p->age,p->score,p->name,p->addr); //直接用地址来调用
		
		p++;//使用地址调用后不要忘记每次循环结束前要将地址偏移到下一位
	}

	return 0;
}

同时,也可以用结构体指针,结合函数和二级指针改写选票系统:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

struct XuanMin
{
	char name[32];
	int tickets;
};    
	
void initXms(struct XuanMin **pxm, int *pt)
{
	if(*pxm == NULL){
		printf("请输入有几个人参选:\n");
		scanf("%d",pt);
		*pxm = (struct XuanMin*)malloc(*pt * sizeof(struct XuanMin)); //根据输入的人数多少来动态分配内存字节
	}
	for(int i=0; i<*pt; i++){
		(*pxm)->tickets = 0;
		printf("请输入第%d个选民的名字:\n",i+1);
		scanf("%s",(*pxm)->name);
		(*pxm)++;
	}
	*pxm = *pxm - *pt; //由于使用了二级指针,因此函数不需要返回值,*pxm的值就是结构体指针xm,但是由于pxm是个二级指针,所以可以直接影响结构体xm
	                   //所以,在函数结束的时候就有必要将*pxm(即xm)指回结构体首地址
}

void printXms(struct XuanMin *p, int len)
{
	for(int i=0; i<len; i++){
		printf("候选人%d号姓名为:%s, 得%d票!\n",i+1, p->name, p->tickets);
		p++; 
	}	
}

void doVot(struct XuanMin *p, int len)
{
	int error = 0;
	char tempName[32]; //注意这里一定是定义字符串变量,而不是*tempName这种字符串常量!!!
	struct XuanMin *pbak = p;
	
	for(int i=0; i<5; i++){ 
		printf("你是%d号投票人,请输入你要投的人名:(一人一票)\n",i+1);
		memset(tempName,'\0',sizeof(tempName)); //先将tempName变量内容清空
		scanf("%s",tempName);
		for(int j=0; j<len; j++){
			if(strcmp(tempName,p->name) == 0){ //如果判断写的人名和候选人人名一致,则该候选人加一票
				(p->tickets)++;
				break;
			}else{
				error++; //如果名字对不上则error增加1
			}
			p++;
		}
		if(error == len){ //如果error和名字数量一样,则说明找不到对应的名字
			printf("没有找到对应候选人,你没有机会了,有请下一位投票者!\n");
		}
		error = 0; //error信号清零
		p = pbak; //每次遍历结束要把指针重新指回开头
	}

}

struct XuanMin* getMax(struct XuanMin *p, int len)
{
	struct XuanMin *max = p;
	
	for(int i = 0; i<len; i++){
		printf("候选人%d号姓名为:%s, 得%d票!\n",i+1, p->name, p->tickets);
		if(max->tickets < p->tickets){
			max = p;
		}else if(max->tickets == p->tickets && i != 0){ //因为max一开始就等于xm[0],所以i=0的时候必然等于,没有意义!
			puts("检测到平票,本次投票结果作废,请重新投票");
			exit(-1);
		}
		p++;
	}
	
	return max;
}

int main()
{
	struct XuanMin *xm = NULL;
	struct XuanMin *final;
	int total = 0;
	
	struct XuanMin **pxm = &xm; //定义一个二级指针指向结构体指针xm的地址
	int *pt = &total;

	initXms(pxm,pt); 
	
	doVot(xm,total);
	
	final = getMax(xm,total);
	printf("%s以%d票当选!", final->name, final->tickets);
		
	return 0;
}

共用体/联合体

1. 有时候同一内存空间存放类型不同,不同类型的变量共享一块空间

2. 像结构体但是有区别:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

struct TestT //结构体
{
	int idata;
	char cdata;
	double ddata;
};    

union TestU //联合体
{
	int idata;
	char cdata;
	double ddata;
};    
	
int main()
{
	struct TestT t1;
	union TestU u1;
	
	printf("结构体t1的大小是:%d\n",sizeof(t1));
	printf("联合体u1的大小是:%d\n",sizeof(u1));
	
	return 0;
}

 联合体的大小取决于其内部数据中所占空间最大的那个类型的大小。对于这个例子,double占8个字节,比int(4) 和 char(1) 都大,所以联合体的大小就是8个字节。

如果打印这些变量的地址: 

	printf("idata:%p\n",&t1.idata);
	printf("cdata:%p\n",&t1.cdata);
	printf("ddata:%p\n",&t1.ddata);
	uu
	printf("idata:%p\n",&u1.idata)u;
	printf("cdata:%p\n",&u1.cdata);
	printf("ddata:%p\n",&u1.ddata);

可见,联合体的起始地址都是相同的,占用的是同一片内存空间。

 

联合体/共用体的数据覆盖

对于联合体,如果定义了联合体一个成员的值之后再定义另一个,然后要求打印最开始的那个值可能出现错误,因为公用的内存空间,第一个写的值可能被新的值覆盖。

使用联合体实现以下问题:“有若干个人员的数组,其中有学生和教师,学生的数据中必须包括:姓名,班级;教师的数据包括:姓名,科目。要求用同一个表格来处理

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

struct Person
{
	char name[32];
	char zhiYe;
	union S{
		int class;
		char keMu[12];
	}mes; //虽然结构体定义中不推荐直接创建变量,但是这是特殊情况,直接定义了结构体中一个名为mes的联合体变量	
};

int main()
{
	struct Person p[2];
	int i;
	for(i=0; i<2; i++){
		printf("请输入职业:t代表老师,s代表学生\n");
		scanf("%c",&(p[i].zhiYe));
		if(p[i].zhiYe == 's'){
			printf("请输入学生班级:\n");
			scanf("%d",&(p[i].mes.class));
			printf("请输入学生名字:\n");
			scanf("%s",&(p[i].name));
			getchar();//吸收回车!!否则第二次就无法识别s和t
		}else if (p[i].zhiYe == 't'){
			printf("请输入老师的科目:\n");
			scanf("%s",&(p[i].mes.keMu));
			printf("请输入老师名字:\n");
			scanf("%s",&(p[i].name));
			getchar();//吸收回车!!否则第二次就无法识别s和t
		}else{
			printf("无法识别,程序退出");
			exit(-1);
		}
	}
	
	for(i=0; i<2; i++){
		
		if(p[i].zhiYe == 's'){
			printf("学生的名字是:%s,班级是%d\n",p[i].name, p[i].mes.class);
		}else{
			printf("老师的名字是:%s,科目是%s\n",p[i].name, p[i].mes.keMu);
			
		}
	}	
	
	
	return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
C语言中的结构体是一种自定义的数据类型,可以包含多个不同类型的成员变量。通过结构体,我们可以将相关的数据组织在一起,方便操作和管理。结构体的定义使用关键字struct,具体的语法格式为: ``` struct 结构体名 { 数据类型 成员名1; 数据类型 成员名2; ... }; ``` 在定义结构体时,我们可以同时定义结构体变量,即创建一个结构体类型的实例。例如: ``` struct stu { char *name; int num; int age; char group; float score; } stu1, stu2; ``` 在这个例子中,stu是一个结构体类型,包含了name、num、age、group和score这五个成员变量。同时我们也定义了两个结构体变量stu1和stu2,它们都是stu这个结构体类型的实例。 另外,我们还可以匿名地定义结构体,即不给结构体命名,在定义结构体变量时直接使用结构体的定义。例如: ``` struct { char *name; int num; int age; char group; float score; } stu1, stu2; ``` 在这个例子中,我们没有给结构体命名,只是定义了一组与之对应的结构体变量stu1和stu2。 结构体变量的引用可以通过成员运算符.来访问结构体的各个成员变量。例如,对于上述的结构体变量stu1,我们可以通过stu1.name、stu1.num等来引用它的各个成员变量。 共用体(Union)是一种特殊的数据类型,它允许在相同的内存空间中存储不同类型的数据。与结构体类似,共用体也由多个成员变量组成,但是共用体中的成员变量共享同一块内存空间,只能同时存储其中的一个成员值。共用体的定义使用关键字union,具体的语法格式为: ``` union 共用体名 { 数据类型 成员名1; 数据类型 成员名2; ... }; ``` 共用体的使用方式与结构体类似,可以定义共用体类型并创建共用体变量。共用体变量的引用也可以通过成员运算符.来访问共用体的成员变量。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值