0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

解析C语言结构体字节如何对齐

电子工程师 来源:编程学习总站 作者:写代码的牛顿 2021-06-12 17:42 次阅读

01

默认字节对齐

C语言结构体字节对齐是老生常谈的问题了,也是高频面试题,现在我们来深入研究这个问题,彻底弄懂到底是怎么回事,给你一个结构体定义和平台机器位数就能手动计算出结构体占用字节数,现在我们不使用宏#pragma pack,采用默认字节对齐方式。

先抛出结论:

在一个结构体中第一个成员变量放在偏移为0的位置,以后的变量都存储在该变量占用字节数整数倍的地址上。

结构体总大小,必须是内部最大成员变量的整数倍,不足的补齐。

好了,现在我们直接写个小程序验证并分析是否真是这样一回事。

struct st{ short a1; short a2; short a3; }; struct st2{ long a1; short a2; };

这里我们定义了两个很简单的结构体,short占用2个字节,struct st我们一眼就知道大小了6个字节,但是struct st2呢?笔者电脑是64位,那么long占用8个字节,short占用2个字节。我们先来按照结论进行分析,在struct st2中成员变量a1在偏移0处存储且占用8个字节,成员变量a2占用2个字节,由于8是2的倍数,所以a2在偏移8的位置存储,又因为有结论2。

我们根据结论2可以得出,struct st2必须占用8的倍数大小,所以struct st2总大小是16个字节,不足的后面补齐。现在我分别打印出struct st1和struct st2占用字节数大小和struct st2各个成员变量地址,观察是否和分析的一样。

int main() { struct st2 st_val2; printf(“sizeof(long) = %d ”, sizeof(long)); printf(“sizeof(struct st) = %d ”, sizeof(struct st)); printf(“sizeof(struct st2) = %d ”, sizeof(struct st2)); printf(“st_val2 addr = %p ”, &st_val2); printf(“st_val2 a1 addr = %p ”, &st_val2.a1); printf(“st_val2 a2 addr = %p ”, &st_val2.a2); return 0; }

编译运行输出:

sizeof(long) = 8 sizeof(struct st) = 6 sizeof(struct st2) = 16 st_val2 addr = 0x7ffee107f3b8 st_val2 a1 addr = 0x7ffee107f3b8 st_val2 a2 addr = 0x7ffee107f3c0

现在我们看一下输出结果,struct st如我们所愿占用6个字节大小,struct st2也按照我们分析的一样占用16个字节。我们在程序中定义了一个struct st2类型变量st_val2,从输出中可以看出变量st_val2的a1成员变量和st_val2变量地址一样,成员变量a2在偏移8处存储(0x c0 = 0xb8 + 8)。一切如我们所愿,看起来好像挺简单的,我们知道C语言有丰富的数据类型,下面我们再定义一个更复杂的结构体。

struct st3{ int a1; char a2; short a3; long a4; char a5; };

这个结构体包含了大量数据类型成员变量,再复杂的结构体也能按照我们的结论分析到底占用了几个字节。

在struct st3中int型成员变量a1占用4个字节,在偏移0处存储,char型成员变量a2占用2个字节那么应该放在2的倍数地址处存储,a1已经占用了4个字节,所以a2应该在偏移4的地址存储。

short型成员变量a3占用2个字节,也应该放在2的倍数地址处存储,所以a3在偏移6的地址处存储,a2后面填充1个字节。

long型成员变量a4占用8个字节,应该放在8的倍数地址上存储,前面我们已经知道a3在偏移6的地址处存储,且占用2个字节8 = 6 + 2,所以a4应该在偏移8的地址处存储。

最后一个char型成员变量a5占用一个字节,那么a5在偏移16地址处存储。

现在我们计算一下struct st3结构体占用空间大小,从a5偏移出计算16 + 1 = 17。在struct st3中最大成员变量占用8个字节,所以结构体总大小应该是8的倍数,最后结构体总大小是17 + 7 = 24,这里的7个字节在最后补齐。

我们依旧写一个小程序输出struct st3类型变量各个成员变量地址和结构体总大小。

int main() { struct st3 st_val3; printf(“sizeof(struct st3) = %d ”, sizeof(struct st3)); printf(“st_val3 addr = %p ”, &st_val3); printf(“st_val3.a1 addr = %p ”, &st_val3.a1); printf(“st_val3.a2 addr = %p ”, &st_val3.a2); printf(“st_val3.a3 addr = %p ”, &st_val3.a3); printf(“st_val3.a4 addr = %p ”, &st_val3.a4); printf(“st_val3.a5 addr = %p ”, &st_val3.a5); return 0; }

编译运行输出:

sizeof(struct st3) = 24 st_val3 addr = 0x7ffeed0c33b0 st_val3.a1 addr = 0x7ffeed0c33b0 st_val3.a2 addr = 0x7ffeed0c33b4 st_val3.a3 addr = 0x7ffeed0c33b6 st_val3.a4 addr = 0x7ffeed0c33b8 st_val3.a5 addr = 0x7ffeed0c33c0

从输出我们可以看出,和我们分析的完全一样。

枚举类型变量和联合体类型变量都可以作为结构体的成员变量,在分析这些结构体占用大小时,分析方法和我们上面的一模一样,只需要把内部任何一种数据类型变量当做一个普通变量看待即可,但是结构体类型成员变量有点不一样,它不适用于结论2,我们举个例子。

struct st4{ char a1[3]; int a2; long a3; struct st3 a4; };

在struct st4中我们定义了一个struct st3类型成员变量,前面我们已经分析过了struct st3占用24个字节。成员变量a1占用3个字节,成员变量a2占用4个字节,所以a2存储在偏移4的地址上,在a1后面填充一个字节。成员变量a3占用8个字节,则a3存储在偏移8的地址上。那么结构体总共占用字节数大小是:8 + 8 + 24 = 40。

最后我们写一个程序验证一下是否如此。

int main() { struct st4 st_val4; printf(“sizeof(struct st4) = %d ”, sizeof(struct st4)); printf(“st4 addr = %p ”, &st_val4); printf(“st_val4.a1 addr = %p ”, &st_val4.a1); printf(“st_val4.a2 addr = %p ”, &st_val4.a2); printf(“st_val4.a3 addr = %p ”, &st_val4.a3); printf(“st_val4.a4 addr = %p ”, &st_val4.a4); return 0; }

编译运行输出:

sizeof(struct st4) = 40 st4 addr = 0x7ffeec1263a0 st_val4.a1 addr = 0x7ffeec1263a0 st_val4.a2 addr = 0x7ffeec1263a4 st_val4.a3 addr = 0x7ffeec1263a8 st_val4.a4 addr = 0x7ffeec1263b0

和我们分析的一模一样。

02

#pragma pack宏的作用

我们看一下下面这段代码。

#pagma pack(1)int main() { struct st3 st_val3; printf(“sizeof(struct st3) = %d ”, sizeof(struct st3)); printf(“st_val3 addr = %p ”, &st_val3); printf(“st_val3.a1 addr = %p ”, &st_val3.a1); printf(“st_val3.a2 addr = %p ”, &st_val3.a2); printf(“st_val3.a3 addr = %p ”, &st_val3.a3); printf(“st_val3.a4 addr = %p ”, &st_val3.a4); printf(“st_val3.a5 addr = %p ”, &st_val3.a5); return 0; }

这段代码里我们使用了#pagma pack宏,表示结构体按1字节对齐。也就是说结构体变量st_val3总大小是内部成员变量占用字节数总和,没有字节填充。

现在编译运行如下:

sizeof(struct st3) = 16 st_val3 addr = 0x7ffee13a93b8 st_val3.a1 addr = 0x7ffee13a93b8 st_val3.a2 addr = 0x7ffee13a93bc st_val3.a3 addr = 0x7ffee13a93bd st_val3.a4 addr = 0x7ffee13a93bf st_val3.a5 addr = 0x7ffee13a93c7

在struct st3中int型a1占用4字节,char型变量a2占用1个字节,short型变量a3占用2个字节,long型变量a4占用8个字节,char型变量a5占用1个字节,所以总大小是:4 + 1 + 2 + 8 + 1 = 16。如果是#pagma pack(2)呢?相信你可以自己计算了。

编辑:jq

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • C语言
    +关注

    关注

    180

    文章

    7618

    浏览量

    138666
  • 代码
    +关注

    关注

    30

    文章

    4857

    浏览量

    69530

原文标题:C语言结构体字节对齐

文章出处:【微信号:AndroidPush,微信公众号:Android编程精选】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    相关推荐

    分析C语言代码结构的设计问题

    来分析一个C语言代码结构的设计问题。 这段代码,使用了两次malloc,分别给 p1 和 p2 申请了内存。用完后,内存释放,防止内存泄漏。 大家觉得,这样的代码设计有没有问题。 代码是某位学员在
    的头像 发表于 02-11 09:31 128次阅读

    三菱PLC编程语言解析

    (Sequential Function Chart, SFC)等。以下是对这些编程语言的简要解析: 1. 梯形图(Ladder Diagram, LD) 梯形图是PLC编程中最常用的图形编程语言,因其
    的头像 发表于 12-26 17:36 603次阅读

    结构成员的顺序会影响结构的大小吗

    相同的结构成员,如果把顺序调整一下,会不会影响结构的大小? 答案是会的,这主要跟字节对齐有关
    的头像 发表于 11-25 16:24 331次阅读

    C语言C++中结构的区别

    同样是结构,看看在C语言C++中有什么区别?
    的头像 发表于 10-30 15:11 433次阅读

    C语言与Java语言的对比

    C语言和Java语言都是当前编程领域中的重要成员,它们各自具有独特的优势和特点,适用于不同的应用场景。以下将从语法特性、内存管理、跨平台性、性能、应用领域等多个方面对C
    的头像 发表于 10-29 17:31 576次阅读

    字节豆包推出AI智能耳机

    字节跳动旗下豆包品牌近日推出了其首款AI智能耳机——Ola Friend。这款耳机采用了开放式设计,单耳重量仅为6.6克,佩戴舒适轻盈。
    的头像 发表于 10-10 16:55 734次阅读

    技术干货驿站 ▏深入理解C语言:编程高手必备,全方位解析运算符的核心技能!

    C语言的编程领域中,运算符是实现数据处理与逻辑操作的关键工具。无论是在处理简单的数值计算,还是在构建复杂的逻辑结构时,运算符的使用贯穿始终。作为编程语言的基础组成部分之一,运算符不仅
    的头像 发表于 09-18 15:56 491次阅读
    技术干货驿站 ▏深入理解<b class='flag-5'>C</b><b class='flag-5'>语言</b>:编程高手必备,全方位<b class='flag-5'>解析</b>运算符的核心技能!

    技术干货驿站 ▏深入理解C语言:掌握程序结构知识

    在计算机编程的世界中,C语言被广泛认可为一门强大而高效的编程语言,其简洁的语法和直接的指令使得它成为了许多程序员的首选。了解C语言的程序
    的头像 发表于 07-27 08:45 1598次阅读
    技术干货驿站 ▏深入理解<b class='flag-5'>C</b><b class='flag-5'>语言</b>:掌握程序<b class='flag-5'>结构</b>知识

    PLC编程语言C语言的区别

    在工业自动化和计算机编程领域中,PLC(可编程逻辑控制器)编程语言C语言各自扮演着重要的角色。尽管两者都是编程语言,但它们在多个方面存在显著的区别。本文将从多个维度深入探讨PLC编程
    的头像 发表于 06-14 17:11 3659次阅读

    你是否真的了解结构占用了多少字节

    结构成员所占内存空间大小一般情况下,如果想知道结构成员的内存占用情况需要:1、先用结构在内
    的头像 发表于 06-04 08:04 644次阅读
    你是否真的了解<b class='flag-5'>结构</b><b class='flag-5'>体</b>占用了多少<b class='flag-5'>字节</b>?

    嵌入式中C语言结构基本实现

    C语言中的数组只能允许程序员定义存储相同类型数据。但是结构C语言编程中允许您存储不同数据类型的数据。
    的头像 发表于 05-11 08:49 1193次阅读
    嵌入式中<b class='flag-5'>C</b><b class='flag-5'>语言</b><b class='flag-5'>结构</b><b class='flag-5'>体</b>基本实现

    用FreeRTOS使用队列怎么发送一个结构呢?

    怎么使用队列,发送一个12个字节结构呢? osEvent osMessageGet (osMessageQId queue_id, uint32_t millisec
    发表于 04-17 07:35

    求助,关于STM32H7的Cache无效化操作32字节对齐问题求解

    core_cm7.h更新到5.1.1版本后,发现该函数对dsize做了32字节对齐,但是op_addr地址32字节对齐却注释掉了?,图片的这句话是不是说SCB-&gt
    发表于 03-29 06:51

    C语言结构史上最详细的讲解【软件干货】

    struct结构数据类型 前言 我们知道,在C语言中有一些基本的数据类型,如 char int float long double string(
    的头像 发表于 03-28 17:52 937次阅读

    嵌入式系统中C语言结构的基础实现与应用

    C语言中的数组只能允许程序员定义存储相同类型数据。但是结构C语言编程中允许您存储不同数据类型的数据。
    发表于 03-12 14:29 637次阅读
    嵌入式系统中<b class='flag-5'>C</b><b class='flag-5'>语言</b><b class='flag-5'>结构</b><b class='flag-5'>体</b>的基础实现与应用