结构体内存对齐

为什么有内存对齐

1. 平台原因(移植原因):
不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件平台只能在某些地址处取某些特定类型的数据,否则会抱出硬件异常。
因为:(基于这个原因,就要把某些数据对齐到能够取得、访问、修改、的地址处,这样才能去获得对应数据)

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

什么意思?

以结构体S为例

struct S
{char c;int i;
};

 

如果没有对齐,char c;存放了之后,int i; 直接存放在char c后面,可能有时候对齐时刚好会出现这种情况,但不是每一次都这样。我们直到计算机有32位、64位,也就是每次读取的数据4个字节、8个字节。以4个字节为例,如果没有对齐,读取 c 时会将 i 的也读取一部分出来,导致出错。如果读取 i ,因为没有对齐,数据从0号开始读,i数据没读完,还要再读一次,浪费时间。在对齐的内存空间上就不会出现这种情况。读取 i 时可以从4号位开始读,这样就可以一次性读取完 i 的数据,不用担心数据读取不完全或者错误。


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

 

规则

1. 第一个成员在与结构体变量偏移量为0的地址处。
2. 其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。
对齐数 = 编译器默认的一个对齐数 与 该成员大小的较小值。(VS中默认的值为8)
3. 结构体总大小为最大对齐数(每个成员变量都有一个对齐数)的整数倍。
4. 如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整
体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍。

gcc,Linux中没有默认对齐数,对齐数就是成员自身的大小,默认对齐数可以修改 #pragma pack() 

括号填入4、8即可,填入1就认为没有对齐数。

规则解释

假设现在有结构体S1,现在要求该结构体的内存大小

struct S1
{char c1;int i;char c2;
};printf("%d\n",seziof(struct S1));//结果是12

规则1解释:

很容易理解,就是不管什么类型,只要是结构体的第一个成员就要从0号偏移地址开始往下存储。

所以char c1;从0偏移处开始存放,自己是多大的字节就占多少个内存地址

 规则2解释:

我使用的是VS编译器,对默认齐数是8(gcc,linux系统没有对齐数,其实也无所谓,反正对齐数就是该类型的字节大小)。这里我们的第二个成员是 int 型的,占4个字节大小,如果考虑默认的对齐数则:int i;的字节大小是4,VS默认是8,这两者的较小值就是4,所以4就是对齐数。

              则 i 存放在偏移量是4的倍数的地址上,比如4,8,12...

              所以 i 从4号地址开始往下存放,i 是int型,所以从4到7都是i占据的内存

规则3解释:

在c1,i,c2中,对齐数分别是1,4,1
则在这个结构体里面最大的对齐数就是4
在存放过程中,c1,i,c2已经存放完毕,占用0-8一共9个字节地址
但是9不是最大对齐数4的倍数,所以还要往下浪费一些空间直到该地址偏移量是4的倍数
所以再往下浪费三个地址到11号地址处(这里的号数只是为了方便理解取的)
在做题过程中要记住的是偏移地址的个数,而不是自己设定的号数

验证方法

为了验证结论,可以使用offsetof函数打印存放的号数是不是和我们的一样。
这个函数的头文件是 #include <stddef.h>

函数原型:

size_t offsetof( structName, memberName );

第一个参数是结构体的名字,第二个参数是结构体成员的名字。该宏返回结构体structName中成员memberName的偏移量。偏移量是size_t类型的。

printf("%d\n",offsetof(struct S1,c1));
printf("%d\n",offsetof(struct S1,i));
printf("%d\n",offsetof(struct S1,c2));

规则4解释:

 计算S4的内存大小

struct S3
{double d;char c;int i;
};struct S4
{char c1;struct S3 s3;double d;
};

先计算S3的内存大小是16;

在S4里的S3的最大对齐数是8(double d;),所以S3存放的对齐数要是8的整数倍;从8号位开始存放结构体S3,一共占据16个字节大小。第三个成员double d;的字节大小是8,对齐数也是8,下面是8的倍数的近距离刚好是24,所以从24号开始存放到31号刚刚好8个字节。

此时占了32字节,在这两个结构体中,所有对齐数里面最大的就是8,32是8的倍数,所以不需要往下浪费地址

切记

对齐数的倍数从0号地址开始,用偏移号数来找对齐的倍数。

什么意思:比如结构体的第一个char a;肯定从0号位开始存,而且char只占用一个字节地址;

第二个是double b;本身字节大小是8,对齐数也是8,存放时就要是8的倍数,此时应该从0号开始数8个地址,0-7刚好是8个位,8-15地址号存放double b。

如图:

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/268130.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

STM32-GPIO编程

一、GPIO 1.1 基本概念 GPIO&#xff08;General-purpose input/output&#xff09;通用输入输出接口 --GP 通用 --I input输入 --o output输出 通用输入输出接口GPIO是嵌入式系统、单片机开发过程中最常用的接口&#xff0c;用户可以通过编程灵活的对接口进行控制&#xff0c;…

【数据结构】平衡树引入

数据结构-平衡树 前置知识 二叉树二叉树的中序遍历 问题 维护一个数据结构&#xff0c;支持插入元素、删除元素、查询元素的排名、查询排名对应的元素、查询元素的前驱、查询元素的后继等。 BST&#xff08;二叉搜索树&#xff09; 作为一个基本无效&#xff08;很容易卡掉…

【Linux】线程的概念理解,从感知理解到全面深入

1.初始线程概念 在伟大的”计算机哲学“操作系统这本书中&#xff0c;一般给出线程的概念为&#xff1a;是在进程内部运行的一个执行分支&#xff08;执行流&#xff09;&#xff0c;属于进程的一部分&#xff0c;粒度要比进程更加细腻和轻量化。大家对这一概念一看而过既可以…

Vuex快速上手

一、Vuex 概述 目标&#xff1a;明确Vuex是什么&#xff0c;应用场景以及优势 1.是什么 Vuex 是一个 Vue 的 状态管理工具&#xff0c;状态就是数据。 大白话&#xff1a;Vuex 是一个插件&#xff0c;可以帮我们管理 Vue 通用的数据 (多组件共享的数据)。例如&#xff1a;购…

C语言--每日练习题--Day38

第一题 1. 下列代码的运行结果&#xff08;&#xff09; short i 65537; int j i 1; printf("i%d,j%d\n", i, j); A&#xff1a;i 65537&#xff0c;j 65538 B&#xff1a;i 1&#xff0c;j 2 C&#xff1a;i -1&#xff0c;j 0 D&#xff1a;i 1&#xff…

使用JSON-Server快速搭建RESTful API接口

​​​​​​​ 概要 随着前端技术的快速发展&#xff0c;前后端分离已经成为了一种趋势。在前后端分离的架构中&#xff0c;前端需要与后端进行数据的交互&#xff0c;这就需要后端提供RESTful API接口。而在开发过程中&#xff0c;我们常常需要模拟后端数据接口&#xf…

交友系统:打造独具魅力的社交平台!APP小程序H5三端源码交付,支持二开!

随着社交媒体的兴起&#xff0c;交友系统成为了现代社会不可或缺的一部分。人们希望通过网络结识新朋友&#xff0c;拓展社交圈&#xff0c;寻找志同道合的伙伴&#xff0c;甚至找到自己的爱情。本文将为您介绍交友系统的定义、功能以及如何打造一个独具魅力的社交平台。 一个成…

SQL中的三值逻辑:TRUE、FALSE 和 UNKNOWN。

在SQL中&#xff0c;通常采用三值逻辑处理条件表达式的真值。这种逻辑是基于三种可能的真值状态&#xff1a;TRUE、FALSE 和 UNKNOWN。 TRUE&#xff08;真&#xff09;&#xff1a; 表示条件为真或成立。 FALSE&#xff08;假&#xff09;&#xff1a; 表示条件为假或不成立。…

全局代理IP的工作原理和实现方法

目录 前言 一、全局代理IP的工作原理 1.代理服务器 2.代理协议 二、全局代理IP的实现方法 1.构建代理服务器 2.实现数据转发 3.使用代理服务器 4.启动代理服务器 三、全局代理IP的代码实现 四、总结 前言 在网络中&#xff0c;代理服务器是一种获取网络资源的方式。…

万界星空科技电子装配行业MES解决方案

电子电器装配属于劳动密集型、科技含量较高的行业&#xff0c;产品零部件种类繁多&#xff0c;生产组装困难&#xff0c;生产过程存在盲点&#xff0c;同时也决定了生产流水线多且对自动化水平要求较高。 万界星空科技提供的电子行业MES解决方案&#xff0c;提供从仓储管理、生…

源码级详解Spring的三级缓存,循环依赖的处理流程

一.什么是三级缓存 1.一级缓存&#xff1a;存放已经初始化完成的Bean 2.二级缓存&#xff1a;存放半成品Bean&#xff0c;既实例化完成未初始化的Bean。 3.三级缓存&#xff1a;存放bean工厂 二.为什么是三级缓存 一级缓存是必须的&#xff0c;这个我们没有什么疑问。那为…

Flutter的BuildContext简介

文章目录 BuildContext 简介BuildContext的主要作用 BuildContext 简介 BuildContext是Flutter中的一个重要概念&#xff0c;表示当前Widget在树中的位置上下文。它是一个对Widget树的一个位置的引用&#xff0c;用于查找、访问和操作该位置上的相关信息。每个Widget都有一个关…