序对齐问题的原因及举例说明...

探讨了在不同编译器环境下(如VC和ADS)结构体成员变量的字长对齐原则及其对程序效率的影响,并分析了在x86架构下对齐访问如何提升数据读写速度。
DeepBlue @ 2007-09-07 09:40

 

因为今天和同事谈到了ARM平台下数据总线宽度及对齐方式对程序效率的影响问题
在定义结构数据类型时,为了提高系统效率,要注意字长对齐原则。
1.
先看下面的例子:
#include <iostream.h>
#pragma pack(4)
struct A
{
 char a;
 int  b;
};
#pragma pack()

#pragma pack(1)
struct B
{
 char a;
 int  b;
};
#pragma pack()

int main()
{
 
 A a;
 cout<<sizeof(a);   //8
 
 B b;
 cout<<sizeof(b);   //5
}

默认的vc我记得是4字节对齐,而ADS下是一字节对齐。
先谈PC下的对齐:
大家可以看到在ms的vc下按4字节对齐和1字节对齐的结果是截然不同的分别为8和5为什么会有这样的结果呢?这就是x86上字节对齐的作用。为了加快程序执行的速度,一些体系结构以对齐的方式设襁POST http://blog.ycul.com/post.php HTTP/1.0 D?长作为对齐边界。对于一些结构体变量,整个结构要对齐在内部成员变量最大的对齐边界,如A,整个结构以4为对齐边界,所以sizeof(a)为8,而不是5。
如果是原始我们概念下的的A中的成员将会一个挨一个存储 应该只有char+int只有5个字节这个差异就是由于对齐导致的显然我们可以看到 A的对齐要比B浪费3个字节的存储空间那为什么还要采取对齐呢?
那是因为体系结构的对齐和不对齐,是在时间和空间上的一个权衡。
字节对齐节省了时间。应该是设计者考虑用空间换取时间。

为什么说对齐会提高效率呢节省时间?我想大家要理解的重点之重点就在这里了
在我们常用的PC下总线宽度是32位
1.如果是总线宽度对齐的话
那么所有读写操作都是获取一个<=32位数据可以一次保证在数据总线传输完毕。没有任何的额外消耗。
|1|2|3|4|5|6|7|8|
从1开始这里是a的起始位置,5起始为b的位置 访问的时候
如果访问a一次在总线传输8位其他24位无效的
访问b时则一次在总线上传输32完成
读写均是一次完整
插说一下:读操作先要将读地址放到地址总线上然后下个时钟周期再从外部
存储器接口上读回数据通过数据总线返回需要两个周期
而写操作一次将地址及数据写入相应总线就完成了。
读操作要比写操作慢一半
 
2.我们看访问数据时如果不对齐地址的情况
|1|2|3|4|5|6|7|8|
此时a的地址没变还在1而因为是不对齐则b的位置就在2处
这时访问就带来效率上问题 访问a时没问题还是读会一个字节
但是2处地址因为不是总线宽度对齐一般的CPU在此地址操作将产生error
sparc,MIPS。它们在硬件的设计上就强制性的要求对齐。在不对齐的地址上肯定发生错误。但是x86是支持非对齐访问的。

它通过多次访问来拼接得到的结果,具体做法就是从1地址处先读回后三字节234 暂存起来。然后再由5地址处读回一个字节5 与234进行拼接组成一个完整的int也就是b返回。
大家看看如此的操作带来的消耗多了不止三倍。很明显在字长对齐时效率要高许多。但然这种效率仅仅是访问多字节带来的。如果还是进行的byte操作那效率差不了多少。

嵌入式开发普遍比较重视性能,所以对齐的问题,有3种不同的处理方法:
1)有一种使用空间换时间做法是显式的插入reserved成员:
         struct A{
           char a;
           char reserved1[3];  //使用空间换时间
           int b;
           
}a;  ==>感觉此种编码方式比较专业,有显式提醒代码阅读者与维护者的功能...
2)随便怎么写,一切交给编译器自动对齐。
3)还有一种将逻辑相关的数据放在一起定义。

代码中关于对齐的隐患,很多是隐式的。比如在强制类型转换的时候。下面举个例子:
unsigned int i = 0x12345678;
unsigned char *p=NULL;
unsigned short *p1=NULL;

p=&i;
*p=0x00;
p1=(unsigned short *)(p+1);
*p1=0x0000;
最后两句代码,从奇数边界去访问unsignedshort型变量,显然不符合对齐的规定。
在x86上,类似的操作只会影响效率,但是在MIPS或者sparc上,可能就是一个error。

<think>好的,我需要详细回答用户关于Trace32的Data.SAVE.Binary命令的完整十六进制保存指令,并提供具体示例。用户特别提到了需要包含十六进制保存的完整指令,并且参考了之前的回答和引用内容,但引用内容似乎和调试信号处理有关,可能与当前问题关联不大,但需要确保不遗漏任何可能相关的信息。 首先,我需要回顾之前关于Data.SAVE.Binary的回答。用户之前询问过如何使用该命令进行内存数据的保存,包括大小端转换,以及在Vim中查看和比较文件。之前的回答已经涵盖了基础命令、大小端选项、地址范围格式以及高级选项如/ZIP和/Append。现在用户希望获得完整的十六进制保存指令,可能是指更详细的参数说明或更复杂的示例。 接下来,我需要确认Data.SAVE.Binary命令的完整语法。根据Trace32的文档,该命令的完整格式可能包括更多的参数,例如数据格式、块大小、填充选项等。用户提到“十六进制保存”,可能是指将内存数据保存为十六进制格式的文件,但通常二进制文件已经是十六进制数据的二进制表示,所以需要澄清用户是否指的是某种十六进制文本格式,比如Intel HEX或Motorola S-record。不过,根据之前的回答,Data.SAVE.Binary通常保存的是原始二进制数据,而不是文本格式的十六进制。因此可能需要确认用户的需求,是否希望将二进制数据转换为十六进制文本格式保存,或者仅仅是正确使用该命令保存二进制数据,并在需要时进行十六进制查看。 如果用户确实需要保存为十六进制文本格式,可能需要额外的转换步骤,因为Data.SAVE.Binary默认保存的是二进制文件。这时可能需要使用其他工具或脚本将二进制文件转换为十六进制文本格式。不过,在Trace32中可能没有直接支持保存为十六进制文本的命令,因此需要明确说明这一点,并建议后续处理步骤,比如使用xxd或hexdump工具转换。 然而,根据用户的问题描述,他们可能只是希望得到更详细的Data.SAVE.Binary命令示例,包括所有可能的参数,尤其是与十六进制相关的部分。需要检查Trace32的文档,确认该命令是否支持十六进制地址指定、数据格式选项等。 在之前的回答中,已经给出了示例命令,如: Data.SAVE.BINARY D:\data.bin 0x40000000--0x4000FFFF /Endian Little 用户现在可能需要更完整的指令,包括更多参数,比如数据宽度(8/16/32位)、块大小、是否填充等。例如,是否可以使用/Width参数指定数据宽度,或者使用其他选项控制保存的数据格式。 此外,用户可能希望了解如何确保保存的数据是完整的十六进制范围,如何处理地址对齐问题,以及如何在保存时进行字节转换。例如,如果目标内存是32位大端格式,而需要保存为小端格式,应如何设置参数。 需要综合之前的回答和Trace32的官方文档,提供更全面的命令示例和参数解释,确保覆盖用户可能需要的所有选项,特别是与十六进制保存相关的部分。同时,要提醒用户注意地址范围的正确格式,以及大小端转换对数据的影响,避免数据错误。 最后,可能需要补充一些常见问题,例如处理非对齐地址时的注意事项,或者如何验证保存的文件是否正确,比如在Vim中使用xxd查看二进制文件的内容,并与内存中的数据进行比较,确保保存过程无误。</think>### Trace32 Data.SAVE.Binary 十六进制保存指令详解 #### 一、完整命令语法 ```t32 Data.SAVE.Binary <文件路径> <内存地址范围> [/Endian <模式>] [/Width <位数>] [/NoFill] [/Block <块大小>] ``` - **核心参数**: - `<文件路径>`:保存路径(支持绝对/相对路径),如 `D:\dump.bin` - `<内存地址范围>`:支持两种格式: - **绝对范围**:`起始地址--结束地址`(如 `0x40000000--0x4000FFFF`) - **偏移范围**:`基地址++长度`(如 `0x20000000++0x1000`) - **关键选项**: - `/Endian <模式>`:字节控制,支持: - `Big`(大端) - `Little`(小端`[^1]`) - `Swap`(交换字节,适用混合系统) - `/Width <位数>`:数据宽度(默认为8位),可选 `8/16/32`,影响内存访问粒度 - `/NoFill`:禁止填充未映射内存区域(默认自动填充0xFF) - `/Block <块大小>`:分块写入(单位:字节),如 `/Block 0x1000` #### 二、十六进制保存示例 1. **基础示例**:保存32位小端数据 ```t32 Data.SAVE.Binary D:\output.bin 0x08000000--0x0800FFFF /Endian Little /Width 32 ``` - 保存地址 `0x08000000` 至 `0x0800FFFF` 的4字节宽数据,按小端转换后写入文件 2. **分块保存**:处理大内存区域 ```t32 Data.SAVE.Binary D:\large_dump.bin 0x40000000++0x100000 /Block 0x4000 /Endian Big ``` - 将 `0x40000000` 开始的1MB内存按64KB分块保存,强制大端格式 3. **混合字节处理**:交换现有数据字节 ```t32 Data.SAVE.Binary D:\swapped.bin 0x20000000--0x20000FFF /Endian Swap ``` - 将原始内存数据的字节完全反转(例如大端转小端) #### 三、验证与调试技巧 1. **Vim十六进制对比**: ```bash # 转换二进制文件为十六进制文本 xxd -g 4 saved_data.bin > hex_dump.txt ``` - `-g 4` 指定4字节为一组显示,便于对齐内存结构[^2] 2. **Trace32内存校验**: ```t32 Data.LOAD.Binary D:\output.bin 0xA0000000 /Endian Little Data.COMPARE 0x08000000--0x0800FFFF 0xA0000000--0xA000FFFF ``` - 加载备份文件到临时地址,与原内存区域对比验证一致性 #### 四、典型问题处理 - **非对齐地址警告**:若地址范围未按数据宽度对齐,Trace32会提示: ```t32 WARNING: Address 0x20001001 not aligned to access size (32-bit) ``` **解决方案**: - 使用 `/Width 8` 按字节存取(避免对齐问题) - 调整地址范围至对齐边界(如 `0x20001000--0x20001003`) --- ### 示例场景:保存DSP寄存器配置 ```t32 // 保存DSP核心寄存器组(32位小端) Data.SAVE.Binary C:\DSP_Registers.bin 0x1C000000++0x400 /Endian Little /Width 32 // 保存后使用Vim分析寄存器值: vim -b DSP_Registers.bin :%!xxd -g 4 -c 16 // 每行16字节,4字节分组显示 ``` ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值