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

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

3天内不再提示

C语言中Linux字节对齐的问题

multisim 来源: 一口Linux 作者:土豆居士 2021-08-16 11:25 次阅读

最近作者在做一个项目,遇到一个问题,运行于ARM上的threadx在与DSP通信采用消息队列的方式传递消息(最终实现原理是中断+共享内存的方式),在实际操作过程中发现threadx总是crash,于是经过排查,是因为传递消息的结构体没有考虑字节对齐的问题。

随手整理一下C语言中字节对齐的问题与大家一起分享。

一、概念

对齐跟数据在内存中的位置有关。如果一个变量的内存地址正好位于它长度的整数倍,他就被称做自然对齐。比如在32位cpu下,假设一个整型变量的地址为0x00000004,那它就是自然对齐的。

首先了解什么位、字节、字

bit 1个二进制位称为1个bit
字节 Byte 8个二进制位称为1个Byte
word 电脑用来一次性处理事务的一个固定长度
名称 英文名 含义

字长

一个字的位数,现代电脑的字长通常为16,32, 64位。(一般N位系统的字长是N/8字节。)

不同的CPU一次可以处理的数据位数是不同的,32位CPU可以一次处理32位数据,64位CPU可以一次处理64位数据,这里的位,指的就是字长。

而所谓的字长,我们有时会称为字(word)。在16位的CPU中,一个字刚好为两个字节,而32位CPU中,一个字是四个字节。若以字为单位,向上还有双字(两个字),四字(四个字)。

二、对齐规则

对于标准数据类型,它的地址只要是它的长度的整数倍就行了,而非标准数据类型按下面的原则对齐:数组 :按照基本数据类型对齐,第一个对齐了后面的自然也就对齐了。联合 :按其包含的长度最大的数据类型对齐。结构体:结构体中每个数据类型都要对齐。

三、如何限制定字节对齐位数?

1. 缺省

在缺省情况下,C编译器为每一个变量或是数据单元按其自然对界条件分配空间。一般地,可以通过下面的方法来改变缺省的对界条件:

2. #pragma pack(n)

· 使用伪指令#pragma pack (n),C编译器将按照n个字节对齐。· 使用伪指令#pragma pack (),取消自定义字节对齐方式。

#pragma pack(n) 用来设定变量以n字节对齐方式。n字节对齐就是说变量存放的起始地址的偏移量有两种情况:

如果n大于等于该变量所占用的字节数,那么偏移量必须满足默认的对齐方式

如果n小于该变量的类型所占用的字节数,那么偏移量为n的倍数,不用满足默认的对齐方式。

结构的总大小也有一个约束条件,如果n大于等于所有成员变量类型所占用的字节数,那么结构的总大小必须为占用空间最大的变量占用的空间数的倍数;否则必须是n的倍数。

3. __attribute

另外,还有如下的一种方式:· __attribute((aligned (n))),让所作用的结构成员对齐在n字节自然边界上。如果结构中有成员的长度大于n,则按照最大成员的长度来对齐。·attribute((packed)),取消结构在编译过程中的优化对齐,按照实际占用字节数进行对齐。

3. 汇编.align

汇编代码通常用.align来制定字节对齐的位数。

.align:用来指定数据的对齐方式,格式如下:

.align[absexpr1,absexpr2]

以某种对齐方式,在未使用的存储区域填充值. 第一个值表示对齐方式,4, 8,16或 32. 第二个表达式值表示填充的值。

四、为什么要对齐?

操作系统并非一个字节一个字节访问内存,而是按2,4,8这样的字长来访问。因此,当CPU从存储器读数据到寄存器,IO的数据长度通常是字长。如32位系统访问粒度是4字节(bytes), 64位系统的是8字节。当被访问的数据长度为n字节且该数据地址为n字节对齐时,那么操作系统就可以高效地一次定位到数据,无需多次读取,处理对齐运算等额外操作。数据结构应该尽可能地在自然边界上对齐。如果访问未对齐的内存,CPU需要做两次内存访问。

字节对齐可能带来的隐患:

代码中关于对齐的隐患,很多是隐式的。比如在强制类型转换的时候。例如:

unsignedinti=0x12345678; unsignedchar*p=NULL; unsignedshort*p1=NULL; p=&i; *p=0x00; p1=(unsignedshort*)(p+1); *p1=0x0000;

最后两句代码,从奇数边界去访问unsignedshort型变量,显然不符合对齐的规定。在x86上,类似的操作只会影响效率,但是在MIPS或者sparc上,可能就是一个error,因为它们要求必须字节对齐.

五、举例

例1:os基本数据类型占用的字节数

首先查看操作系统的位数

在64位操作系统下查看基本数据类型占用的字节数:

#include intmain() { printf("sizeof(char)=%ld ",sizeof(char)); printf("sizeof(int)=%ld ",sizeof(int)); printf("sizeof(float)=%ld ",sizeof(float)); printf("sizeof(long)=%ld ",sizeof(long)); printf("sizeof(longlong)=%ld ",sizeof(longlong)); printf("sizeof(double)=%ld ",sizeof(double)); return0; }

例2:结构体占用的内存大小--默认规则

考虑下面的结构体占用的位数

structyikou_s { doubled; charc; inti; }yikou_t;

执行结果

sizeof(yikou_t)=16

在内容中各变量位置关系如下:

9e6db748-fdb8-11eb-9bcf-12bb97331649.png

其中成员C的位置还受字节序的影响,有的可能在位置8

编译器给我们进行了内存对齐,各成员变量存放的起始地址相对于结构的起始地址的偏移量必须为该变量类型所占用的字节数的倍数, 且结构的大小为该结构中占用最大空间的类型所占用的字节数的倍数。

对于偏移量:变量type n起始地址相对于结构体起始地址的偏移量必须为sizeof(type(n))的倍数结构体大小:必须为成员最大类型字节的倍数

char:偏移量必须为sizeof(char)即1的倍数 int:偏移量必须为sizeof(int)即4的倍数 float:偏移量必须为sizeof(float)即4的倍数 double:偏移量必须为sizeof(double)即8的倍数

例3:调整结构体大小

我们将结构体中变量的位置做以下调整:

structyikou_s { charc; doubled; inti; }yikou_t;

执行结果

sizeof(yikou_t)=24

各变量在内存中布局如下:

9eb0590e-fdb8-11eb-9bcf-12bb97331649.png

当结构体中有嵌套符合成员时,复合成员相对于结构体首地址偏移量是复合成员最宽基本类型大小的整数倍。

例4:#pragma pack(4)

#pragmapack(4) structyikou_s { charc; doubled; inti; }yikou_t;sizeof(yikou_t)=16

例5:#pragma pack(8)

#pragmapack(8) structyikou_s { charc; doubled; inti; }yikou_t;sizeof(yikou_t)=24

例6:汇编代码

举例:以下是截取的uboot代码中异常向量irq、fiq的入口位置代码:

9f00b354-fdb8-11eb-9bcf-12bb97331649.png

六、汇总实力

有手懒的同学,直接贴一个完整的例子给你们:

#include main() { structA{ inta; charb; shortc; }; structB{ charb; inta; shortc; }; structAA{ //inta; charb; shortc; }; structBB{ charb; //inta; shortc; }; #pragmapack(2)/*指定按2字节对齐*/ structC{ charb; inta; shortc; }; #pragmapack()/*取消指定对齐,恢复缺省对齐*/ #pragmapack(1)/*指定按1字节对齐*/ structD{ charb; inta; shortc; }; #pragmapack()/*取消指定对齐,恢复缺省对齐*/ ints1=sizeof(structA); ints2=sizeof(structAA); ints3=sizeof(structB); ints4=sizeof(structBB); ints5=sizeof(structC); ints6=sizeof(structD); printf("%d ",s1); printf("%d ",s2); printf("%d ",s3); printf("%d ",s4); printf("%d ",s5); printf("%d ",s6); } ------------END------------

责任编辑:haq

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

    关注

    87

    文章

    11236

    浏览量

    209024
  • C语言
    +关注

    关注

    180

    文章

    7602

    浏览量

    136359
  • 字节
    +关注

    关注

    0

    文章

    40

    浏览量

    13718

原文标题:Linux字节对齐的那些事

文章出处:【微信号:A1411464185,微信公众号:multisim】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

    C语言中申请的堆内存能不能自动释放

    C语言中申请的堆内存能不能自动释放?每次都要手动 free 太麻烦,也容易忘记。 学过 C++ 的同学,应该首先能想到智能指针。 但是这是C语言
    的头像 发表于 11-27 09:33 56次阅读

    C语言中的socket编程基础

    Socket编程简介 Socket是一种通信机制,允许程序之间进行通信。在C语言中,socket编程是网络编程的基础。通过使用socket,程序可以发送和接收数据,实现不同计算机之间的通信
    的头像 发表于 11-01 16:51 262次阅读

    c语言中从左到右结合怎么看

    C语言中,操作符的结合性(Associativity)是指当操作符在表达式中连续出现时,它们如何与操作数结合的顺序。对于大多数二元操作符(即需要两个操作数的操作符),C语言遵循两种基
    的头像 发表于 08-20 11:42 750次阅读

    C语言基础-为什么要使用C

    当今最流行的 Linux 操作系统和 RDBMS(Relational Database Management System:关系数据库管理系统) MySQL 都是使用 C 语言编写的。
    发表于 03-25 11:20 418次阅读

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

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

    C语言中的typedef的应用

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

    C语言#define的应用

    C/C++ 编程语言中,当程序被编译时,被发送到编译器,编译器将程序转换为机器语言,然后完成编译并执行该程序。预处理器也称为宏预处理器。
    发表于 03-06 11:29 362次阅读
    <b class='flag-5'>C</b><b class='flag-5'>语言</b>#define的应用

    介绍C语言中错误处理和异常处理的一些常用的方法和策略

    C语言是一种低级的、静态的、结构化的编程语言,它没有提供像C++或Java等高级语言中的异常处理机制,例如try-catch-finally
    的头像 发表于 02-28 14:25 600次阅读

    C语言中的可变参数介绍

    C 语言为这种情况提供了一个解决方案,它允许您定义一个函数,能根据具体的需求接受可变数量的参数
    发表于 02-28 14:00 290次阅读
    <b class='flag-5'>C</b><b class='flag-5'>语言中</b>的可变参数介绍

    枚举有多大?c语言枚举end的作用是什么?

    枚举有多大?c语言枚举end的作用是什么? 枚举在C语言中是一种常见的数据类型,用于定义一组相互关联的常量或者变量。它通常用于表示一系列可能的取值,使得程序更加易读和易维护。在
    的头像 发表于 01-19 14:19 573次阅读

    如何解决C语言中的“访问权限冲突”异常?C语言引发异常原因分析

    如何解决C语言中的“访问权限冲突”异常?C语言引发异常原因分析  在C语言中,访问权限冲突异常通
    的头像 发表于 01-12 16:03 5287次阅读

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

    的默认对齐方式和规则 结构体在Keil Arm工程中的默认对齐方式是根据编译器或者编译器选项来定的。通常情况下,编译器会按照平台的特定对齐方式对结构体进行对齐,这样可以提高内存访问速度
    的头像 发表于 01-05 14:40 3573次阅读

    C语言中结构体能不能相加

    C语言中,结构体能不能相加?
    的头像 发表于 12-19 17:04 1229次阅读
    <b class='flag-5'>C</b><b class='flag-5'>语言中</b>结构体能不能相加

    如何给C语言中的函数定义两个不同的名字?

    最近有位哥问我,如何给C语言中的函数定义两个不同的名字?就是这两个名字都是指向同一个函数,同一个地址,而且两个名字都可以当做函数来用的那种。
    的头像 发表于 12-19 16:21 778次阅读

    从AD7712读取的数据在C语言中是保存为什么数据类型?

    谁用过AD7712吗,从AD7712读取的数据在C语言中是保存为什么数据类型啊,long int 吗?我用AD7712对波形进行数字化,AD7712输出的数据应该是有正有负的对吧?
    发表于 12-13 09:33