首页 > 系统相关 >结构体内存对齐(三板斧解决结构体的大小)

结构体内存对齐(三板斧解决结构体的大小)

时间:2023-11-10 19:32:49浏览次数:33  
标签:三板斧 成员 默认 编译器 大小 对齐 结构

(文章目录)

前言

我们知道,整型变量有自己的大小,浮点型变量有自己的大小,数组也有自己的大小,只要数据存放到内存中,就会占用内存大小。 所以作为C语言数据类型的一种——结构体 同样也有自己的大小。 要注意的是,结构体虽是多种数据类型的集合,但结构体的大小并不像我们想的那样简单地将每个结构体成员的大小相加就能得到的。

想要计算结构体的大小,需要先去了解计算结构体的规则!

一、结构体对齐规则

结构体的大小计算要遵循结构体的对齐规则

  1. 结构体的第一个成员永远都放在0偏移处。(即结构体的首地址处,即对齐到0处)
  2. 从第二个成员开始,以后的每个成员变量都要对齐到某个对齐数的整数倍的地址处。
  3. 当成员变量全部放进去后,结构体的总大小必须是,所有成员的对齐数中最大对齐数的整数倍,如果不够,则浪费空间对齐。
  4. 如果嵌套了结构体,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍。

对齐数 = 该结构体成员变量自身的大小 与 编译器默认的一个对齐数的 较小值注:VS中的默认对齐数为8。gcc环境下,没有默认对齐数。不是所有编译器都有默认对齐数,当编译器没有默认对齐数的时候,成员变量的大小就是该成员的对齐数。

二、结构体大小计算 - 三板斧

举例

struct S
{
	double a;
	char b;
	int c;
};

一板斧

找出每个成员变量的大小将其与编译器的默认对齐数相比较,取其较小值为该成员变量的对齐数。 注:VS2022默认对齐数为8.

在这里插入图片描述

二板斧

根据每个成员对应的对齐数找出它们在内存中的相对位置

在这里插入图片描述

三板斧

通过最大对齐数确定最终该结构体的大小

在这里插入图片描述

通过上面的分析图我们可以知道:

  • 紫部分(double a成员占用)+ 红色部分(char b成员占用)+ 黄部分(int ic成员占用)+红色与黄色之间的白色部分(浪费掉了)总共占用了16个字节的内存空间。
  • 再将总共占用的内存空间(16字节)与结构体成员的最大对齐数(8字节)相比较,此时16正好是8的整数倍,所以该结构体在VS编译器下的大小就16个字节。
  • 注意:如果成员变量占用的总字节个数不是其成员变量中的最大对齐数的整数倍,这时我们需要将其扩大到最大对齐数的整数倍。

三、为什么存在内存对齐?

  • 平台原因(移植原因):不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件平台只能在某些地址处取某些特 定类型的数据,否则抛出硬件异常。比如,当一个平台要取一个整型数据时只能在地址为4的倍数的位置取得,那么这时就需要内存对齐,否则无法访问到该整型数据。

  • 性能原因:数据结构(尤其是栈)应该尽可能地在自然边界上对齐。原因在于,为了访问未对齐的内存,处理器需要作两次内存访问;而对齐的内存访问仅需要一次访问。

总体来说:结构体的内存对齐是拿空间来换时间的做法。 在设计结构体的时候,我们既要满足对齐,又要节省空间,应该:让占用空间小的成员尽量集中在一起。

四、修改默认对齐数

当结构体的对齐方式不合适的时候,我们可以自己更改默认对齐数,要修改编译器的默认对齐数,需要借助于以下预处理命令:

#pragma pack()

如果在该预处理命令的括号内填上数字,那么默认对齐数将会被改为对应数字。 如果只使用该预处理命令,不在括号内填写数字,那么会恢复为编译器默认的对齐数。

#include <stdio.h>
#pragma pack()//取消设置的默认对齐数,还原为默认

#pragma pack(8)//设置默认对齐数为8
struct S1
{
	char a;
	int b;
	char c;
};
#pragma pack(1)//设置默认对齐数为1
struct S2
{
	char a;
	int b;
	char c;
};
int main()
{
	printf("%d\n", sizeof(struct S1));//结果为12
	printf("%d\n", sizeof(struct S2));//结果为6
	return 0;
}

标签:三板斧,成员,默认,编译器,大小,对齐,结构
From: https://blog.51cto.com/u_16312968/8306940

相关文章

  • 数据结构之线性表
    线性表之顺序存储:1sqlist.h2#ifndef_SQLIST_H3#define_SQLIST_H45#defineMAX_SIZE66typedefstruct7{8intdata[MAX_SIZE];9intlast;10}sqlist,*sqlink;1112voidcreatList(sqlinkL);//建空表13intgetLength......
  • 技术体系结构——架构&框架
    一、总体技术体系单一架构一个项目,一个工程,导出为一个war包,在一个Tomcat上运行。也叫allinone。单一架构,项目主要应用技术框架为:Spring、SpringMVC、Mybatis分布式架构一个项目(对应IDEA中的一个project),拆分成很多个模块,每个模块是一个IDEA中的一个module。每一个工......
  • 不使用递归,如何构造树结构
    原理很简单,利用对象引用特性。科普一下知识点:浅拷贝:浅拷贝又称为浅复制,浅克隆,浅拷贝是指拷贝时只拷贝对象本身(包括对象中的基本变量),而不拷贝对象包含的引用所指向的对象,拷贝出来的对象的所有变量的值都含有与原来对象相同的值,而所有对其他对象的引用都指向原来的对象,简单......
  • 数据结构-队列
    一、概念1、队列的定义队列是仅限在一端进行插入,另一端进行删除的线性表。队列又被称为先进先出(FirstInFirstOut)的线性表,简称FIFO。2、队首允许进行元素删除的一端称为队首3、队尾允许进行元素插入的一端称为队尾二、接口1、可写接口(1)数据入队队列的插入操作,叫做入队。它是......
  • 结构型模式-装饰模式
    1什么是装饰模式装饰模式(DecoratorPattern)是一种结构型设计模式,它允许将新功能动态地添加到对象中,通过将对象放入特殊的包装对象中,这样可以在不改变其接口的情况下,对对象的功能进行逐步扩展。在装饰模式中,通常包括以下几个角色:抽象构件(Component)、具体构件(ConcreteComponent)......
  • 数据结构入门 — 顺序表详解
    前言数据结构入门—顺序表详解关注博主,后期持续更新系列文章文章末尾有源码*****感谢观看,希望对你有所帮助*****文章目录前言一、顺序表1.顺序表是什么2.优缺点二、概念及结构1.静态顺序表2.动态顺序表三、顺序表接口实现(代码演示)1.动态存储结构2.顺序表打印3.顺序表初......
  • 数据结构入门 — 链表详解_双向链表
    前言数据结构入门—双向链表详解*关注博主,后期持续更新系列文章文章末尾有源码*****感谢观看,希望对你有所帮助*****系列文章第一篇:数据结构入门—链表详解_单链表第二篇:数据结构入门—链表详解_双向链表第三篇:数据结构入门—链表详解_循环链表文章目录前言系列文章什......
  • 19.5 Boost Asio 传输结构体
    同步模式下的结构体传输与原生套接字实现方式完全一致,读者需要注意的是在接收参数是应该使用socket.read_some函数读取,发送参数则使用socket.write_some函数实现,对于套接字的解析同样使用强制指针转换的方法。服务端代码如下所示#include<iostream>#include<boost/asio.hpp>......
  • 19.5 Boost Asio 传输结构体
    同步模式下的结构体传输与原生套接字实现方式完全一致,读者需要注意的是在接收参数是应该使用socket.read_some函数读取,发送参数则使用socket.write_some函数实现,对于套接字的解析同样使用强制指针转换的方法。服务端代码如下所示#include<iostream>#include<boost/asio.hpp>......
  • Golang struct 结构体注意事项和使用细节
    结构体所有字段在内存当中是连续的typePointstruct{ x,yint}typeRectstruct{ leftUp,rightDownPoint}funcmain(){ //r1会在内存当中有四个整数 r1:=Rect{ leftUp:Point{ x:1, y:2, }, rightDown:Point{ x:3, y:4, }, } //r1有......