• 内存对齐模式 —— 原理讲解


    内存对齐模式定义:
    当前变量的首地址,与当前变量的首地址有关,即:若当前类型的类型是type,那么,当前变量的首地址必须是sizeof(type)的整数倍
    例如:定义这样一个结构体:
    typedef struct NEW_TYPE{
    int one;
    char two;
    short three;
    double four;
    };
    假设这个结构体第一个变量从编号为0的空间开始存储,那么,该结构体所占空间如下图所示:
    在这里插入图片描述

    那么,为什么计算机存储数据要使用内存对齐模式呢?
    首先,为了与之后所讲内存管理模式形成对比,我先提及一下非页式管理:
    非页式管理的原理是连续存储空间的分配与回收。
    而连续存储空间的分配与回收,会造成大量的内存碎片,这种碎片被称为“外部碎片”;而在碎片过多的情况下,会出现:所有可用空间之和满足要求,但是,任何一个空闲碎片都不满足要求而导致内存分配失败的现象。
    为了解决这个现象,可以进行“压缩”,即:将所有已分配空间移动在一起,将所有可用空间“合并”成一个大的可用空间,但,这种操作非常消耗系统资源,同时,要涉及复杂的“重定位”技术。

    总的来看,上述问题的根本原因是:内存的连续存储分配。
    而我们如果能够挣脱这个约束,那么,上述问题就能够解决了。

    为了解决上述问题,这里提出“页式分配方案”:

    1.将物理内存分成大小相同的若干“页”,这成为“物理页面”,并对这些“页”进行编号;
    2.将软件按物理页面大小进行分割,分割成若干“逻辑页面”,从0开始连续地对这些逻辑页面开始编号;
    3.按照逻辑页面的顺序,对于每一个逻辑页面,在物理页面中找“尚未使用的”页面,并分配给这些逻辑页面;
    4.分配的同时,生成由逻辑页号和物理页号作为数值对数值的集合,这个集合称为“页面表”。
    说明:地址映射过程如下:
    逻辑编号/物理页面大小(找相应的逻辑页号)
    按照页面表找到相应的物理页好
    逻辑编号%物理页面大小(找到在相应物理页面中的偏移量)
    上述的地址映射过程要进行两次除法,这是十分耗时的!
    为了加快这样的运算,可以及那个物理页面大小设置为2^n
    (因为:m/(2^n) <=> m>>n; m & (2^n); m % (2^n) <=> m & ((2^n) -1 ),这样的话,就完全转化为位运算了,能大大降低计算时间)

    那么,页式分配方案和内存对齐模式又有什么关系呢?
    可以这样假设,假如没有内存对齐模式,那么,数据将是连续存储,那么,根据页式分配方案,可能会出现一个数据被分成了两半,分别放在两个物理页面中,这样的话,读取一个数据就要进行两次内存访问,十分耗时,所以,内存对齐模式将极大地提高效率。

    那么,如果我们想要改变内存对齐模式的默认长度怎么办呢?
    下面引进 #pragma命令:

    pragma 命令是向编译器提供额外信息的标准方法,使用pragma 命令可能会导致代码可移植性降低,但是,最新版本的 GNU C 编译器和微软 Visual C 编译器都支持 #pragma pack(n),这个命令使得编译器让结构成员对齐到特定的字节边界(即n),默认情况下,n取4。

    而pack(1)指示每个结构成员必须对齐到字节边界,及连续存储,这样能够避免出现上面所提及的内存碎片。

    下面,我们来编写一段代码,来了解一下#pragma命令的使用方法吧:

    #include <stdio.h>
    
    #pragma pack(push)
    #pragma pack(1)
    
    typedef struct {
    	int one;
    	char two;
    	int three;
    	double four;
    }NEW_TYPE;
    
    int main() {
    	NEW_TYPE num = {
    		0x12,
    		'A',        // 0x41
    		0x135,
    		3.14,
    	};
    	FILE *fp;
    
    	fp = fopen("abcde.dat", "wb");
    	fwrite(&num, sizeof(MY_TYPE), 1, fp);
    
    	fclose(fp);
    
    	return 0;
    }
    
    #pragma pack(pop)
    

    这里的#pragma pack(push)是将系统默认的对其模式保留下来,而#pragma pack(pop)是将自己定义的对其模式删去,避免代码对计算机的修改导致有些功能无法运行。

    有关#pragma 命令的使用方法详解,请参考如下文章(这位博主对于#pragma命令的讲解比较透彻):
    《C语言#pragma使用方法》

  • 相关阅读:
    给video添加自定义进度条
    高德地图鼠标获取经纬度
    高德地图行政区域划分
    面试经验之谈
    Hybrid App 开发模式
    运维 08 常用服务安装部署
    运维 07 Linux系统基础优化及常用命令
    运维 06 vim与程序员
    运维 05 Shell基本命令
    运维 04 Shell基础命令(二)
  • 原文地址:https://www.cnblogs.com/codderYouzg/p/12410978.html
Copyright © 2020-2023  润新知