栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > C/C++/C#

C语言自定义类型:结构体,枚举,联合

C/C++/C# 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

C语言自定义类型:结构体,枚举,联合

C语言自定义类型:结构体,枚举,联合
  • 结构体
    • 结构体的基础知识
    • 结构体的声明
    • 匿名结构体类型
    • 结构体内存对齐
    • 结构体传参
  • 位段
    • 位段概念
    • 位段的声明
    • 解析
  • 枚举
    • 枚举的定义
    • 枚举的取值
    • 枚举的应用
  • 联合体
    • 联合体定义
    • 联合体的特点
    • 联合体大小的计算

结构体 结构体的基础知识

结构是一些值的集合,这些值成为成员变量。有些复杂的数据类型,不能单纯的由一个基本数据类型描述,可以用结构体描述。
如描述一个学生

变量名基本数据类型
姓名char
年龄int
性别char
成绩float
结构体的声明
struct tag//结构体类型名
{
	//member
	//这里定义成员变量
};//分号千万不能丢

可以紧跟在分号后面创建变量,创建的为全局变量,也可以直接在此进行初始化

struct tag//结构体类型名
{
	//member
}s1,s2;

如描述一个学生

struct Stu//
{
char name[20];//名字
int age;//年龄
char sex[5];//性别
char id[20];//学号
};
匿名结构体类型

声明的时候,可以省略结构体的名字

struct//这里的tag被省略了
{
	int a;
	int b;
}x;
结构体内存对齐

这里我们主要讨论一个问题:如何计算结构体类型的大小

struct S1
{
	char a;
	char b;
	int c;
};

struct S2
{
	char a;
	int b;
	char c;
};

int main()
{	
	printf("%d,%dn",sizeof(struct S1),sizeof(struct S2));
	return 0;
}

2个char1个字节,1个int4个字节,大小是占6个字节吗?

那如何计算? 首先得掌握结构体的对齐规则:
1.第一个成员在与结构体变量偏移量为0的地址处。
2.其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。
对齐数 = 编译器默认的一个对齐数 与 该成员大小的较小值。
VS中默认的值为8
Linux没有默认对齐数
3.结构体总大小为各个成员最大对齐数的整数倍。
4.如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍。

在s1中,char a变量大小为1,默认对齐数为8,对齐数取较小值,为1,
char b对齐数也为1。但int c变量大小为4,默认对齐数为8,取最小值则为4。但此时内存的使用偏移量为2,不为4的倍数,要到对齐到4的倍数(4)开始使用内存。最后得出大小为8。

如何理解第3点呢?

#include
struct S1
{
	char c1;
	double d1;
	int i;
};
int main()
{
	printf("%dn", sizeof(struct S1));
	return 0;
}

double类型对齐数为8,所以char类型后需要在偏移量为8的位置写下double类型,在偏移量为16下写下int类型,int占4字节,大小并不为20,而是24,因为需要为个成员最大对齐数(double类型)8的整数倍24。

如何理解第4点呢?

#include
struct S1
{
	char c1;
	double d1;
	int i;
};
struct S2
{
	char c2;
	struct S1 s1;
	int j;
};
int main()
{
	printf("%dn", sizeof(struct S1));
	printf("%dn", sizeof(struct S2));
	return 0;
}


S2中,因为嵌套了S1,S1中最大对齐数为8,char c2之后的偏移量为1,要在偏移量为8的地方写下,S1的大小为24,在偏移量为32的地方写下int,占4个字节,此时S2大小为36,不为最大对齐数8的倍数,所以要到40。

结构体传参
struct S
{
	int data[1000];
	int num;
};
struct S s = { {1,2,3,4},1000 };
//结构体传参
void print1(struct S s)
{
	printf("%dn", s.num);
}
//结构体地址传参
void print2(struct S* ps) {
	printf("%dn", ps->num);
}
int main()
{
	print1(s);//传结构体
	print2(&s); //传地址
	return 0;
}

print2比1好,传参的时候尽量使用指针,因为传值调用需要压栈,会重新复制一份结构体,导致性能的下降,而在函数内修改结构体的内容也必须用到指针。

位段 位段概念

这里的位,指的是二进制的位
位段可以同样使用结构体实现
位段的成员必须是int,unsigned int(标准的定义没有char类型,但经过实验,char类型也可实现位段)

位段的声明
struct A
{
	int _a : 2;
	int _b : 5;
	int _c : 10;
	int _d : 30;
};
解析

位段后面的数字,表示这个变量需要占用的比特位
如果剩下的空间不够下一个变量开辟,将直接使用下一个字节,剩下的比特位会被浪费掉。
位段设计出来的作用,是为了节省空间
比如,我们的学生类型中的分数变量,我们发现分数永远不会超过100分,所以我们只需要7个比特位存储,而不需要占用4字节,也就是32比特位了。
但是,位段有跨平台的问题,因为位段的设计并没有详细标准,各个编译器和系统可能不一样。

枚举

枚举类型就是把可能的取值,一一列举出来

枚举的定义
//enum枚举关键字

enum name//枚举类型名
{	
	//member list枚举成员名
};

比如定义一个星期
//enum为枚举类型关键字

enum Day
{
	MON,
	TUES,
	WED,
	THUR,
	FRI,
	SAT,
	SUN
};
枚举的取值

默认第一个变量为0,一次递增1,当然可以在定义中赋予初值,后面的变量会继续以被赋予的值继续加一。

enum Day
{
	MON=1,
	TUES,
	WED,
	THUR,
	FRI,
	SAT,
	SUN
};
枚举的应用

比如我们要实现一个计算器
写一个计算器的菜单
1.add 2.sub
3.mul 4.div
我们在代码的选项中可以这样写

switch (option)
case 1:
case 2:
case 3:
case 4:

用数字来代替选项,会带来许多不必要的麻烦,我们考虑可以使用枚举变量。

enum Option
{
	ADD=1,
	SUB,
	MUL,
	DIV
};

将菜单改为以下这样,就方便我们进行对照了

switch (option)
case ADD:
case SUB:
case MUL:
case DIV:
联合体

它的特征是成员变量共用一块存储空间

联合体定义
union Test
{	
	char c;
	int i;
}
联合体的特点

联合的成员是共用同一块内存空间的,这样一个联合变量的大小,至少是最大成员的大小(因为联合至少得有能力保存最大的那个成员)。

union U
{
	int i;
	char c;
}u;

int main()
{
	u.i = 0x11223344;
	u.c = 0x55;
	printf("%xn",u.i);
	return 0;
}

联合体大小的计算

联合的大小至少是最大成员的大小。
当最大成员大小不是最大对齐数的整数倍的时候,就要对齐到最大对齐数的整数倍。

union Un1
{
	char c[2];
	int i;
};

union Un2
{
	char c[5];
	int i;
};
int main()
{
	printf("%dn", sizeof(union Un1));
	printf("%dn", sizeof(union Un2));
	return 0;
}


Un1最大成员为i,大小为4个字节。Un2最大成员为数组c,大小为5个字节,但不是4的倍数,所以要占下一个整数倍(8)的大小。

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/1022317.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号