自定义类型:结构体+枚举类型+联合体+(内存对齐原则)

结构体

定义

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

结构的声明

🎄🎄格式:

struct  结构类型

{

成员变量;

};

举例:

struct Stu
{
 char name[20];//名字
 int age;//年龄
 char sex[5];//性别
 char id[20];//学号
};//分号不能丢

当然,也可以不完全声明,也就是匿名结构,不给出结构的名字。

问题:

​
//匿名结构体类型
struct
{
 int a;
 char b;
 float c;
}x;
struct
{
 int a;
 char b;
 float c; 
}a[20], *p;

​

当两个结构都省略掉类型(标签)的时候,那么  p=&x 这个用法就是不对的。

因为编译器会把它们当做完全不同的两个类型,所以是非法的。

  

结构的自引用:

举例:

struct Node
{
    int data;
    struct Node* next;
}

记住是 类型+*,不能忽略掉*

问题:

typedef struct
{
 int data;

 Node* next; 
}Node;

这样是可以的吗,答案是不行的,应该写成这样

typedef struct Node
{
 int data;
 struct Node* next; 
}Node;

这里还没typedef定义完成就已经开始使用了,很明显是错误的。

  

结构体的定义

1,

 定义一个结构体时

逐个给定值

2,

在初始化的同时给定值。

3,

在定义结构体的时候在末尾顺便加上一个个体名.

🚀🚀结构体内存对齐(重点

 光看文字很难理解,我们加上例题分析:

例题1:

🎄🎄 第一个成员变量从地址0处开始,vs中默认的对齐数是8,char类型大小是1,选择其中较小值作为对齐数,所以是1。而int类型的i的对齐数是4(类型大小或者默认对齐数的较小值),要对齐到对齐数的整数倍开始,也就是从4开始存储了,也就是最后存储完对齐的地址是8,char同理算得对齐数是1,所以对齐到地址9地方。然后结构体总大小要是最大对齐数的倍数,也就是要是4的倍数,所以自动增到12为止。

 如图,最后对齐的大小就是12个字节了。

例题2:

 例题2算得16.

🎄🎄第一个double对齐数算得是8,所以占用内存0~7总共八个字节,第二个char类型对齐数是1,占用第八个字节, 最后一个int从12开始占用12-15四个字节,因为对齐原则,最终要对齐到最大的对齐数8的倍数,所以是16个字节。

例题3:

由这句话可以知道s3的对齐数是8,对齐大小是16个字节。 

🎄🎄c1对齐第一个字节,然后s3对齐到8开始占用,占用8~23这16个字节,d的对齐数是8,从24开始占用8个字节大小。最终占用0~31这32个字节。

🛸🛸内存对齐这么复杂,为什么要有这东西呢?难道是为了给自己添加麻烦吗?

当然不是!

 首先,每个硬件平台不一定都可以随意访问任意地址上的数据,有些只能访问指定的地址处的数据,所以为了能够获取到需要的东西,设置了这个规则。

 其次,内存的获取是一次四个字节,像图中第二种方式,那么获取一个int型就要获取两次,而第一种只需要一次,在性能上大大改善了。

 

总的来说:结构体内存的对其就是拿空间换取时间的做法。

   

修改默认对齐参数

我们要用到 #pragma 这个预处理指令

如图,#pragma pack(8)开始, #pragma pack()结束,这中间的句子中,就是修改了默认对齐数的。其中第一个括号中的数字就是默认对齐数。修改的数字应该为2的次方数,例如:1,2,4,8……

  

位段

什么是位段?以一段代码为例:
struct A {
 int _a:2;
 int _b:5;
 int _c:10;
 int _d:30;
};

其中A就是一个位段类型。

位段与结构体是类似的,但有两个不同:

1.位段的成员必须是 int、unsigned int 或signed int 。
2.位段的成员名后边有一个冒号和一个数字。

🛸🛸位段的内存分配

1、位段的成员可以是 int unsigned、int、signed int 或者是 char(整形家族)类型。

2、位段的空间上是按照需要以四个字节或者一个字节(char)的方式来开辟的。

3、位段涉及很多不确定因素,位段是不跨平台的,注重可移植的程序应该避免使用位段。

例子:

//一个例子
struct S {
 char a:3;
 char b:4;
 char c:5;
 char d:4;
};
struct S s = {0};
s.a = 10; 
s.b = 12;
s.c = 3;
s.d = 4;

冒号后面的数字是规定使用的比特位大小。

那么空间是如何开辟的呢?

我们可以知道,10转化为二进制的代码是1010,可是 只能用3个比特位,所以取出010来存储,12二进制是1100,取用四个比特位,全部放进去,而存储的时候是四个。存储时,四个二进制为是一个十六进制位。按照这样计算,得到的数字是6、2、3、4。所以地址中的存储是,620304,也验证了我们的猜想,而且我们也可以知道,位段在一个字节中浪费的位置,下一个变量如果不够的话不会继续使用,而是开辟新的字节并在其中存储。

枚举类型

顾名思义,就是一一列举

枚举类型的定义

enum Day//星期
{
 Mon,
 Tues,
 Wed,
 Thur,
 Fri,
 Sat,
 Sun
};

其中定义的Day就是一个枚举类型,其中一一列举了一个星期七天。{}中的内容是枚举类型的可能取值,也叫枚举常量 。这些可能取值都是有默认值的,默认从0开始,逐个+1。当然也可以在最开始给定初值,但是在外面就不能改变了,因为这是一个常量。

🛸🛸枚举的好处

明明可以用 #define 代替,为什么还要用枚举呢?

1、增加代码的可读性和可维护性。

2、和 #define 定义的标识符相比,枚举有类型检查,更加的严谨。

3、为了防止命名污染(两个变量命名相同)。因为枚举类型是封装在一个类型中,是局部变量。

4、便于调试。#define是不可调试的,在预处理阶段就已经改变。

5、便于使用,一次可以定义多个常量。

联合(共用体)

联合也是一种特殊的自定义类型 这种类型定义的变量也包含一系列的成员,特征是这些成员公用同一块空间(所以联合也叫共用体)。
//联合类型的声明
union Un
{
 char c;
 int i;
};
//联合变量的定义
union Un un;
//计算连个变量的大小
printf("%d\n", sizeof(un));

其中的c和i这两个变量是共用同一块内存的。un的大小为4。当然,不可能就是最大成员的大小,那样也过于简单了。

其中的变量同时只能用一个,因为会互相改变值.

依靠这个原理,我们又有了一种测试大小端的方法:

🎄🎄内存计算原则:        >=最大的变量大小

                                        是最大对齐数的倍数         

比如:        

        

 其中short s[7]的大小是14,对齐数是4,所以最后大小是对齐到16。

这里可以将数组看作是七个short类型的变量。

非常感谢各位的观看!

  • 11
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 11
    评论
评论 11
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

青衫哥

你的鼓励将是我创作的最大动力

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

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

打赏作者

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

抵扣说明:

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

余额充值