四.如何修改编译器的默认对齐值?
  1.在VC IDE中,可以这样修改:[Project]|[Settings],c/c++选项卡Category的Code Generation选项的Struct Member Alignment中修改,默认是8字节。
  2.在编码时,可以这样动态修改:#pragma pack .注意:是pragma而不是progma.
  五.针对字节对齐,我们在编程中如何考虑?
  如果在编程的时候要考虑节约空间的话,那么我们只需要假定结构的首地址是0,然后各个变量按照上面的原则进行排列即可,基本的原则是把结构中的变量按照 类型大小从小到大声明,尽量减少中间的填补空间.还有一种是为了以空间换取时间的效率,我们显示的进行填补空间进行对齐,比如:有一种使用空间换时间做 法是显式的插入reserved成员:
  struct A{
  char a;
  char reserved[3];//使用空间换时间
  int b;
  }
  reserved成员对我们的程序没有什么意义,它只是起到填补空间以达到字节对齐的目的,当然即使不加这个成员通常编译器也会给我们自动填补对齐,我们自己加上它只是起到显式的提醒作用.
  六.字节对齐可能带来的隐患:
  代码中关于对齐的隐患,很多是隐式的。比如在强制类型转换的时候。例如:
  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,因为它们要求必须字节对齐.
  七.如何查找与字节对齐方面的问题:
  如果出现对齐或者赋值问题首先查看
  1. 编译器的big little端设置
  2. 看这种体系本身是否支持非对齐访问
  3. 如果支持看设置了对齐与否,如果没有则看访问时需要加某些特殊的修饰来标志其特殊访问操作。
  八.ARM下的对齐处理
  from DUI0067D_ADS1_2_CompLib
  3.13 type qulifiers
  有部分摘自ARM编译器文档对齐部分
  对齐的使用:
  1.__align(num)
  这个用于修改高级别对象的字节边界。在汇编中使用LDRD或者STRD时
  要用到此命令__align(8)进行修饰限制。来保证数据对象是相应对齐。
  这个修饰对象的命令大是8个字节限制,可以让2字节的对象进行4字节
  对齐,但是不能让4字节的对象2字节对齐。
  __align是存储类修改,他只修饰高级类型对象不能用于结构或者函数对象。
  2.__packed
  __packed是进行一字节对齐
  1.不能对packed的对象进行对齐
  2.所有对象的读写访问都进行非对齐访问
  3.float及包含float的结构联合及未用__packed的对象将不能字节对齐
  4.__packed对局部整形变量无影响
  5.强制由unpacked对象向packed对象转化是未定义,整形指针可以合法定
  义为packed。
  __packed int* p; //__packed int 则没有意义
  6.对齐或非对齐读写访问带来问题
  __packed struct STRUCT_TEST
  {
  char a;
  int b;
  char c;
  } ;    //定义如下结构此时b的起始地址一定是不对齐的
  //在栈中访问b可能有问题,因为栈上数据肯定是对齐访问[from CL]
  //将下面变量定义成全局静态不在栈上
  static char* p;
  static struct STRUCT_TEST a;
  void Main()
  {
  __packed int* q; //此时定义成__packed来修饰当前q指向为非对齐的数据地址下面的访问则可以
  p = (char*)&a;
  q = (int*)(p+1);
  *q = 0x87654321;
  /*
  得到赋值的汇编指令很清楚
  ldr      r5,0x20001590 ; = #0x12345678
  [0xe1a00005]   mov      r0,r5
  [0xeb0000b0]   bl       __rt_uwrite4 //在此处调用一个写4byte的操作函数
  [0xe5c10000]   strb     r0,[r1,#0]   //函数进行4次strb操作然后返回保证了数据正确的访问
  [0xe1a02420]   mov      r2,r0,lsr #8
  [0xe5c12001]   strb     r2,[r1,#1]
  [0xe1a02820]   mov      r2,r0,lsr #16
  [0xe5c12002]   strb     r2,[r1,#2]
  [0xe1a02c20]   mov      r2,r0,lsr #24
  [0xe5c12003]   strb     r2,[r1,#3]
  [0xe1a0f00e]   mov      pc,r14
  */
  /*
  如果q没有加__packed修饰则汇编出来指令是这样直接会导致奇地址处访问失败
  [0xe59f2018]   ldr      r2,0x20001594 ; = #0x87654321
  [0xe5812000]   str      r2,[r1,#0]
  */
  //这样可以很清楚的看到非对齐访问是如何产生错误的
  //以及如何消除非对齐访问带来问题
  //也可以看到非对齐访问和对齐访问的指令差异导致效率问题
  }
  九.ARM下的边界未对齐访问的后果
  from http://blog.csdn.net/alenwelkin/archive/2006/12/19/1448324.aspx
  自行编写了一个程序,定义一个char型数组如下:
  char p[] = {0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x80};
  分别用long型和short型指针去指向p+3的位置,这个地址在编译后是0x10963,明显是一个对long和short来说都不对齐的地址。
  程序输出结果如下,括号里为内容,前面是地址。
  /mnt $ ./test
  l 0x10963(0x30201040), s 0x10963(0x5040), lt 0x30201040, st 0x5040
  使用arm-linux-objdump工具反汇编可执行文件发现,对于short指针的引用,编译器做了特殊处理以保证其引用的正确性,而long指针没有做。
  unsigned long *l = (p + 3);
  83e0: e59f3084 ldr r3, [pc, #132] ; 846c <.text+0x190>
  83e4: e50b3010 str r3, [fp, #-16]
  unsigned short *s = (p + 3);
  83e8: e59f307c ldr r3, [pc, #124] ; 846c <.text+0x190>
  83ec: e50b3014 str r3, [fp, #-20]
  unsigned long lt = *l;
  83f0: e51b3010 ldr r3, [fp, #-16] //变量l->r3
  83f4: e5933000 ldr r3, [r3] //l内容->r3
  83f8: e50b3018 str r3, [fp, #-24] //r3->lt
  unsigned short st = *s;
  83fc: e51b3014 ldr r3, [fp, #-20] //变量s->r3
  8400: e5d32000 ldrb r2, [r3] //s内容(低位)->r2
  8404: e5d33001 ldrb r3, [r3, #1] //s内容(高位)->r3
  8408: e1823403 orr r3, r2, r3, lsl #8 //r3左移后与r2拼合成short值->r3
  840c: e54b301a strb r3, [fp, #-26] //r3中的short值(低位)->变量st低位
  8410: e1a03443 mov r3, r3, asr #8 //r3右移8位->r3
  8414: e54b3019 strb r3, [fp, #-25] //r3中的short值(高位)->变量st高位
  0x30201040是怎么来的?想了半天也不明白。
  查阅ARM相关书籍发现,如果协处理器CP15:c1:c0中的1位和22位均为0,则ARM指令ldr的返回值是memory(addr & ~3, 4) ROR ((addr & 3) * 8)。前半句的含义是对4边界向下取整,在本例中是0x10960,再取其内容是 0x40302010,后半句在本例中是循环右移24位。合起来看是0x40302010 ROR 24 = 0x30201040,与本例正好吻合!