美文网首页
大话结构体之五:以空间换时间,Struct(结构体)中的成员对齐

大话结构体之五:以空间换时间,Struct(结构体)中的成员对齐

作者: Coder_LL | 来源:发表于2021-04-14 09:32 被阅读0次

    By Long Luo

    引言

    在上一篇以空间换时间,Struct(结构体)中的成员对齐之道(上)中,我们了解到struct ALIGN2struct ALIGN3的成员变量都是1个int型,1个char型及1个short型,可是它们所占的空间却1个是8字节,一个是12字节。

    为什么会有这样的区别呢?

    通过上篇关于对齐的介绍,我们已经猜测这是因为编译器对其作了对齐的处理所致,但是编译器处理的细节具体是什么呢?

    在这一篇里,我们将对程序的编译,汇编,链接进行逐一分析,解开这个谜团。

    不要走开,下面更精彩!

    编译过程:

    一般情况下,C程序的编译过程为

    1. 预处理
    2. 编译成汇编代码
    3. 汇编成目标代码
    4. 链接

    这一篇我们将使用gcc对上述几个过程进行仔细分析,了解其处理细节。

    首先我们看一个例子,源码如下:

    /************************************************************************************
    ** File: - Z:\code\c\Alignment\Align.c
    **  
    ** Copyright (C), Long.Luo, All Rights Reserved!
    ** 
    ** Description: 
    **      Align.c --- To learn the details of Alignment by the compiler.
    ** 
    ** Version: 1.1
    ** Date created: 22:33:50,10/12/2012
    ** Author: Long.Luo
    ** 
    ** --------------------------- Revision History: --------------------------------
    **  <author>    <data>          <desc>
    ** 
    ************************************************************************************/
    
    #include <stdio.h>
    
    struct ALIGN2
    {
        char mA;
        int mB;
        short mC;
    };
    
    struct ALIGN3
    {
        int mB;
        char mA;
        short mC;
    };
    
    
    int main(void)
    {
        struct ALIGN2 aln2;
        struct ALIGN3 aln3;
    
        printf("The size of struct ALIGN2 is: %d\n", sizeof(aln2));
        printf("\t aln2.mA=0x%x, aln2.mB=0x%x, aln2.mC=0x%x\n", &aln2.mA, &aln2.mB, &aln2.mC);
    
        printf("The size of struct ALIGN3 is: %d\n", sizeof(aln3));
        printf("\t aln3.mA=0x%x, aln3.mB=0x%x, aln3.mC=0x%x\n", &aln3.mA, &aln3.mB, &aln3.mC);
    
        return 0;
    }
    

    接下来我们对Align.c按照C程序的编译流程进行一一分析,如下所示:

    1. 预处理

    输出文件的后缀为:*.cpp文件。

    struct_5_1.jpg

    2. 编译成汇编代码

    1. 使用-x参数说明根据指定的步骤进行工作,cpp-output指明从预处理得到的文件开始编译;

    2. 使用-S说明生成汇编代码后停止工作

    gcc –x cpp-output –S –o align.s align.cpp
    
    struct_5_2.jpg

    我们也可以直接编译到汇编代码:

    gcc –S Align.c
    

    得到align.s文件之后,在最开始之处我们可以看到:

        .file   "Align.c"
        .section    .rodata
        .align 4
    

    其中的.align 4就表明了其后面所有的数据都遵守4字节对齐的限制

    3. 编译成目标代码

    汇编代码--->目标代码


    struct_5_3.jpg

    4. 编译成执行代码

    目标代码-->执行代码

    最终的输出结果如下所示:


    struct_5_4.jpg

    内存分析

    我们可以绘出struct ALIGN2struct ALIGN3内存分配图

    struct_5_5.jpg

    假如我们不对齐?

    上图是内存对齐struct ALIGN2struct ALIGN3的内存分配情况,但是假如我们不对齐呢?

    其内存分配如下所示:


    struct_5_6.jpg

    很明显,int mBshort mC都不满足对齐要求。

    对齐的好处是什么呢?

    通过上一节,我们知道了如果不对齐,我们可以节省出几个byte的内存空间,在计算机世界中,可以对齐也可以不对齐,但是实际中,都做了对齐。

    那么,对齐的好处是什么呢

    答案是:对齐是在时间空间之间做了一个tradeoff!

    对齐可以提高取数据的效率

    在IA32架构中,数据总线是32位,即一次可以存取4个字节的数据。

    在对齐的情况下,struct ALIGN2的每个成员都可以在一个指令周期内完成;

    而假设我们的struct ALIGN2没有对齐,那么对于struct ALIGN2char mA,CPU可以一次取出4个字节获得低位的一个字节,同时需要将高位的3个字节保存在寄存器中,之后的int mB,CPU必须再取得低位的1个字节并通之前保存在寄存器中的数据结果组合在一起,每一个都需要好几条指令,是不是相当麻烦?

    如何自定义对齐?

    那肯定有同学要问了,有没有办法让处理器按照自己的要求进行地址对齐呢?

    ---当然可以!

    我们可以通过预编译命令#pragma pack(n),n=1,2,4,8,16来改变这一系数,其中的n就是你要指定的“对齐系数”。

    比如,我想让处理器按照1个字节的方式对齐,则代码如下:

    /************************************************************************************
    ** File: - Z:\code\c\Alignment\AlignPackOne.c
    **  
    ** Copyright (C), Long.Luo, All Rights Reserved!
    ** 
    ** Description: 
    **      Align.c --- To learn the details of Alignment by the compiler.
    ** 
    ** Version: 1.1
    ** Date created: 23:39:05,10/12/2012
    ** Author: Long.Luo
    ** 
    ** --------------------------- Revision History: --------------------------------
    **  <author>    <data>          <desc>
    ** 
    ************************************************************************************/
    
    #include <stdio.h>
    
    
    #pragma pack(1)
    
    struct ALIGN2
    {
        char mA;
        int mB;
        short mC;
    };
    
    struct ALIGN3
    {
        int mB;
        char mA;
        short mC;
    };
    
    
    int main(void)
    {
        struct ALIGN2 aln2;
        struct ALIGN3 aln3;
    
        printf("The size of struct ALIGN2 is: %d\n", sizeof(aln2));
        printf("\t aln2.mA=0x%x, aln2.mB=0x%x, aln2.mC=0x%x\n", &aln2.mA, &aln2.mB, &aln2.mC);
    
        printf("The size of struct ALIGN3 is: %d\n", sizeof(aln3));
        printf("\t aln3.mA=0x%x, aln3.mB=0x%x, aln3.mC=0x%x\n", &aln3.mA, &aln3.mB, &aln3.mC);
    
        return 0;
    }
    

    编译之后输出结果如下:


    struct_5_7.jpg

    可以看出,在我们要求的1字节对齐方式下,struct ALIGN2struct ALIGN3的结果都是7,只占了4+2+1个字节,内存空间一个字节都利用到极致。

    至此,关于内存对齐就到此告一段落了,你弄明白了吗

    之前的系列文章,我们的struct都有成员变量,那么你有没有考虑过如果过struct完全是空的情况呢?

    下一篇我们将重点探讨这个问题!

    不要走开,后面更精彩!

    原文链接:以空间换时间,Struct(结构体)中的成员对齐之道(下)

    相关文章

      网友评论

          本文标题:大话结构体之五:以空间换时间,Struct(结构体)中的成员对齐

          本文链接:https://www.haomeiwen.com/subject/ghmvkltx.html