结构体内存对齐 (位段、联合union与大小端)


什么是结构体?

   相信对于已经学习或了解过C语言知识的伙伴们,对于结构体这个名词肯定不陌生,但是结构体相关知识你都了解吗?继续往下学习或者复习巩固一下吧。

结构体定义 及 声明

结构体定义:结构体是由一批数据组合而成的结构型数据。组成结构型数据的每个数据称为结构型数据的“成员” ,其描述了一块内存区间的大小及解释意义。

这样比较抽象的定义看完是不有点似懂非懂。
  

举个例子:结构体的声明

1
struct student                          struct tag       //类型名
{                                       {
	char name[10];      姓名                       
	char sex[3];        性别               member-list    //成员列表
	int age;            年龄
	char num[20];       学号            }variable-list;  //结构体类型定义变量
} s;        //必须要带分号

这个声明创建了一个名叫 s 的变量,s包含四个成员:{字符数组name、sex、num和整形 age}。

这是对于一个学生结构体类型的声明,其中 student 称为结构体的 类型名(标志);其中如 name,age等这些叫做结构体成员列表; 而xiaoming则是在声明结构体类型时定义的结构体变量(也可以在结构体声明后定义:struct student xiaoming)。

typedef struct

也可以用 typedef 对结构体类型进行重命名,这种写法比较常见,如下student结构体重命名为 stu更方便记忆与使用。即 stu 就是 struct student(这里与上面声明写法比较容易混淆在我当时刚学习时就一直没有搞懂)

2
typedef struct student
{
	char name[10];      //姓名
	char sex[3];        //性别
	int age;            //年龄
	char num[20];       //学号
}stu;

这时再看一遍定义,其实在C语言中结构体是一种数据结构,是一种由程序设计人自定义的数据类型,其中结构体成员列表其实可以理解为抽象出为数据的事物属性的集合;如上面的结构体 student 表示学生这一类型,即有姓名,性别,年龄和学号等属性组成的学生这一自定义类型,便是结构体。(自我见解,欢迎指正)
  
  

结构体变量的定义及初始化

有了上面的结构体和,如何对结构体变量进行定义与初始化

在这里插入图片描述

struct student s1 = { "zhangsan", "男", 21, "001" };    
  
stu s2 = { "xiaoming", "男", 20, "002" };
这两种初始化方式等价

注意:要在定义变量的同时赋初值,而不能先定义后赋值。
   
   

结构体内存对齐

那么c语言中,每种类型在相同平台都有固定的大小

(windows 32位平台)
(windows 32位平台)运行结果
   
   

那结构体在内存中是如何实际存储的呢?s1 和 s2 大小相等吗?分别为多少?

3
struct S1
{
    char a;        // 1
    char b;        // 1
    int c;          // 4
};
printf("%d\n", sizeof(struct S1));4
struct S2
{
    char a;        // 1
    int  b;        // 4
    char c;       // 1
};
printf("%d\n", sizeof(struct S2));

在这里插入图片描述

在这里插入图片描述

S1 与 S2类型成员一样,但占用空间大小却不同。
所以在设计结构体时既要满足对齐,又尽可能节省空间,将空间小的成员尽量集中到一起

为什么存在内存对齐?

1.平台原因(移植原因): 不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件平台只能在某些地址处取某些特定类型的数据,否则抛出硬件异常。

2.性能原因: 数据结构(尤其是栈)应该尽可能地在自然边界上对齐。因为处理器读写数据,并不是以字节为单位,而是以块(2,4,8,16字节)为单位进行的。如果不进行对齐,那么本来只需要一次进行的访问,可能需要好几次才能完成,导致效率低下。

所以内存对齐便是用少量空间换取时间来提高效率的一种方式

在这里插入图片描述
   
   

对齐规则

  1. 第一个成员一定对齐,位于结构体变量偏移量(offset)为0的地址处。

!  对齐数 = 编译器默认值成员自身大小 两者较小值   !

  1. 其他成员要对齐对齐数的整数倍的地址处。(整数倍(最小值) = 当前起始偏移量 / 对齐数)
    ( 当前起始偏移量 % min(成员大小对齐数) == 0)
    如果不能被整除就需要补齐字节使其能够被整除

  2. 结构体 总大小必须为最大对齐数(每个成员都有一个对齐数)的整数倍 。
    ( 当前存入变量的起始偏移量能否整除变量自身大小 )
    也就是sizeof结构体的结果,如果不能整除要补齐字节

  3. 如果嵌套了结构体的情况,先按规则计算出嵌套的结构体大小,最后结构体的整体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍。
       
       

如何计算内存大小

   遵循对齐规则进行计算

例题:

struct S3
{
	double d;
	char c;
	int i;
};

struct S4
{
	char a;
	struct S3 s3;
	int arr[5];
	char *p[3];
	char(*q)[4];
};

解析:

在这里插入图片描述
参考结果:(最大对齐数为8) S4 = 1 + (7)+ 16 + 20 + 12 + 4 + (4) = 64.
在这里插入图片描述
运行截图

修改默认对齐数

32位cpu上默认的指定对齐值是4字节,64位cpu上默认的指定对齐值是8字节

Visual Studio 中默认对其数 为 8
Linux 中默认对其数 为 4

通过#pragma 预处理指令,将默认对齐数修改。

5
#pragma pack (4)   //设置默认对齐数为 4
struct S1
{
    char a;        // 1
    char b;        // 1
    int c;          // 4
};

#pragma pack ()  //取消设置的对齐数,重置为默认

在这里插入图片描述
   
   

位段

C语言允许在一个结构体中以位为单位来指定其成员所占内存长度,这种以位为单位的成员称为“位段”或称“位域”( bit field) 。利用位段能够用较少的位数存储数据。

位段成员可以是int unsigned int char类型,在成员名后是 跟上给该成员分配的比特位个数

struct B{
	int num1 : 2;
	int num2 : 5;
	int num3 : 10;
	int num4 : 30;
};

在这里插入图片描述

ip数据报格式在这里插入图片描述
   
   

分配原理

在这里插入图片描述
位段内存对齐
只有相邻成员类型一致时才会有内存对齐

   
可以参考这个大佬实际运行测试的结果
位域(位段)用法,对齐机制

   

union 联合

在进行某些算法的C语言编程的时候,需要使几种不同类型的变量存放到同一段内存单元中。也就是使用覆盖技术,几个变量互相覆盖。这种几个不同的变量共同占用一段内存的结构,在C语言中,被称作“共用体”类型结构,简称共用体,也叫联合体。
   

union NU{
	char c;
	int i;
};

特点:联合的成员共用同一块内存空间; 联合体的大小至少是最大成员的的大小
在这里插入图片描述
输出结果:在这里插入图片描述
结构体大小也需要内存对齐到最大对其数字的整数倍

大小端

根据联合成员低位可以判断是大端存储还是小端存储

int main()
{
	un.i = 0x11223344;
	if (un.c == 0x11)
		printf("是大端\n");    // 高位在低地址
	if (un.c == 0x44)
		printf("是小端\n");    // 低位在低地址
}

在这里插入图片描述
我平时使用的电脑都是小端存储
在这里插入图片描述

在这里插入图片描述
小端:地址存储数据的位,高地址存数据的高位(小 低低高高)
大端:地址存储字节,低地址存数据的高位(大 高低高低)
   
   
   
   
码字不易,关注三连支持一波!

  • 15
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 9
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值