欢迎光临
我们一直在努力

C语言 结构体内存对齐 小白图解

C语言 结构体内存对齐 小白图解

大白话讲清楚:结构体的成员,不是紧紧挨在一起放内存,中间会有空隙(padding填充字节),这个规则就叫内存对齐。

一、一句话理解内存对齐

CPU读取内存的时候,喜欢按固定块来读(比如一次读4字节),如果数据地址刚好落在块开头,读取速度更快。
为了提速,编译器会在结构体成员之间自动塞空白字节(填充padding),这就是内存对齐。

代价:浪费一点点内存;好处:CPU读写更快。

二、核心对齐规则(精简小白版,考试就记这3条)

  • 每个成员的对齐数 = 该成员自身大小 和 编译器默认对齐数,取较小的那个
    32位默认对齐数=4;64位默认对齐数=8。
  • 成员存放地址:必须是自己对齐数的整数倍。不够就前面填空白padding。
  • 整个结构体总大小:必须是【最大成员对齐数】的整数倍。末尾不够,在结构体最后补padding。
  • 最简单记忆口诀:
    成员对齐:自己占几字节,就放在几的倍数地址;
    整体对齐:结构体总大小,要等于最大成员大小的倍数。

    三、例子1:最简单结构体,没有填充

    struct Test1
    {
    char c; // char 1字节
    int i; // int 4字节
    };

    分步拆解(64位环境,默认对齐数8)

    • char:占1字节,对齐数min(1,8)=1。放在地址0。地址0是1倍数 ✅
    • int:占4字节,对齐数min(4,8)=4。必须放在4的倍数地址
      地址1、2、3都不是4倍数,所以地址1、2、3填充空白padding。
      int从地址4开始,占用4、5、6、7。

    内存布局:

    地址:0 1 2 3 4 5 6 7
    数据:[char] [填充] [int]

    总占用:8字节
    最大成员是int(4),8是4的倍数 ✅,末尾不用补。

    #mermaid-svg-qVjbkZ0jAgkvWGuZ{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-qVjbkZ0jAgkvWGuZ .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-qVjbkZ0jAgkvWGuZ .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-qVjbkZ0jAgkvWGuZ .error-icon{fill:#552222;}#mermaid-svg-qVjbkZ0jAgkvWGuZ .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-qVjbkZ0jAgkvWGuZ .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-qVjbkZ0jAgkvWGuZ .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-qVjbkZ0jAgkvWGuZ .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-qVjbkZ0jAgkvWGuZ .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-qVjbkZ0jAgkvWGuZ .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-qVjbkZ0jAgkvWGuZ .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-qVjbkZ0jAgkvWGuZ .marker{fill:#333333;stroke:#333333;}#mermaid-svg-qVjbkZ0jAgkvWGuZ .marker.cross{stroke:#333333;}#mermaid-svg-qVjbkZ0jAgkvWGuZ svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-qVjbkZ0jAgkvWGuZ p{margin:0;}#mermaid-svg-qVjbkZ0jAgkvWGuZ .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-qVjbkZ0jAgkvWGuZ .cluster-label text{fill:#333;}#mermaid-svg-qVjbkZ0jAgkvWGuZ .cluster-label span{color:#333;}#mermaid-svg-qVjbkZ0jAgkvWGuZ .cluster-label span p{background-color:transparent;}#mermaid-svg-qVjbkZ0jAgkvWGuZ .label text,#mermaid-svg-qVjbkZ0jAgkvWGuZ span{fill:#333;color:#333;}#mermaid-svg-qVjbkZ0jAgkvWGuZ .node rect,#mermaid-svg-qVjbkZ0jAgkvWGuZ .node circle,#mermaid-svg-qVjbkZ0jAgkvWGuZ .node ellipse,#mermaid-svg-qVjbkZ0jAgkvWGuZ .node polygon,#mermaid-svg-qVjbkZ0jAgkvWGuZ .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-qVjbkZ0jAgkvWGuZ .rough-node .label text,#mermaid-svg-qVjbkZ0jAgkvWGuZ .node .label text,#mermaid-svg-qVjbkZ0jAgkvWGuZ .image-shape .label,#mermaid-svg-qVjbkZ0jAgkvWGuZ .icon-shape .label{text-anchor:middle;}#mermaid-svg-qVjbkZ0jAgkvWGuZ .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-qVjbkZ0jAgkvWGuZ .rough-node .label,#mermaid-svg-qVjbkZ0jAgkvWGuZ .node .label,#mermaid-svg-qVjbkZ0jAgkvWGuZ .image-shape .label,#mermaid-svg-qVjbkZ0jAgkvWGuZ .icon-shape .label{text-align:center;}#mermaid-svg-qVjbkZ0jAgkvWGuZ .node.clickable{cursor:pointer;}#mermaid-svg-qVjbkZ0jAgkvWGuZ .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-qVjbkZ0jAgkvWGuZ .arrowheadPath{fill:#333333;}#mermaid-svg-qVjbkZ0jAgkvWGuZ .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-qVjbkZ0jAgkvWGuZ .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-qVjbkZ0jAgkvWGuZ .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-qVjbkZ0jAgkvWGuZ .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-qVjbkZ0jAgkvWGuZ .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-qVjbkZ0jAgkvWGuZ .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-qVjbkZ0jAgkvWGuZ .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-qVjbkZ0jAgkvWGuZ .cluster text{fill:#333;}#mermaid-svg-qVjbkZ0jAgkvWGuZ .cluster span{color:#333;}#mermaid-svg-qVjbkZ0jAgkvWGuZ div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-qVjbkZ0jAgkvWGuZ .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-qVjbkZ0jAgkvWGuZ rect.text{fill:none;stroke-width:0;}#mermaid-svg-qVjbkZ0jAgkvWGuZ .icon-shape,#mermaid-svg-qVjbkZ0jAgkvWGuZ .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-qVjbkZ0jAgkvWGuZ .icon-shape p,#mermaid-svg-qVjbkZ0jAgkvWGuZ .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-qVjbkZ0jAgkvWGuZ .icon-shape .label rect,#mermaid-svg-qVjbkZ0jAgkvWGuZ .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-qVjbkZ0jAgkvWGuZ .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-qVjbkZ0jAgkvWGuZ .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-qVjbkZ0jAgkvWGuZ :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}

    内存地址

    0: char c

    1: padding空白

    2: padding空白

    3: padding空白

    4~7: int i

    ❗误区:很多小白以为 1+4=5字节,实际占8字节!中间3个空字节。

    四、例子2:调换成员顺序,大小会变!(重点)

    struct Test2
    {
    int i; //4字节
    char c; //1字节
    };

    • int放在地址0,占用0~3
    • char放在地址4,占用地址4

    现在已占用5字节。
    规则3:整个结构体大小必须是最大成员(4)的倍数。5不是4倍数,末尾补3个padding。

    内存布局

    地址:0~3 4 5 6 7
    数据:[int] [char] [末尾填充]

    总大小依旧8字节。

    结论:结构体成员顺序会影响结构体占用内存大小! 把大类型放前面,可以减少内存浪费。

    五、例子3:包含short(2字节)加深理解

    struct Test3
    {
    char a; //1字节
    short b; //2字节
    int c; //4字节
    };

    • a:地址0,1字节
    • short对齐数=2,地址1不是2倍数 → 地址1填充1字节。short从地址2,占2、3
    • int对齐数=4,地址4,占4~7
      总大小8字节。

    0:a | 1:填充 | 2~3:short |4~7:int

    六、验证代码,复制运行看结果

    #include <stdio.h>
    struct Test1
    {
    char c;
    int i;
    };

    int main()
    {
    printf("Test1结构体大小 = %zd\\n", sizeof(struct Test1));
    return 0;
    }

    输出:Test1结构体大小 = 8

    七、高频易错坑

  • ❌ 错误想法:结构体大小 = 所有成员字节直接相加
    ✅ 正确:成员之间、结构体末尾,可能自动填充空白padding。
  • 对齐看成员类型本身大小,不是变量值。
  • 结构体嵌套结构体:里面的结构体,对齐数取它内部最大成员大小。
  • 可以用#pragma pack(n)手动修改默认对齐数(考试经常考)
  • #pragma pack(1) //设置对齐数为1,取消填充,成员紧紧相邻
    struct Test
    {
    char c;
    int i;
    };
    //此时大小 = 1+4 =5,没有padding
    #pragma pack() //恢复默认对齐

    八、一句话总结

    内存对齐就是编译器自动在结构体里面塞空白填充字节,保证每个成员放在对应倍数的内存地址,加快CPU读取速度;
    结构体总大小必须是最大成员大小的整数倍;成员摆放顺序会影响总占用内存。

    赞(0)
    未经允许不得转载:171主机测评 » C语言 结构体内存对齐 小白图解
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址