MDK对齐方式设定

发布者:MysticalEssence最新更新时间:2015-09-09 来源: eefocus关键字:MDK  对齐方式 手机看文章 扫描二维码
随时随地手机看文章
__align(4) u8 mem1base[MEM1_MAX_SIZE];

 为何要字节对齐?

 从理论上讲似乎对任何类型的变量的访问可以从任何地址开始,但实际情况是在访问特定类型变量的时候经常在特定的内存地址访问,各个硬件平台对存储空间的处理上有很大的不同。一些平台对某些特定类型的数据只能从某些特定地址开始存取。

 

MDK 字节对齐关键字 __packed

typedef __packed struct abc
{
 int a;
 int b;
}strabc;

 

vxworks 字节对齐关键字 _WRS_PACK_ALIGN(1)

 typedef  struct abc
{
 int a;
 int b;
}_WRS_PACK_ALIGN(1) strabc;

 

(1)什么是字节对齐
    一个变量占用 n 个字节,则该变量的起始地址必须能够被 n 整除,即: 存放起始地址 % n = 0, 对于结构体而言,这个 n 取其成员种的数据类型占空间的值最大的那个。
(2)为什么要字节对齐
    内存空间是按照字节来划分的,从理论上说对内存空间的访问可以从任何地址开始,但是在实际上不同架构的CPU为了提高访问内存的速度,就规定了对于某些类 型的数据只能从特定的起始位置开始访问。这样就决定了各种数据类型只能按照相应的规则在内存空间中存放,而不能一个接一个的顺序排列。

    举个例子,比如有些平台访问内存地址都从偶数地址开始,对于一个int型(假设32位系统),如果从偶数地址开始的地方存放,这样一个读周期就可以读出这 个int数据,但是如果从奇数地址开始的地址存放,就需要两个读周期,并对两次读出的结果的高低字节进行拼凑才能得到这个int数据,这样明显降低了读取 的效率。
(3)如何进行字节对齐

    每个成员按其类型的对齐参数(通常是这个类型的大小)和指定对齐参数(不指定则取默认值)中较小的一个对齐,并且结构的长度必须为所用过的所有对齐参数的整数倍,不够就补空字节。

    这个规则有点苦涩,可以把这个规则分解一下,前半句的意思先获得对齐值后与指定对齐值进行比较,其中对齐值获得方式如下:

1. 数据类型的自身对齐值为:对于char型数据,其自身对齐值为1,对于short型为2,对于int, long, float类型,其自身对齐值为4,对于 double 类型其自身对齐值为8,单位为字节。
2.结构体自身对齐值:其成员中自身对齐值最大的那个值。

其中指定对齐值获得方式如下:

#pragma pack (value)时的指定对齐值value。

未指定则取默认值。

    后半句的意思是主要是针对于结构体的长度而言,因为针对数据类型的成员,它仅有一个对齐参数,其本身的长度、于这个对齐参数,即1倍。对于结构体而言,它 可能使用了多种数据类型,那么这句话翻译成对齐规则: 每个成员的起始地址 % 自身对齐值 = 0,如果不等于 0 则先补空字节直至这个表达式成立。
    换句话说,对于结构体而言,结构体在在内存的存放顺序用如下规则即可映射出来:
    (一)每个成员的起始地址 % 每个成员的自身对齐值 = 0,如果不等于 0 则先补空字节直至这个表达式成立;
    (二)结构体的长度必须为结构体的自身对齐值的整数倍,不够就补空字节。

    举个例子:

 

#pragma pack(8)
struct A{
    char a;
    long b;
};

struct B{
    char a;
    struct A b;
    long c;
};

struct C{
    char a;
    struct A b;
    double c;
};

struct D{
    char a;
    struct A b;
    double c;
    int d;
};

struct E{
    char a;
    int b;
    struct A c;
    double d;    
};


    对于 struct A 来说,对于char型数据,其自身对齐值为1,对于long类型,其自身对齐值为4, 结构体的自身对齐值取其成员最大的对齐值,即大小4。那么struct A 在内存中的顺序步骤为:
(1) char a, 地址范围为0x0000~0x0000,起始地址为0x0000,满足 0x0000 % 1 = 0,这个成员字节对齐了。
(2) long b, 地址起始位置不能从0x00001开始,因为 0x0001 % 4 != 0,  所以先补空字节,直到0x00003结束,即补3个字节的空字节,从0x00004开始存放b,其地址范围为0x00004~0x0007.
(3)此时成员都存放结束,结构体长度为8,为结构体自身对齐值的2倍,符合条件(二).
    此时满足条件(一)和条件(二),struct A 中各成员在内存中的位置为:a*** b ,sizeof(struct A) = 8。(每个星号代表一位,成员各自代表自己所占的位,比如a占一位,b占四位)

    对于struct B,里面有个类型为struct A的成员b自身对齐值为4,对于long类型,其自身对齐值为4. 故struct B的自身对齐值为4。那么struct B 在内存中的顺序步骤为:
(1) char a, 地址范围为0x0000~0x0000,起始地址为0x0000,满足 0x0000 % 1 = 0,这个成员字节对齐了。
(2) struct A b, 地址起始位置不能从0x00001开始,因为 0x0001 % 4 != 0,  所以先补空字节,直到0x00003结束,即补3个字节的空字节,从0x00004开始存放b,其地址范围为0x00004~0x00011.
(3) long c,地址起始位置从0x000012开始, 因为 0x0012 % 4 = 0,其地址范围为0x00012~0x0015.
(4)此时成员都存放结束,结构体长度为16,为结构体自身对齐值的4倍,符合条件(二).
    此时满足条件(一)和条件(二),struct B 中各成员在内存中的位置为:a*** b c ,sizeof(struct C) = 24。(每个星号代表一位,成员各自代表自己所占的位,比如a占一位,b占八位,c占四位)
    对于struct C,里面有个类型为struct A的成员b自身对齐值为4,对于double 类型,其自身对齐值为8. 故struct C的自身对齐值为8。那么struct C 在内存中的顺序步骤为:
(1) char a, 地址范围为0x0000~0x0000,起始地址为0x0000,满足 0x0000 % 1 = 0,这个成员字节对齐了。
(2) struct A b, 地址起始位置不能从0x00001开始,因为 0x0001 % 4 != 0,  所以先补空字节,直到0x00003结束,即补3个字节的空字节,从0x00004开始存放b,其地址范围为0x00004~0x00011.
(3) double c,地址起始位置不能从0x000012开始, 因为 0x0012 % 8 != 0,所以先补空字节,直到0x000015结束,即补4个字节的空字节,从0x00016开始存放c,其地址范围为0x00016~0x0023.
(4)此时成员都存放结束,结构体长度为24,为结构体自身对齐值的3倍,符合条件(二).
    此时满足条件(一)和条件(二),struct C 中各成员在内存中的位置为:a*** b **** c ,sizeof(struct C) = 24。(每个星号代表一位,成员各自代表自己所占的位,比如a占一位,b占八位,c占八位)

    对于struct D,自身对齐值为8。前面三个成员与 struct C 是一致的。对于第四成员d,因为 0x0024 % 4 = 0, 所以可以从0x0024开始存放d, 其地址范围为0x00024~0x00027.此时成员都存放结束,结构体长度为28,28 不是结构体自身对齐值8的倍数,所以要在后面补四个空格,即在0x0028~0x0031上补四个空格。补完了,结构体长度为32, 为结构体自

身对齐值的4被,,符合条件(二).
    此时满足条件(一)和条件(二),struct D 中各成员在内存中的位置为:a*** b **** c d **** ,sizeof(struct D) = 32。(每个星号代表一位,成员各自代表自己所占的位,比如a占一位,b占八位,c占八位, d占四位)。

    对于struct E 中各成员在内存中的位置为:a*** b c d, sizeof(struct E) = 24。(每个星号代表一位,成员各自代表自己所占的位,比如a占一位,b占四位,c占八位, d占八位)。
    通过struct D 和 struct E 可以看出,在成员数量和类型一致的情况,后者的所占空间少于前者,因为后者的填充空字节要少。如果我们在编程时考虑节约空间的话,应该遵循将变量按照类型 大小从小到大声明的原则, 这样尽量减少填补空间。另外,可以在填充空字节的地方来插入reserved成员, 例如
struct A
{
   char a;
   char reserved[3];
   int b;
};
这样做的目的主要是为了对程序员起一个提示作用,如果不加则编译器会自动补齐。

 

 

习题

typedef struct

{

   int a;               //ARM(int=4)  51(int=2)

   char b;           // 1

   short c;         // 2

}AAA;

typedef struct

{

   char b;

   int a;

   short c;

}BBB;

 

i = sizeof(AAA);

j = sizeof(BBB);

//注意在51单片机,ARM,PC不同  

 

51 (i=j=5)            //好像强制单字节对齐

ARM(i=8,j=12)  //按规则默认对齐

PC(i=8,j=12)

 

AAA对齐方式如下(ARM)

I I I I

I0I I

 

BBB对齐方式如下(ARM)

I 0 0 0

 I  I

I I  0 0

 

通过#pragma pack可以调整对齐字节数 

#pragma pack(1) //指定Align为 1字节;

。。。。。。。。。。。。//需要对齐的结构体

    

#pragma pack() //恢复到原先值

关键字:MDK  对齐方式 引用地址:MDK对齐方式设定

上一篇:字符串倒序查找字串
下一篇:UC/OSⅡ基本函数

推荐阅读最新更新时间:2024-03-16 14:31

MDK硬件调试_在Debug printf Viewer窗口显示打印信息_ITM
平时调试代码的时候大家都喜欢用printf函数来输出一些打印信息,来提示自己代码的执行情况。在刚开始接触这个调试方法的时候,我采用的方式就是浪费一个芯片的串口,然后利用TTL转USB模块,在PC端的串口调试助手中打印提示信息。另一种方式是硬件仿真的时候可以利用J-Link等工具实现的一种打印提示信息的方式。由于需要重定向fputc,所以两者最终只能选择其一,如果在资源不紧张的情况下,浪费一个串口其实也没啥,但是有些项目中会有很多通信接口需要使用这个时候就不可能浪费一个串口给我们做调试用了,这个时候通过MDK输出一些打印信息对于我们调试代码是很有帮助的。能实现MDK硬件调试打印调试信息的方式有两种:ITM机制和semihosting
[单片机]
<font color='red'>MDK</font>硬件调试_在Debug printf Viewer窗口显示打印信息_ITM
STM32——MDK4与MDK5中设置系统各部分时钟对比
/******************************************************************************* * 函数名 : RCC_Configuration * 函数描述 : 在MDK4中设置系统各部分时钟 * 输入参数 : 无 * 输出结果 : 无 * 返回值 : 无 *******************************************************************************/ void RCC_Configuration(void) { /* 定义枚举类型变量 HSEStartUpStatus */
[单片机]
解决mdk生成bin烧录lpc1343无法运行问题
最近拿到了lpc1343的最小系统板,arm cortex-M3,好东西啊。 拿起手册赶紧看起,看完系统时钟跟GPIO部分就忍不住要跑程序了。呵呵 具体的配置就不说了。写好程序就开始编译。 通过LPC1343 芯片本身自带的USB BootLoader 来更新我自己的程序,这个功能太爽了。 首先mdk生成的是hex文件,需要转化为bin文件,用keil自带的工具可以实现 使用FROMELF命令行生成BIN的文件, 使用方法如下: keil-project-options for target-user-run user programs after build - run #1 C:/Keil/ARM/
[单片机]
STM32在MDK下的一种通用建立库函数工程的方法(标准库)
物料准备 MDK4或者(MDK5+Legacy支持包) ST官方下载的库文件(STM32F10x_StdPeriph_Lib_V3.5.0) 下载地址 http://pan.baidu.com/s/1cyxwXS 库函数主要文件夹简介 需要的用的库文件都在STM32F10x_StdPeriph_Lib_V3.5.0Libraries文件夹下,它又包含了以下两个文件夹 CMSIS(一些核心和库文件代码,CMSIS主要代码) CM3DeviceSupportSTSTM32F10x文件夹 system_stm32f10x.c(STM32F10x CMSIS Cortex-M3设备访问层的系统源文件) system_stm32f10
[单片机]
STM32在<font color='red'>MDK</font>下的一种通用建立库函数工程的方法(标准库)
Keil MDK编译器(V4.03)与J-LINK使用
前几天进手了一个J-LINK,因为H-JTAG毕竟对MDK支持的不是太完美,比如用keil mdk上面的下载按钮烧录程序,不是直接就能烧录进去,而是弹出H-Flash软件,再手工选择文件烧录;而且用H_JTAG单步调试还容易莫名其妙的跑飞。好在J-LINK不是太贵(我是说国内仿制的),mdk又能比较完美的支持J-link,今天就将我使用mdk和J-link的情况做一下记录。 我把MDK升级到了V4.10,之前使用的V4.01,当然这个V4.10也不是最新版,最新版应该到了V4.12。 首先需要安装mdk编译器,可以到 http://www.embedinfo.com/down-class.asp?lx=big&an
[单片机]
Keil <font color='red'>MDK</font>编译器(V4.03)与J-LINK使用
stm32专题三十六:MDK编译过程和文件类型(三)
生成 bin 和 hex文件 若编译过程无误,即可把工程生成前面对应的*.axf 文件,而在 MDK 中使用下载器(DAP/JLINK/ULINK 等)下载程序或仿真的时候, MDK 调用的就是*.axf 文件,它解释该文件,然后控制下载器把*.axf 中的代码内容下载到 STM32 芯片对应的存储空间,然后复位后芯片就开始执行代码了。 然而,脱离了 MDK 或 IAR 等工具,下载器就无法直接使用*.axf 文件下载代码了,它们一般仅支持 hex 和 bin 格式的代码数据文件。默认情况下 MDK 都不会生成 hex 及 bin 文件,需要配置工程选项或使用 fromelf 命令。 fromelf的命令描述:
[单片机]
stm32专题三十六:<font color='red'>MDK</font>编译过程和文件类型(三)
MDK中用C++开发STM32
前言 最近想开发一段单片机的代码,代码本身有很多的重复元素,这重复定义的一些结构体使用起来有些繁琐,所以就想用C++开发,C++的继承 模板类可以很容易的解决这些问题。因为在单片机运行,习惯用MDK或者IAR这些软件。但是这些软件都是默认C开发的,用C++开发需要重新配置,有些麻烦。但是我还是试了试,做了一个小demo供大家参考。 代码文件我传到我的github中去了,大家有兴趣可以参考一下 https://github.com/conscience-still/MDK-Cplusplus--LED 一、STM32CubeMX生成底层代码 因为是做一个demo,不需要很复杂,就用cubemx生成了一个简单的串口和IO控制的
[单片机]
<font color='red'>MDK</font>中用C++开发STM32
基于RealView MDK的CAN总线仿真研究
  0 引言   RealView MDK是ARM公司的产品,被全球超过10万的嵌入式开发工程师验证和使用,是ARM公司目前最新推出的针对各种嵌入式处理器的软件开发工具。RealView MDK集成了业内最领先的技术,包括μVision3集成开发环境与RealView编译器。支持ARM7、ARM9和最新的Cortex-M3核处理器,自动配置启动代码,集成Flash烧写模块,强大的Simulation设备模拟、性能分析等功能。   当前多数基于ARM的开发工具都有仿真功能,但是仅仅局限于对内核指令集的仿真。而RealView MDK的设备模拟器可以仿真整个目标硬件,包括快速指令集仿真、外部信号和I/O仿真、中断过程
[嵌入式]
小广播
添点儿料...
无论热点新闻、行业分析、技术干货……
设计资源 培训 开发板 精华推荐

最新单片机文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

换一换 更多 相关热搜器件
随便看看
电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved