自定义类型知识详解(结构体,位段,枚举,联合体)

目录

结构体

        定义,初始化,自引用

        内存对齐,修改默认对齐数

        传参

位段

        位段内存问题

        跨平台及其应用问题

枚举

        定义,初始化

        实战举例使用及其优点

联合体

        联合体定义

        特点

        大小计算问题


结构体

        定义,初始化,自引用

1:结构体的定义:

结构体是一些值的集合,这些值可以是不同类型的变量,被称为成员变量。

2:初始化:

三种初始化方式,被注释掉的代码只有在支持C99的编译器下才可以使用,他是乱序初始化,我们常用的是顺序初始化。

#include <stdio.h>

struct Student
{
	char name[8];
	int age;
	char sex[4];
}a = {"haha",4,"nan"};           //第一种


int main()
{
	struct Student c = { "gaga",6,"?" };   //第二种
	//struct Student b = { .age = 5, .name = "hehe", .sex = "nu" };   //第三种

	return 0;
}

这里还有一种特殊的声明方式: 

 这样声明的话只能在这里进行初始化,因为他没有名字,后面再想使用这个结构体,没有名字就无法创建该类型变量。

3:结构体的自引用:

这是错误用例,如果这样写的话,那么sizeof(struct Student)大小是多少呢?这样是算不出来的。

 接下来我们看正确用例:

这样,就是一个指针,指向这个结构体类型,就不会出现上面无限套娃的情况,再往后面加其实就是一个链表了。

再看重命名后的错误用例:

 Stu前面的代码(typedef是重命名,不算在类型里)都是这个结构体类型,也就是说Stu在结构体里是未定义的,然后去使用Stu,这是错误的,改正:

        内存对齐,修改默认对齐数

这里我们将说到结构体的大小计算,涉及到结构体的内存对齐。

我们先看一下这个结构体的大小,各位也可以先计算一下,看看结果是否正确。

 正确答案是:12个字节,你算对了吗?

结构体对齐规则:

        1:第一个成员变量在与结构体偏移量为0的位置。

 2:每个成员变量要对齐到他们各自的对齐数的整数倍处,也就是说偏移量是其对齐数的整数倍,起始偏移量为0,对齐数的大小为其sizeof的大小与默认对齐数比较后取小者。

3:每个编译器都有默认对齐数,VS为8个字节,计算结构体总大小时要看其最大对齐数,取成员变量中最大的对齐数,在每个成员变量各自对齐后,结构体所占内存总大小是将总和补到最大对齐数的整数倍。

4:如果嵌套了结构体,则嵌套的结构体的最大对齐数为其自身的最大对齐数,对齐到自身最大对齐数的整数倍处。

接下来我们来计算这个案例 

char ch 占一个字节,VS默认对齐数为8,取小,对齐数为1, 放在开头,int a占4个字节,比8小,对齐数取4,4偏移量是4的倍数,从4位置开始分配四个字节的内存空间,short同理,8是2的整数倍,从该处分配内存空间。

结构体中成员变量的对齐数中最大为4,与默认对齐数8相比取4,最大对齐数为4,总大小为4的倍数,则取12个字节。(图中占用字节为7个字节,浪费5个字节,但这是有意义的

 内存对齐的意义:

1:平台的移植性:不是每个平台都能访问任意位置的地址上的任意数据。

2:性能原因:

总的来说,结构体内存对齐就是用空间换取时间的做法 

        传参:

结构体传参最好传指针,如果传值,实参的临时拷贝对栈区的压力会增大。

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

typedef struct T
{
	int a;
	char b;
}T;

void Test(T* stu)
{
	printf("haha:%d", stu->a);
}

int main()
{

	T stu;
	memset(&stu, 0, sizeof(T));

	Test(&stu);

	return 0;
}

位段

1:位段成员必须是int ,signed int,unsigned int(char也可以)

2:位段成员名后面有一个冒号和数字(分配的bit位)

 A就是一个位段类型。

        位段内存问题

位段涉及很多问题,比如读取数据的方向,空间不足时数据存部分还是不存。

位段的空间开辟是按照开辟四个字节(int),或一个字节(char)的方式

        跨平台及其应用问题

 

枚举

        定义,初始化

enum A
{
	Monday,
	Tuesday,
	Wednesday,
	Thursday,
	Friday,
	Saturday,
	Sunday
};

使用enum创建枚举类型,从第一个枚举常量开始,若不给定值,默认为0,后面的枚举常量依次加1,我们看结果:

 当然,我们也可以给枚举常量赋初值:

        实战举例使用及其优点

 只能拿枚举常量给枚举变量赋值,才不会出现类型的差异。

 当然,硬要强制转换也是可以的,不推荐。

一般来说我们也不这么用,真正要用是在switch语句中使用,每一个case后的常量换成枚举常量。

比如:

case 0:我们可以换成case Add:(假设Add是枚举常量的第一个)

这样我们看起来就会方便一些,不会说一看0,不清楚这是做什么的。

联合体

        联合体定义:

一种特殊的自定义类型,接下来看声明:

        特点:

所有成员共用同一块空间,他们的起始地址都相同:

 一个union联合体的大小,至少是最大成员的大小。

        大小计算问题

整体大小也要对齐到最大对齐数的整数倍,上面举例的联合体大小为4个字节。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Lei宝啊

觉得博主写的有用就鼓励一下吧

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值