打印

ARM中字节对齐的深入探讨

[复制链接]
6161|6
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
a_bb|  楼主 | 2007-4-17 22:39 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
阅读了yos的**《内存对齐问题学习小结》,深有体会,看来在进行指针操作时,必须进行强制类型转换,否则可能出现预想不到的错误。

在我的一个项目中,需要进行数据包解码,同样出现数据对齐的问题,却没能找到好的解决方法问题如下
CPU ARM7 ,编译环境 Keil RVCT3.0

#pragma pack(1)
typedef struct {
  uint8 u8a[3];
  uint8 u8b[4];
  uint16 u16c;
  uint32 u32d;
  uint32 u32e;
} TSTBLK,* PTSTBLK;

主程序
main
{
    uint16 i;
    PTSTBLK    pTST;

    uint16 u16k;
    uint32 u32m,u32l;
    uint8  DBUF[200];
    
    for(i= 0 ;i<200;i++)DBUF=i;

    pTST=(PTSTBLK)DBUF;    // 1
    u16k = (pTST->u16c);    // 2
    u32m = pTST->u32d;      // 3
    u32l = pTST->u32e;      // 4
//以下语句是避免 u16k,u32m,u32l被优化掉
    i = u16k;
    i = (uint16)u32m;
    i = (uint16)u32l;
}

运行语句1 后, 结构体中的
u8a[] = 0x00~0x02
u8a[] = 0x03~0x06
u16c = 0x0807
u32d = 0x0C0B0A09
u32e = 0x100F0E0D
显然以上结果是我们所需要的,正确!
但继续运行 2,3,4得到
u16k = 0x0706
u32m = 0x080B0A09
u32l = 0x0C0F0E0D
字节对齐发生了问题,乱了!
乱得还不轻 u32m 没有等于0x0B0A0908
u32m 没有等于0x0B0A0908
u32m 也没有等于0x0F0E0D0C
why?

我试图用u16k = (uint16)(pTST->u16c);
    u32m = (uint32)pTST->u32d;
去修改,但无效!
其实pTST->u16c本身就是16位的,强制转到16位自然没有任何意义。

请问各位有好的处理方法吗?毕竟在数据解码中非对齐格式是很常见的!
谢谢各位

相关帖子

沙发
coke| | 2007-4-18 13:32 | 只看该作者

使用__packed关键词即可

使用特权

评论回复
板凳
coke| | 2007-4-18 13:33 | 只看该作者

re

如:
typedef __packed struct
{
    U8 Reserved1;
    U8 Command_format;
    U16 List_length;
    U32 Number_block;
    U8 Reserved2;
    U16 Block_length;
}Format_list;

使用特权

评论回复
地板
hfhwgh| | 2007-4-19 00:18 | 只看该作者

你说的情况对于ARM CPU确实存在,但对于其它体系结构就不会

这是一个典型的ARM非对齐访问的问题。#pragma pack(1)能保证你的结构体中的数据是紧缩对齐的(在内存中是依次排列的)。那么对于
#pragma pack(1)
typedef struct {
  uint8 u8a[3];
  uint8 u8b[4];
  uint16 u16c;
  uint32 u32d;
  uint32 u32e;
} TSTBLK,* PTSTBLK;
假设该结构体存放的基地址为0,则u8a[3]位于0-2字节, u8b[4]位于3-6字节,u16c位于7-8字节,依次类推。那么当我们去访问u16c时编译器会编译成一条访问地址为7的半字读的汇编语言,而地址为7对于半字读来说是一个非对齐访问,CPU就自动会把地址变成把最低位忽略,也是说CPU读的实际地址为6,于是读u16c得到的是6 、7两个字节即u16c=0x0706。对于字的访问CPU会忽略低两位地址,分析方法与前相同。你的两个32位数我不能理解,你怎么可能得到那样的结果,是不是写错了哦??我觉得你应该分别得到0x0B0A0908 0x0F0E0D0C才对。当然还涉及一个字节序的问题。
这一个问题在ARM CPU中会出现,但对于POWERPC的CPU或X86的CPU你的代码就不会出现问题,这都是CPU对非对齐访问采用的处理方式不同造成,POWERPC X86 会把非半字的非对齐访问变成两个字节访问,因为不会出现上面问题。MIPS我没有去研究过。
对于ARM CPU把结构体改为:
#pragma pack(1)
typedef struct {
   uint8 u8b[4];
  uint32 u32d;
  uint32 u32e;  
uint16 u16c;
uint8 u8a[3];
} TSTBLK,* PTSTBLK;
整个结构体仍然只占23个字节,但应该不会出现前面的问题。

 

使用特权

评论回复
5
a_bb|  楼主 | 2007-4-19 08:43 | 只看该作者

__packed 能解决问题

谢谢大家,用__packed 可以解决问题,但我没理解__packed 和#pragma pack(1)的区别,请问谁能再解释得清楚些?

我先前的实验结果确实是 u32m = 0x080B0A09   u32l = 0x0C0F0E0D
为什么不是0x0B0A0908 和0x0F0E0D0C,原因不详

我不同意更改结构体,因为数据结构是规定死了,不能随意改
对于ARM CPU把结构体改为:
#pragma pack(1)
typedef struct {
   uint8 u8b[4];
  uint32 u32d;
  uint32 u32e;  
uint16 u16c;
uint8 u8a[3];
} TSTBLK,* PTSTBLK;

使用特权

评论回复
6
hfhwgh| | 2007-4-19 12:09 | 只看该作者

re

http://blog.csdn.net/xhfwr/archive/2006/07/23/963793.aspx 这个地址有一些解释。我的理解是:#pragma pack(1)只是将结构体按字节排列,而编译成汇编时仍然按数据类型来的如对一个16位数据的读就是一条半字读指令,但它不去关心地址是否非对齐;__packed 不但会字节排列还会指示编译器把非对齐的访问变成对齐访问如:访问非对齐的半字访问编译成两个字节的访问,这样就不会有非对齐访问了。  谢谢楼主的问题和二楼的解答,我一直都没有想明白ARM应该怎么来防止非对齐访问,这下总算明白一点呢。    

使用特权

评论回复
7
a_bb|  楼主 | 2007-4-19 13:10 | 只看该作者

感悟

谢谢大家的讨论,受益非浅

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

35

主题

98

帖子

0

粉丝