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,%d\n",sizeof(struct S1),sizeof(struct S2));
	return 0;
}

2个char1个字节,1个int4个字节,大小是占6个字节吗?
C语言自定义类型:结构体,枚举,联合_第1张图片
那如何计算? 首先得掌握结构体的对齐规则:
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("%d\n", 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("%d\n", sizeof(struct S1));
	printf("%d\n", 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("%d\n", s.num);
}
//结构体地址传参
void print2(struct S* ps) {
	printf("%d\n", 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("%x\n",u.i);
	return 0;
}

在这里插入图片描述

联合体大小的计算

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

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

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

在这里插入图片描述
Un1最大成员为i,大小为4个字节。Un2最大成员为数组c,大小为5个字节,但不是4的倍数,所以要占下一个整数倍(8)的大小。

你可能感兴趣的:(C语言学习,c语言,开发语言)