C语言结构体字节对齐(内存对齐)之#pragma pack和__attribute__((packed)的使用

这篇具有很好参考价值的文章主要介绍了C语言结构体字节对齐(内存对齐)之#pragma pack和__attribute__((packed)的使用。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

在不使用#pragma pack__attribute__((packed) 等选项来自定义字节对齐大小的情况下,关于正常字节对齐的描述,可参考博文:

C/C++计算类/结构体和联合体(union)所占内存大小(内存对齐问题)_联合体占用的内存空间_SOC罗三炮的博客-CSDN博客同学可以尝试将char f 注释,最后将得到24,也可以从侧面说明再加上一个char f,其大小肯定大于等于24 byte。Test3中的最大数据成员大小比成员结构体Test内部最大成员大小要小,这时规则3是按照。sizeof的大小是24,即满足容下a[20],同样24是b、c和d的倍数,规则3。sizeof的大小是20,即a[20]的大小,同样20是b和c的倍数,规则3。s中char ch1;占用 1Byte(存储位置8)的,sizeof的结果是40。最大元素大小的整数倍地址。_联合体占用的内存空间https://blog.csdn.net/luolaihua2018/article/details/115372273?ops_request_misc=&request_id=fd0dff42f0ee4596a85be00069ed9893&biz_id=&utm_medium=distribute.pc_search_result.none-task-blog-2~blog~koosearch~default-2-115372273-null-null.268%5Ev1%5Econtrol&utm_term=%E5%AD%97%E8%8A%82%E5%AF%B9%E9%BD%90&spm=1018.2226.3001.4450本文主要讨论如何使用#pragma pack和__attribute__((packed)等选项来自定义字节对齐大小。

目录

1,对整个结构体进行打包压缩

 #pragma pack(n) 实验

__attribute__((packed)) 实验

 2,编译器内存对齐操作

test1,正常情况下的对齐操作,12 bytes

 test 2,使用__attribute__((packed)),相当于#pragma pack(1),8 bytes (最小)

 test3, 使用#pragma pack(2),10 bytes

  test4, 使用#pragma pack(4),12 bytes

  test5, 使用#pragma pack(5) ,12bytes

 test6, 使用#pragma pack(8) ,12bytes 

test7,使用__attribute__((packed))对结构体中的成员进行修饰,10 bytes

3,使用armlink 选项 --info=sizes 查看 结构体大小


使用pack功能将数据结构进行打包,以减小应用程序对内存的占用,这一点在嵌入式系统中,尤其是需要存储和访问大量内存的时候,显得尤其重要。

如果没有使用#pragma pack和__attribute__((packed)等选项,将数据结构进行打包。编译器为了提高对数据成员的访问速度,通常会在不同大小的数据成员之间插入空白内存(padding),进行补齐操作,即内存对齐。默认情况下的内存对齐,需要遵守一下三条规则:

  1. 数据成员对齐规则,结构体(struct)(或联合(union))的数据成员,第一个数据成员存放在offset为0的地方,以后每个数据成员存储的起始位置要从该成员大小或者成员的子成员(只要该成员有子成员,比如数组、结构体等)大小的整数倍开始(如:int 在 64bit 目标平台下占用 4Byte,则要从4的整数倍地址开始存储)
  2. 结构体作为成员,如果一个结构体里有某些结构体成员,则结构体成员要从其内部最大元素大小的整数倍地址开始存储
  3. 结构体的总大小,即sizeof的结果,必须是其内部最大成员长度(即前面内存对齐指令中提到的有效值)的整数倍,不足的要补齐
     

ARM的嵌入式编译器提供了编译选项和属性(#pragma pack和__attribute__((packed)),可以让程序员自定义内存对齐的大小,对数据结构(结构体或联合体)进行打包,必要情况下甚至可以不需要内存留白,即不对齐。

#pragma pack (<n>) 对于结构体中的每个成员,如果 <n> 小于该成员的默认对齐大小(关于默认对齐大小,可参考上文中提到的博文),则使用 <n> bytes 作为该成员的对齐大小。如果<n> 大于默认对齐大小,则使用默认对齐大小。简言之,使用<n> 和默认对齐大小中较小的那个,作为该数据成员的对齐大小。 详情见: __alignof__.
__attribute__((packed)) 等效于#pragma pack(1),最小对齐大小,即不进行内存对齐,可以对结构体中的单个数据成员使用。

1,对整个结构体进行打包压缩

在声明结构体或联合体时,可以使用__attribute__((packed)) 或 #pragma pack(n) 对整个结构体进行声明。这样,结构体或联合体里面的每个数据成员都会按照新的对齐大小进行对齐。

__attribute__((packed)) 和  #pragma pack(n)的作用范围不同,使用__attribute__((packed))可以精准地对其声明的结构体或者联合体进行打包压缩,而不会影响其他及结构体,甚至是其子结构体(其数据成员也为结构体)的正常内存对齐。而 #pragma pack(n) 的作用范围更大,在使用#pragma pack(n)之后的所有结构体联合体(不包括子结构体)的内存对齐都会受到其影响。

struct __attribute__((packed)) stc
{
    char one;
    short two;
    char three;
    int four;
} c,d;

#pragma pack (1)
struct stc
{
    char one;
    short two;
    char three;
    int four;
} c,d;

 #pragma pack(n) 实验

/* ================= test 1 ==================*/
struct stc
{
    char one;
    short two;
    char three;
    int four;
} cc; 

#pragma pack (1)
struct stcd
{
    char one;
    short two;
    char three;
    int four;
} dd;

sizeof(cc) = 12
sizeof(dd) = 8

/* ================= test 2 ==================*/
#pragma pack (1)
struct stc
{
    char one;
    short two;
    char three;
    int four;
} cc; 

struct stcd
{
    char one;
    short two;
    char three;
    int four;
} dd;

sizeof(cc) = 8
sizeof(dd) = 8

/* ================= test3 ==================*/

typedef struct 
{
    char one;
    short two;
    char three;
    int four;
} stc;
stc cc;

struct stcd
{
    char one;
    short two;
    char three;
    int four;
    stc C;
} dd;

sizeof(cc) = 12
sizeof(dd) = 24

/* ================= test4 ==================*/

#pragma pack (1)
typedef struct 
{
    char one;
    short two;
    char three;
    int four;
} stc;

stc cc;
struct stcd
{
    char one;
    short two;
    char three;
    int four;
    stc C;
} dd;

sizeof(cc) = 8
sizeof(dd) = 16

/* ================= test5 ==================*/

typedef struct 
{
    char one;
    short two;
    char three;
    int four;
} stc;
stc cc;
#pragma pack (1)
struct stcd
{
    char one;
    short two;
    char three;
    int four;
//    #pragma pack (1)
    stc C;
} dd;

sizeof(cc) = 12
sizeof(dd) = 20

/* ================= test6 ==================*/

typedef struct 
{
    char one;
    short two;
    char three;
    int four;
} stc;
stc cc;

struct stcd
{
    char one;
    short two;
    char three;
    int four;
    #pragma pack (1)
    stc C;
} dd;

sizeof(cc) = 12
sizeof(dd) = 20

从test4,test5和test6可以看到,虽然 在#pragma pack(n)之后,都会受到其对齐影响,但是结构体dd中的C结构体仍保持自然的内存对齐,并未受影响。此外,在结构体dd内使用#pragma pack(n),仍相当于对整个结构体dd起作用。

__attribute__((packed)) 实验

/* ================= test1 ==================*/

typedef struct __attribute__((packed))
{
    char one;
    short two;
    char three;
    int four;
} stc;

stc cc;

struct stcd
{
    char one;
    short two;
    char three;
    int four;
    stc C;
} dd;

sizeof(cc) = 8
sizeof(dd) = 20

/* ================= test2 ==================*/
typedef struct __attribute__((packed))
{
    char one;
    short two;
    char three;
    int four;
} stc;

stc cc;

struct __attribute__((packed)) stcd
{
    char one;
    short two;
    char three;
    int four;
    stc C;
} dd;

sizeof(cc) = 8
sizeof(dd) = 16

/* ================= test3 ==================*/
typedef struct 
{
    char one;
    short two;
    char three;
    int four;
} stc;

stc cc;

struct __attribute__((packed)) stcd
{
    char one;
    short two;
    char three;
    int four;
    stc C;
} dd;

sizeof(cc) = 12
sizeof(dd) = 20

使用  __attribute__((packed))可以精准地对想要打包压缩的结构体进行操作,而不像#pragma pack(n)的全局生效。同时也是对整个结构体生效,如果结构体内的数据成员仍为结构体,则不对子结构体生效。 __attribute__((packed))相当于#pragma pack(1),即尽可能地压缩结构体空间,不像#pragma pack(n)那样可以选择不同大小的对齐尺寸。

使用__attribute__((packed))声明变量时,要注意其摆放位置,比如test4的声明是错误的,虽然可以编译通过,但是__attribute__((packed))并不会生效,还会报一个warning:

[Warning] 'packed' attribute ignored [-Wattributes]

test5中的声明方式是正确的: 

/* ================ test 4 ==============*/
typedef struct
{
    char one;
    short two;
    char three;
    int four;
} stc;

stc __attribute__((packed)) cc;

sizeof(cc) = 12  

/* ================ test 5 ==============*/

typedef struct __attribute__((packed))
{
    char one;
    short two;
    char three;
    int four;
} stc;
stc cc;

sizeof(cc) = 8

 2,编译器内存对齐操作

读者可以先参考博文,了解正常情况下的结构体对齐操作:
C/C++计算类/结构体和联合体(union)所占内存大小(内存对齐问题)_联合体占用的内存空间_SOC罗三炮的博客-CSDN博客同学可以尝试将char f 注释,最后将得到24,也可以从侧面说明再加上一个char f,其大小肯定大于等于24 byte。Test3中的最大数据成员大小比成员结构体Test内部最大成员大小要小,这时规则3是按照。sizeof的大小是24,即满足容下a[20],同样24是b、c和d的倍数,规则3。sizeof的大小是20,即a[20]的大小,同样20是b和c的倍数,规则3。s中char ch1;占用 1Byte(存储位置8)的,sizeof的结果是40。最大元素大小的整数倍地址。_联合体占用的内存空间https://blog.csdn.net/luolaihua2018/article/details/115372273?ops_request_misc=&request_id=6d04e4a92dcd4c6585378e324b6f7c4a&biz_id=&utm_medium=distribute.pc_search_result.none-task-blog-2~blog~koosearch~default-2-115372273-null-null.268%5Ev1%5Econtrol&utm_term=%E5%86%85%E5%AD%98%E5%AF%B9%E9%BD%90&spm=1018.2226.3001.4450

 下面将通过一些示例,详情介绍使用__attribute__((packed))和#pragma pack(n)对内存对齐产生的影响:

test1,正常情况下的对齐操作,12 bytes

结构体中,各元素按照顺序摆放,同时需要针对不同大小的成员数据进行不同大小的补齐操作

C语言结构体字节对齐(内存对齐)之#pragma pack和__attribute__((packed)的使用,ARM Compiler,Linux_C,c语言,编译优化,嵌入式,arm开发,内存对齐

 test 2,使用__attribute__((packed)),相当于#pragma pack(1),8 bytes (最小)

 #pragma pack(1) 不需要任何内存补齐,没有任何pandding,如下图所示,数据按照成员顺序摆放即可:C语言结构体字节对齐(内存对齐)之#pragma pack和__attribute__((packed)的使用,ARM Compiler,Linux_C,c语言,编译优化,嵌入式,arm开发,内存对齐

C语言结构体字节对齐(内存对齐)之#pragma pack和__attribute__((packed)的使用,ARM Compiler,Linux_C,c语言,编译优化,嵌入式,arm开发,内存对齐

 test3, 使用#pragma pack(2),10 bytes

强制使用2 bytes作为结构体中的最大对齐尺寸

 C语言结构体字节对齐(内存对齐)之#pragma pack和__attribute__((packed)的使用,ARM Compiler,Linux_C,c语言,编译优化,嵌入式,arm开发,内存对齐

  test4, 使用#pragma pack(4),12 bytes

C语言结构体字节对齐(内存对齐)之#pragma pack和__attribute__((packed)的使用,ARM Compiler,Linux_C,c语言,编译优化,嵌入式,arm开发,内存对齐

  test5, 使用#pragma pack(5) ,12bytes

如果n不是2的次方,即不是1,2,4,8...,则不会生效,并且会报一个warning:

9[Warning] alignment must be a small power of two, not 5 [-Wpragmas]

 test6, 使用#pragma pack(8) ,12bytes 

 正常情况下,结构体c和d中的最大自然对齐尺寸为4,所以编译器会在8和4中选一个最小的对齐尺寸,即为4。

test7,使用__attribute__((packed))对结构体中的成员进行修饰,10 bytes

使用__attribute__((packed))可以指定某个数据成员不需要进行内存对齐,虽然指定了 int类型的four不需要内存对齐,但是结构体中的其他成员仍需遵守内存对齐规则,除了int外,最大的是short类型的two,所以该结构体的最小对齐尺寸为short的大小,即2 bytes,这就是为什么最后的结果为10 bytes,而不是9 bytes.

C语言结构体字节对齐(内存对齐)之#pragma pack和__attribute__((packed)的使用,ARM Compiler,Linux_C,c语言,编译优化,嵌入式,arm开发,内存对齐

3,使用armlink 选项 --info=sizes 查看 结构体大小

将示例代码写入str.c,

struct stc
{
    char one;
    short two;
    char three;
    int four;
} c,d;


int main (void)
{
    c.one=1;
    return 0;
}

 并按以下命令编译,生成.o文件:

armclang --target=arm-arm-none-eabi -march=armv8-a -c str.c -o str.o

使用armlink的 -info=sizes选项将.o文件的各个属性的数据的大小列出:

armlink file.o --info=sizes

 其中c和d为全局变量,并未对齐初始化,所以编译器会将其初始化为0,即属于 ZI (zero initialize)数据,由下表可知,其大小为12+12=24 bytes:

Code (inc. data)   RO Data    RW Data    ZI Data      Debug   Object Name

  36          0          0          0         24          0   str.o
---------------------------------------------------------------------------
  36          0         16          0         24          0   Object Totals

参考文章:

Packing data structures

C/C++计算类/结构体和联合体(union)所占内存大小(内存对齐问题)_联合体占用的内存空间文章来源地址https://www.toymoban.com/news/detail-521903.html

到了这里,关于C语言结构体字节对齐(内存对齐)之#pragma pack和__attribute__((packed)的使用的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • C语言之结构体内存对齐与内存的简单理解

    文章目录 内存单元的理解 结构体中内存对齐的规则 为什么会存在内存对齐 首先先要介绍一下C语言中一些常见的存储单元     bit       存放一个二进制位     Byte   1Byte = 8 bit     KB     1KB   = 1024 Byte     MB     1MB   = 1024 KB     GB     1GB   = 1024 MB     TB 

    2023年04月26日
    浏览(28)
  • C语言——结构体类型(二)【结构体内存对齐,结构体数组】

    📝前言: 上一讲结构体类型(一)中,我们讲述了有关 结构体定义,创建,初始化和引用 的内容,这一讲,我们进一步学习结构体的相关知识: 1,结构体内存对齐 2,结构体数组 🎬个人简介:努力学习ing 📋个人专栏:C语言入门基础 🎀CSDN主页 愚润求学 🌄每日鸡汤:

    2024年01月24日
    浏览(54)
  • 【C语言高阶篇】结构体 —— 什么是内存对齐?

    🎬 鸽芷咕 :个人主页  🔥 个人专栏 :《C语言初阶篇》 《C语言进阶篇》 ⛺️生活的理想,就是为了理想的生活!    🌈 hello! 各位宝子们大家好啊,今天给大家带来的是结构体的内存对齐这部分知识,可以说是干货满满啦!    ⛳️ 在我们C语言的面试题中,结构体最

    2024年02月14日
    浏览(42)
  • C语言如何计算结构体大小(结构体的内存对齐)

    结构体的内存对齐是有关结构体内容的很重要一个知识点,主要考察方式是计算结构体的字节大小。 当我们对计算结构体一无所知,我们不妨自己思索如何计算,是不是直接计算结构体成员变量占用内存的大小呢? 那我们先举个例子  观察发现结构体的大小计算跟我们想的

    2024年02月16日
    浏览(32)
  • 【C语言】什么是结构体内存对齐?结构体的大小怎么计算?

      目录 1.结构体内存对齐 对偏移量的理解:​ 2.结构体的大小计算 2.1结构体中只有普通的数据类型的大小计算 2.2 结构体中有嵌套的结构体的大小计算 3.修改默认对齐数 4.为什么存在内存对齐? 这篇文章主要介绍结构体内存对齐和如何计算大小。 在学习结构体内存对齐之前,

    2024年02月11日
    浏览(39)
  • 【C语言13】结构体的声明,定义与结构体的内存对齐

    通俗的说,结构体就是一个类的集合,如同整形数组是整形数字的集合体,结构体也是一些值的集合,这些值称为成员变量。结构的每个成员可以是不同类型的变量。 如果我们要表达一本书,那么这本书的元素有什么呢,书的名字,书的作者,书的创作日期~,而c语言中内置

    2024年02月13日
    浏览(57)
  • 深入了解C语言中的结构体类型与内存对齐

    在C语言中,结构体是一种 自定义的数据类型 , 它允许我们将不同类型的数据组合在一起,形成一个新的数据类型 。结构体的使用为我们解决了一些复杂数据的表示和处理问题,不仅限于单单的整型或者字符。本文将深入探讨结构体类型、结构体变量的创建和初始化,并详

    2024年04月11日
    浏览(30)
  • 【C语言】自定义类型:结构体深入解析(二)结构体内存对齐&&宏offsetof计算偏移量&&结构体传参

    本小节,我们学习结构的内存对齐,理解其对齐规则,内存对齐包含结构体的计算,使用宏 offsetof 计算偏移量,为什么要存在内存对齐?最后了解结构体的传参文章干货满满!学习起来吧😃! 结构体内存对齐指的是 结构体中各成员变量在内存中的存储位置按照一定规则对齐

    2024年02月04日
    浏览(35)
  • C语言自定义类型:结构体的使用及其内存对齐【超详细建议点赞收藏】

    结构是一些值的集合,这些值称为 成员变量 。 结构的每个成员可以是不同类型的变量 。 注意 : 成员列表可以是不同类型的变量; 成员后一定要有分号; 花括号后也有一个分号。 例如描述一个学生: 注意:上述代码没有创建变量,也没有初始化, 只是声明了一个结构体类

    2024年03月13日
    浏览(38)
  • 【C语言高阶篇】C语言面试必考知识点,结构体的内存对齐我看还有谁不会!

    🎬 鸽芷咕 :个人主页  🔥 个人专栏 :《C语言初阶篇》 《C语言进阶篇》 ⛺️生活的理想,就是为了理想的生活!    🌈 hello! 各位宝子们大家好啊,今天给大家带来的是结构体的内存对齐这部分知识,可以说是干货满满啦!    ⛳️ 在我们C语言的面试题中,结构体最

    2024年02月16日
    浏览(33)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包