首页 > 系统相关 >一文告诉你什么是内存对齐?

一文告诉你什么是内存对齐?

时间:2023-06-02 14:33:06浏览次数:52  
标签:字节 int S2 内存 一文 对齐 读取

作者:@古明地盆
喜欢这篇文章的话,就点个关注吧,或者关注一下我的公众号也可以,会持续分享高质量Python文章,以及其它相关内容。:点击查看公众号


楔子

我们来解释一下什么是内存对齐,先来看个栗子:

#include <stdio.h>

typedef struct {
    long a;
    int b;
    char c;
} S1;

typedef struct {
    int b;
    long a;
    char c;
} S2;

int main() {
    printf("%lu %lu\n", sizeof(S1), sizeof(S2));  // 16 24
}

两个结构体的成员是一样的,只是顺序不同,就造成结构体实例的大小不同,这就是内存对齐导致的。现代计算机的内存空间都是按照 byte 划分的,从理论上讲似乎对任何类型的变量的访问可以从任何地址开始,但是实际的计算机系统对基本类型数据在内存中存放的位置有限制,它们会要求这些数据的首地址的值是 8 的倍数,这就是所谓的内存对齐。

32 位机器以 4 字节对齐,64 为机器以 8 字节对齐,我们后面就统一以 64 位机器举例。

为什么要进行内存对齐?

尽管内存是以字节为单位,但现在的 64 位处理器每次会以 8 字节为单位进行读取,假设没有内存对齐,那会发生什么呢?我们以上面的 S2 结构体为例。

因此没有内存对齐的话,在数据存储和读取的时候会做很多额外的工作。但有了内存对齐就不一样了,它保证了基础数据类型不会被读取两次。

内存对齐规则

内存对齐的规则很简单,首先按照 8 字节进行读取和存储,如果当前这个元素的字节数加上下一个元素的字节数超过了 8,那么该元素就要按照 8 字节进行对齐。我们实际操作一下:

#include <stdio.h>

typedef struct {
    // a 占 4 个字节,下面的 b 占 1 字节,加一起没有超过 8,所以不用管
    int a;
    // a 加 b 总共 5 字节,而下面的 c 是 4 字节,加一起超过了 8 字节,所以内存对齐
    // b 的下面会有 3 个字节的空洞
    char b;
    // 此时读取新的 8 字节,c 占 4 字节,d 占 1 字节,加起来没有超过 8 字节,所以不用管
    float c;
    // c + d 总共 5 字节,因此 8 字节块还剩下 3 字节,无法容纳下面是 8 字节的 e
    // 因此内存对齐,d 下面也会多出 3 字节空洞
    char d;
    // 重新读取 8 字节,e 占 8 字节
    long e;
    // 重新读取 8 字节,f 占 4 字节,下面的 g 也是 4 字节,加一起没有超过 8 字节
    float f;
    // f + g 正好 8 字节,正好存下
    int g;
    // 重新读取 8 字节,h 占 1 字节,剩余的 7 字节无法容纳占 8 字节的 i
    // 所以内存对齐,h 下面会有 7 字节的空洞
    char h;
    // 读取 8 字节,i 是 8 字节
    long i;
    // 读取 8 字节,k 是 4 字节,但由于 k 下面没有元素了,所以留下 4 字节的空洞
    int k;
} S;
// 因此,我们计算一下总大小
/* a + b = 8 字节,其中 3 字节的空洞
 * c + d = 8 字节,其中 3 字节的空洞
 * e = 8 字节,正好容纳,相当于 0 字节的空洞
 * f + g = 8 字节,正好容纳
 * h = 8 字节,因为下面的 h + i 超过了 8 字节,所以 i 必须单独读取,于是会留下 7 字节的空洞
 * i = 8 字节,正好容纳
 * k = 8 字节,因为每次都是按照 8 字节读取,即使不够 8 字节,由于下面没有元素了,因此留下 4 字节的空洞
 * 所以结构体 S1 实例总共占 56 个字节
 */

int main() {
    printf("%lu\n", sizeof(S));  // 56
}

我们来画一张图,图像是个好东西:

然后我们再来分析最开始的栗子,为什么 S1 和 S2 的大小会不一样:

#include <stdio.h>

typedef struct {
    // 读取 8 字节,存储 a
    long a;
    // 读取 8 字节,存储 b 和 c
    int b;   
    char c;
} S1;

typedef struct {
    // 读取 8 字节,存储 b,但剩余的 4 字节已无法存储 a,所以会有 4 字节的空洞
    int b;
    // 读取 8 字节,存储 a
    long a;
    // 读取 8 字节,存储 c
    char c;
} S2;

int main() {
    printf("%lu %lu\n", sizeof(S1), sizeof(S2));  // 16 24
}

所以这就是内存对齐导致的大小不一致,那么有没有办法改变内存对齐的方式呢?比如不按照 8 字节对齐,或者干脆紧密排列、不进行对齐。

#include <stdio.h>
#pragma pack(4)  // 按照 4 字节对齐,可选值为 1、2、4、8、16

typedef struct {
    // 读取 4 字节,此时没有空洞
    int b;
    // 读取 8 字节
    long a;
    // 读取 4 字节,存储 c
    char c;
} S2;

int main() {
    printf("%lu\n", sizeof(S2));  // 16
}

我们看到之前是 24 字节,现在变成了 16 字节,因为内存对齐的字节数被改变了。或者我们还可以禁止对齐:

#include <stdio.h>
#pragma pack(4)

typedef struct {
    int b;
    long a;
    char c;
} S2;

typedef struct __attribute__ ((__packed__)) {
    int b;
    long a;
    char c;
} S3;

int main() {
    printf("%lu %lu\n", sizeof(S2), sizeof(S3));  // 16 13
}

我们看到禁止内存对齐之后,数据会紧密排列,此时只占 13 个字节。

标签:字节,int,S2,内存,一文,对齐,读取
From: https://www.cnblogs.com/tomato-haha/p/17451666.html

相关文章

  • 一文读懂责任分配矩阵,解决你80%的项目难题
    成功的项目管理取决于整个团队对角色和职责的理解,使用责任分配矩阵分配和定义角色是使项目保持在正轨并为成功做好准备的好方法。如果设计得当,责任分配矩阵能够促进项目的成功交付。一、什么是责任分配矩阵责任分配(RACI)矩阵是项目管理工具,用于定义和跟踪团队成员在项目中的角色......
  • [UE4]资源异步加载(Assets Asynchronous Loading)与内存释放(Free Memory)
    为什么需要异步加载资源,因为当一次性加载的资源较多或者单个资源较大时,普通的LoadObject()方式会阻塞引擎的主线程。 假设测试工程叫TestTD4,自定义Character叫ATestTD4Character(头文件为TestTD4Character.h)假设在Content/Assets/目录下放了三个动画文件(AnimSequence)。异步加......
  • 内存与地址
    内存1.存储体的存储结构存储的分层思想被用来划分存储体的实际结构,这可以更加有效的管理程序容量:从上往下容量依次减小访问速度:从下往上则越来越快2.无存储体的存储结构为什么要从无存储的存储结构去了解内存,老实讲,一上去就了解内存的分段分页机制,并不懂得......
  • 【十八】id()函数 -- 判断内存空间地址(1)
    【十八】id()函数--判断内存空间地址(1)【1】作用在Python中,id()函数用于返回一个对象的唯一标识符。这个标识符是一个整数,代表了该对象在内存中的地址。换句话说,每个对象都有一个唯一的标识符,即使它们的值相同也是如此。【2】语法id(object)#其中,object是要返回标识......
  • 03-计算机体系结构及内存分层体系
    03-计算机体系结构及内存分层体系计算机体系结构/内存分层体系计算机体系结构CPU/内存/IO内存分层体系CPU寄存器一级缓存寄存器和一级缓存是在CPU内部的,速度很快3.6GHz,容量很小二级缓存在微处理器内部速度稍快主存速度快1.3GHz硬盘(虚拟内存)速度慢5ms(寻道时间)在......
  • glibc堆内存分配算法
    对于小于64字节的空间申请是采用类似于对象池的方法;对于大于512字节的空间申请采用的是最佳适配算法;对于大于64字节而小于512字节的,它会根据情况采取上述办法中的最佳折中策略;对于大于128KB的申请,它会使用mmap机制直接向操作系统申请空间。 空闲链表(最佳适配算法)将堆中各个空......
  • volatile与java内存模型
    一、结论先说结论,volatile能保证可见性和有序性,不能保证原子性。二、volatile的内存语义当写一个volatile变量时,会将变量值刷新回主内存当读一个volatile变更时,会从主内存中读取最新值三、内存屏障是什么?内存屏障是一类同步屏障指令,是cpu或编译器在对内存随机访问操作的一......
  • 二级指针内存模型
    二级指针做输出模型#define_CRT_SECURE_NO_WARNINGS#include<stdlib.h>#include<string.h>#include<stdio.h>//指针做输出:被调用函数分配内存-----OK//指针做输入:主调用函数分配内存//求文件中的两段话的长度intgetMem(char**myp1,int*mylen1,char**myp2,in......
  • IDEA虚拟内存优化
      按照自己需求更改即可(下面是本人的,本人电脑16G内存):   ......
  • 如何查看redis占用内存大小
    http://www.daixiaorui.com/read/209.html#Memoryused_memory:13490096//数据占用了多少内存(字节)used_memory_human:12.87M//数据占用了多少内存(带单位的,可读性好)used_memory_rss:13490096 //redis占用了多少内存used_memory_peak:15301192//占用内存的峰值(字节)used_memory_p......