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

    文章

    7604

    浏览量

    136790
  • 代码
    +关注

    关注

    30

    文章

    4787

    浏览量

    68589

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

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

收藏 人收藏

    评论

    相关推荐

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

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

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

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

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

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

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

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

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

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

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

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

    求助,关于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 757次阅读

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

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

    C语言中的typedef的应用

    C 语言提供了 typedef 关键字,您可以使用它来为类型取一个新的名字。下面的实例为单字节数字定义了一个术语 BYTE。
    发表于 03-06 11:34 386次阅读
    <b class='flag-5'>C</b><b class='flag-5'>语言</b>中的typedef的应用

    ArkTS语言基础类库-解析

    被设计用来传输和存储数据,是一种可扩展标记语言语言基础类库提供了[XML生成、解析与转换]的能力。 URL、URI构造和解析能力:其中[URI]是统一资源标识符,可以唯一标识一个资源
    发表于 02-20 16:44

    经典 C 语言编程,结构和联合体如何共用?

    结构 结构占用的内存大小,首先和编译器的系统位数有关系,类似于CPU是 64 bits 还是 32 bits 的情形;其次,结构
    的头像 发表于 01-11 18:24 1369次阅读
    经典 <b class='flag-5'>C</b> <b class='flag-5'>语言</b>编程,<b class='flag-5'>结构</b><b class='flag-5'>体</b>和联合体如何共用?

    结构与指针的关系

    C语言中,结构(Struct)是一种用户自定义的数据类型,它允许您将不同类型的数据项组合在一起,以便形成一个更复杂的数据结构
    的头像 发表于 01-11 08:00 992次阅读
    <b class='flag-5'>结构</b><b class='flag-5'>体</b>与指针的关系

    keil arm工程中结构1字节对齐如何实现

    的默认对齐方式和规则 结构在Keil Arm工程中的默认对齐方式是根据编译器或者编译器选项来定的。通常情况下,编译器会按照平台的特定对齐
    的头像 发表于 01-05 14:40 3793次阅读

    C语言数据结构之跳表详解

    大家好,今天分享一篇C语言数据结构相关的文章--跳表。
    的头像 发表于 12-29 09:32 831次阅读
    <b class='flag-5'>C</b><b class='flag-5'>语言</b>数据<b class='flag-5'>结构</b>之跳表详解