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语言
    +关注

    关注

    183

    文章

    7642

    浏览量

    144601
  • 代码
    +关注

    关注

    30

    文章

    4941

    浏览量

    73138

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

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

收藏 人收藏
加入交流群
微信小助手二维码

扫码添加小助手

加入工程师交流群

    评论

    相关推荐
    热点推荐

    共用声明

    还要考虑内存对齐的问题。 共用可以类似结构一样来定义和声明,但是共用还可以允许不带名字: union { int i; ch
    发表于 12-05 07:24

    C语言的编程技巧

    一个成员是一个未知大小的数组,适用于动态分配内存并关联一个可变长度的数组。‌ ‌3、匿名结构和联合体‌:C语言允许在结构
    发表于 11-27 06:46

    C语言程序的结构

    ,87LPC764有4KB的Flash ROM,而笔者的程序量只有2KB多点,因而第一个想法是改用C语言作为主要的开发语言,应该不至于导致代码空间不够用。其次,考虑到需要定时功能的模块(或称任务,以下统称任务
    发表于 11-26 08:12

    C语言的分支结构介绍

    1.简单if语句 C语言中的分支结构语句中的if条件语句。 简单if语句的基本结构如下: 代码语言:javascript if(表达
    发表于 11-25 07:48

    网络通讯的结构及地址

    1. 网络地址结构 Socket通过结构描述网络地址,最常用的是IPv4地址结构sockaddr_in(定义在): struct sockaddr_in
    发表于 11-17 07:59

    C语言结构使用

    型的数据组合。 结构的声明与定义 结构的声明一般形式为: c struct 结构
    发表于 11-12 08:30

    MDK uVision V5.36.00使用rt_packed进行字节对齐,但无效,为什么?

    各位RTT专家好: MDK uVision V5.36.00使用rt_packed进行字节对齐,但无效。
    发表于 09-24 06:37

    《ESP32S3 Arduino开发指南》第三章 C/C++语言基础

    ++基础,由于篇幅有限,在此仅对C/C++语言基础进行简单介绍。本章将分为如下9个小节:3.1 数据类型3.2 运算符3.3 表达式3.4 数组3.5 字符串3.6 注释3.7 顺序结构
    发表于 06-10 09:20

    程序设计与数据结构

    《程序设计与数据结构》重点阐述了三大方向内容: 1. C语言学习中的痛点:针对当前工程师在C语言学习中的痛点,如指针函数与函数指针,如何灵
    发表于 05-13 16:45

    深入理解C语言C语言循环控制

    C语言编程中,循环结构是至关重要的,它可以让程序重复执行特定的代码块,从而提高编程效率。然而,为了避免程序进入无限循环,C语言提供了多种循
    的头像 发表于 04-29 18:49 1733次阅读
    深入理解<b class='flag-5'>C</b><b class='flag-5'>语言</b>:<b class='flag-5'>C</b><b class='flag-5'>语言</b>循环控制

    C语言结构与联合体的深度解析:内存布局与应用场景

    一、基础概念与核心差异 1.1 结构(Struct)的本质 **结构C语言中实现数据封装的
    发表于 04-08 09:18

    为什么学了C语言,却写不出像样的项目?

    在学习编程的路上,C语言几乎是每个程序员的“必修课”。不管你是打算从事嵌入式开发、系统编程,还是想要深入理解操作系统的底层原理,C语言都是一块重要的基石。然而许多人在学习
    的头像 发表于 03-14 17:37 681次阅读
    为什么学了<b class='flag-5'>C</b><b class='flag-5'>语言</b>,却写不出像样的项目?

    全套C语言培训资料—PPT课件

    全套C语言培训资料,共427页,13个章节:C语言概述、程序的灵魂—算法、数据类型 & 运算符与表达式、顺序程序设计、选择结构程序设
    发表于 03-12 14:50

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

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

    三菱PLC编程语言解析

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