• 关于内存对齐的一点理解


    关于内存对齐的一点理解

    写在前面

    实验室的事情算是暂时告一段落了,整理了一个关于静态网格水印的论文,已经投递了,接下来就是设计网格序列的水印算法了。差不多陆陆续续的准备找实习了。到目前为止面了两次面试,感觉面试官其实更加希望面试者对底层的一些知识有比较深入的了解,他们希望面试者不仅会使用某个语言或者工具,更希望面试者了解其中的原理。算了,废话不说了,说说我在面试中遇到的一些问题,以及面试归来对这些问题的思考吧。整理这篇文章的目的是面试官问了我知不知道内存对齐,当时听到这个词的时候竟然没有想到这个知识点,回来之后我才想起,其实C++课上老师是有提及过的,只是可能平时没怎么接触,突然就陌生了。在此写点小程序,加深对这个知识点的认识。

    什么是内存对齐

    内存对齐,也称字节对齐,它主要是指数据存储在内存中时,应该放置在地址是数据项大小的整数倍的内存位置上。

    为什么要内存对齐

    关于内存对齐的原因,参考了别人的说法,大概是有下列两方面的原因:
    1、平台原因(移植原因):
    不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件平台只能在某些地址处取某些特定类型的数据,否则抛出硬件异常。
    2、性能原因:数据结构(尤其是栈)应该尽可能地在自然边界上对齐。原因在于,为了访问未对齐的内存,处理器需要作两次内存访问;而对齐的内存访问仅需要一次访问。

    对齐规则

    1、对结构体的各成员来说,第一个成员位于偏移量为0的位置,之后的数据成员偏移量必须是min(#pragma pack()指定的数,该数据成员自身长度)的倍数,#pragma pack()默认值是8
    2、数据成员对齐后,结构体本身也要进行对齐,对齐按照min(#pragma pack()指定的数,结构体中最大数据成员)的值进行。

    实际案例

    #include<iostream>
    #include<cstdio>
    using namespace std;
    
    struct str1
    {
        char a;
        int b;
        short c;
        double d;
    };
    
    struct str2
    {
        double d;
        short c;
        char a;
        int b;
    };
    
    int main()
    {
        cout<<"sizeof str1 = "<<sizeof(str1)<<endl;
        cout<<"sizeof str2 = "<<sizeof(str2)<<endl;
        system("pause");
        return 0;
    }
    

    运行结果如下:
    enter description here

    从程序中可以看出,str1和str2的数据成员类型都相同,但是声明的顺序不同,结果sizeof的得到的结果就不同,这就是内存对齐的一个很好的证明。如果没有内存对齐的话,两个str占用的字节数应该是:
    char 1 + int 4 + short 2 + double 8 = 15。

    结果解释

    根据对齐规则,逐一来看结构体的数据成员在内存中是如何摆放的,下图是在ppt中绘制的一个过程图。
    enter description here
    因此可以看出,sizeof(str1)的结果是24,对于str2,也是一样的过程,这个过程如下图:
    enter description here

    扩展

    在内存对齐中用到的指令为#pragma pack(),这个指令理默认的参数8。实际上,也可以自行设置这个参数,以控制对齐结果。比如在程序中加入下列语句:

    #pragma pack( 1 )
    

    结果如下:
    enter description here
    这个时候相当于没有进行任何对齐。

    另外,如果char数据成员最后声明的话,为了保证结构体的对齐,char后面也会进行填充,使得填充后的下一个地址可以被min(#pragma pack(), 结构体中最大数据成员)整除

    struct str4
    {
    
    };
    

    “空结构体”(不含数据成员)的大小不为0,而是1。试想一个“不占空间”的变量如何被取地址、两个不同的“空结构体”变量又如何得以区分呢于是,“空结构体”变量也得被存储,这样编译器也就只能为其分配一个字节的空间用于占位了。

    感悟

    怎么说呢,这两次面试给我的感触还是蛮深的。感觉不管是使用那个语言或者使用哪种平台进行开发,作为一个开发者,不能只是停留在表面,不能只是会使用一些工具,更重要的是要深入理解底层的原理和细节,对这些细节的理解能够帮助你在思考的时候想的更加全面,同时编写的代码也更加鲁棒。举一个不是很恰当的例子,写代码好比追女孩,要想最终征服一个女孩子,不能只是关注她的外表的东西,还要深入她的内心,啊,果然很不恰当。。。总之就是,要知其然还要知其所以然,这样才能逐步成为一名合格的程序员。

  • 相关阅读:
    偶遇this之坑
    程序员的职业素养——读后感
    我怎么没想到——读后感
    黑客与画家——读后感
    漫谈认证与授权
    动手造轮子:实现一个简单的依赖注入(二) --- 服务注册优化
    动手造轮子:实现简单的 EventQueue
    asp.net core 自定义 Policy 替换 AllowAnonymous 的行为
    SQL Server 中 `JSON_MODIFY` 的使用
    WeihanLi.Npoi 近期更新
  • 原文地址:https://www.cnblogs.com/scut-linmaojiang/p/5281369.html
Copyright © 2020-2023  润新知