C语言当中,结构体、联合体与枚举属于自定义的数据类型,内存排布、参数传递等方面存在不少容易混淆的知识点。本文就结构体、联合体、枚举的基础概念、内存特征以及基本用法展开整理,记录学习当中的理解。
文章目录
- 结构体struct
- 什么是结构体
- 结构体内存对齐
- 结构体传参
- 联合体(共同体)union
- 什么是联合体
- 联合体大小及与结构体的区别
- 枚举enum
- 什么是枚举
- 枚举的使用
结构体struct
什么是结构体
struct可以把多种不同类型的数据打包在一起,用来描述一个对象。
示例一:结构体定义、创建变量、初始化,访问成员
#include<stdio.h>// 定义结构体模版,只是描述格式,不开辟内存structStu{charname[20];// 名字intage;// 年龄charsex[5];// 性别charid[20];// 学号};intmain(){// 按顺序给成员赋值初始化structStus1={"张三",20,"男","202308001"};// C99写法,指定成员赋值,顺序可以随便写structStus2={.age=18,.name="list",id="202308001",sex="女"};// 普遍结构体变量,用. 访问成员s1.age=21;// 结构体指针,用 -> 访问成员structStu*ps=&s1;ps->age=22;return0;}示例二:typedef 给结构体起别名
#include<stdio.h>// typedef 给结构体起别名,以后不用重复写structtypedefstructStu{charname{20};intage;}Student;intmain(){Student s;s.age=19;return0;}结构体内存对齐
计算结构体占用内存大小需要遵守三条对齐规则,目的是方便CPU读取数据。
1. 每个成员存放地址,必须是自身类型大小的倍数。
2. 结构体总大小,必须等于内部最大成员大小的倍数。
3. 不满足上面,编译器自动补空余字节,空余字节不能存数据。
特殊:#pragma pack预处理指令,可以修改编译器默认对齐方式。
#include<stdio.h>// 设置结构体的最大对齐数为1,取消内存填充#pragmapack(1)structS{charc1;// 1 1 1inti;// 4 1 4charc2;// 1 1 1};// 恢复编译器原本默认对齐数,后面代码不受pack(1)影响#pragmapack()intmain(){// 总大小:1 + 4 + 1 = 6字节printf("sizeof(struct S)=%d\n",sizeof(structS));return0;}结构体传参
两种传参方式:值传递、地址传递。
函数传参时,参数是压栈处理,会占用栈空间。如果结构体对象本身规模很大,参数压栈的系统开销就会比较大,容易导致栈溢出。结论:结构体传参优先传结构体的地址。
#include<stdio.h>structS{intdata[5];intnum;};// 值传递:把整个结构体复制一份传给函数// 函数修改的是复制出来的副本,不会改外面原数据;结构体大的时候开销大,存在栈溢出风险voidPrint1(structSs){inti=0;for(i=0;i<5;i++){printf("%d ",s.data[i]);}printf("\n%d\n",s.num);}// 地址传递:只传结构体地址,不复制全部数据,开销小,规避栈溢出问题// const:限制函数,不让修改外面原始数据;去掉const就可以修改原结构体voidPrint2(conststructS*ps){inti=0;for(i=0;i<5;i++){printf("%d ",ps->data[i]);}printf("\n%d\n",ps->num);}intmain(){structSs={{1,2,3,4,5},100};Print1(s);Print2(&s);return0;}联合体(共同体)union
什么是联合体
关键字union,里面可以放多种不同类型变量,全部共用同一块内存。改其中一个成员,别个成员就会被覆盖。
#include<stdio.h>// 定义联合体类型unionUn{charc;inti;};intmain(){unionUn un={0};// 创建联合体变量un.i=0x11223344;// 给int成员赋值printf("修改前 i = %#x\n",un.i);un.c=0x55;// 修改char,会覆盖 int 的低字节(共用内存)printf("修改后 i = %#x\n",un.i);printf("联合体大小:%d\n",sizeof(un));return0;}联合体大小及与结构体的区别
计算规则
- 联合体最少要等于最大那个成员的字节。
- 联合体的最大对齐数 = 联合体中最大基础类型成员的字节数
- 如果这个大小不满足最大对齐数的倍数,后面补空字节凑够倍数。
与结构体的区别
核心对比
- 内存:结构体每个成员各占一块内存;联合体全部成员共用一块内存
- 大小:结构体:所有成员字节相加,再补对齐空位;联合体:取最大成员大小,再补对齐空位
- 使用:结构体所有成员可以同时用;联合体同一时间只能用其中一个成员
枚举enum
什么是枚举
枚举用关键字enum表示,顾名思义就是一 一列举,把变量所有可能的取值全部罗列出来。
像星期,性别,三原色,取值有限,适合使用枚举。
规则:
- 不给常量写数值,第一个默认是
0,后面依次+1。 - 枚举本质是整型常量,底层占4字节。
- 枚举常量不可以被赋值修改。
示例
//星期enumDay{Mon,Tues,Wed,Thur,Fri,Sat,Sun};//性别enumSex{MALE,FEMALE,SECRET};//三原色enumColor{RED,GREEN,BLUE};枚举的使用
作用:替代程序中含义模糊的魔法数字,提升代码可读性,同时限制变量只能使用列举出来的合法值,减少程序出错。
示例