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

    文章

    7530

    浏览量

    128570
  • 代码
    +关注

    关注

    30

    文章

    4555

    浏览量

    66765

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

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

收藏 人收藏

    评论

    相关推荐

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

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

    ArkTS语言基础类库-解析

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

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

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

    嵌套的结构字节是如何对齐

    嵌套的结构体,字节又是如何对齐的呢 先来看下面的代码 typedef struct stu1 { char ary [ 5 ] ; int a; }stu1; typedef struct stu2
    的头像 发表于 11-20 16:01 249次阅读
    嵌套的<b class='flag-5'>结构</b>体 <b class='flag-5'>字节</b>是如何<b class='flag-5'>对齐</b>的

    什么是结构体的字节对齐现象

    什么是结构体的字节对齐现象 程序员,咱都用代码说话,先上 code: (说明:以下代码均在 ARM 平台上,使用 Keil 进行编译测试) # define offset_of (TYPE
    的头像 发表于 11-20 15:55 300次阅读
    什么是<b class='flag-5'>结构</b>体的<b class='flag-5'>字节</b><b class='flag-5'>对齐</b>现象

    为什么ST库函数结构没加对齐地址是连续的?

    为什么ST库函数结构没加对齐,地址是连续的
    发表于 10-15 08:11

    C语言中的结构指针在访问的时候怎么读取成员变量的数据?

    C语言中的结构指针在访问的时候怎么读取成员变量的数据
    发表于 10-10 07:07

    C语言深度解析

    C语言深度解析,本资料来源于网络,对C语言的学习有很大的帮助,有着较为深刻的解析,可能会对读者有
    发表于 09-28 07:00

    嵌入式C语言中的结构是什么?

    在介绍结构之后,将看一下这个强大数据对象的一些重要应用。然后将检查C语言语法以声明结构。最后将简要介绍数据对齐要求。可以通过简单地重新排列其成员的顺序来减小
    发表于 09-20 11:27 259次阅读
    嵌入式C<b class='flag-5'>语言</b>中的<b class='flag-5'>结构</b>是什么?

    关于NUC980的网口的发送描述符的缓冲区地址要求4字节对齐的问题

    NUC980的网口的发送描述符的缓冲区地址要求 4字节对齐的问题,因为我用的协议栈里面要求IP数据段4字节对齐,加上以太网14字节,如果发送
    发表于 09-04 06:44

    keil MDK中程序读写结构成员进入HARDFAUL HANDLE RET为什么无法读写?

    ]; ~ ~ DX[18]; Y[18]; }eedata; #pragma pack(pop)//恢复对齐状态 这里keil中short int 占两字节 float占四字节,我将整个结构
    发表于 08-25 07:58

    关于RT_ALIGN_DOWN字节对齐问题

    我在rtdef.h中看到了如下字节对齐的宏定义。 #define RT_ALIGN(size, align)(((size) + (align) - 1) & ~((align
    发表于 08-20 11:43

    C语言结构对齐介绍

    大家好,我是嵌入式老林,从事嵌入式软件开发多年,今天分享的内容是C语言结构对齐介绍,希望能对你有所帮助
    发表于 07-11 11:50 611次阅读
    C<b class='flag-5'>语言</b><b class='flag-5'>结构</b>体<b class='flag-5'>对齐</b>介绍

    为什么要结构对齐?为什么结构对齐那么重要?

    C语言结构对齐问题,是面试必备问题。我参与招聘技术面试的时候,也喜欢问这个技术点。
    的头像 发表于 05-26 14:10 664次阅读
    为什么要<b class='flag-5'>结构</b>体<b class='flag-5'>对齐</b>?为什么<b class='flag-5'>结构</b>体<b class='flag-5'>对齐</b>那么重要?

    i.MX 8QuadMax为什么CodeQuickAccess 32字节和DataQuickAccess 128字节对齐

    和 DataQuickAccess 128 字节对齐?为什么我们不能接受 4 字节对齐? 是否可以将.quickaccess部分分成两部分,这样我们就可以完全将m_tcml用于 Cod
    发表于 04-28 06:22