字节对齐2

字节顺序是指占内存多于一个字节类型的数据在内存中的存放顺序,通常有小端、大端两种字节顺序。小端字节指低字节数据存放在内存低地址处,高字节数据存放在内存的高地址处;大端字节序是高字节数据存放在低地址处,低字节数据存放在高地址处。基于X86平台的PC机是小端字节序的,而有的嵌入式平台则是大端字节序的。因而对int、uint16、uint32等多于1字节类型的数据,在这些嵌入式平台上应该变换其存储顺序。通常我们认为,在网络中传输的字节的顺序即网络字节序为标准顺序,考虑到与协议的一致以及与同类其他平台产品的互通,在程序中发数据包时,将主机字节序转换为网络字节序,收数据包处将网络字节序转换为主机字节序。
    对于下面的结构体:

  1. struct test
  2. {
  3.     char x1;
  4.     short x2;
  5.     float x3;
  6.     char x4;
  7. };

    结构各成员空间分配情况是怎么样的?
    文章中解释:
    结构的第一个成员x1,其偏移地址为0,占据了第1个字节。第二个成员x2为short类型,其起始地址必须2字节对界,因此,编译器在x2和x1之间填充了空字节。结构的第三个成员x3和第四个成员x4恰好落在了其自然对界地址上,在它们前面不需要额外的填充字节。在test结构体中,成员x3要求4字节对齐,是该结构所有成员中要求的最大对界单元,因而test结构的自然对界条件为4字节,编译器在成员x4后面填充了 3个空字节。整个结构所占据空间为12字节。
    同时,说明了更改C编译器的缺省字节对齐方式:在缺省情况下,C编译器为每一个变量或是数据单元按其自然对界条件分配空间。一般地,可以通过下面的方法来改变缺省的对界条件:
 · 使用伪指令#pragma pack (n),C编译器将按照n个字节对齐。
    · 使用伪指令#pragma pack (),取消自定义字节对齐方式。
    另外,还有如下的一种方式:
    · __attribute((aligned (n))),让所作用的结构成员对齐在n字节自然边界上。如果结构中有成员的长度大于n,则按照最大成员的长度来对齐。
    · __attribute__ ((packed)),取消结构在编译过程中的优化对齐,按照实际占用字节数进行对齐。
    以上的n = 1, 2, 4, 8, 16... 第一种方式较为常见。

    下面是CSDN上提出的问题(原文<<intel和微软和本公司同时出现的面试题>>:http://community.csdn.net/Expert/TopicView3.asp?id=3804035)

  1. #pragma pack(8)
  2. struct s1
  3. {
  4.     short a;
  5.     long b;
  6. };
  7. struct s2
  8. {
  9.     char c;
  10.     s1 d;
  11.     long long e;
  12. };
  13. #pragma pack()

    问:
    (1)sizeof(s2) = ?
    (2)s2的c后面空了几个字节接着是d?
    
    下面是redleaves给出解释:
    很详尽,很透彻,从论坛原文后头的对话可以看出redleaves对C/C++的标准理解很深刻,值得学习:

  1. #pragma pack(8)
  2. struct S1
  3. {
  4. char a;
  5. long b;
  6. };
  7. struct S2 {
  8. char c;
  9. struct S1 d;
  10. long long e;
  11. };
  12. #pragma pack()

    sizeof(s2)结果为24。
    成员对齐有一个重要的条件,即每个成员分别对齐,即每个成员按自己的方式对齐。
    也就是说上面虽然指定了按8字节对齐,但并不是所有的成员都是以8字节对齐。其对齐的规则是:每个成员按其类型的对齐参数(通常是这个类型的大小)和指定对
齐参数(这里是8字节)中较小的一个对齐。并且结构的长度必须为所用过的所有对齐参数的整数倍,不够就补空字节。
    s1中,成员a是1字节,默认按1字节对齐,指定对齐参数为8,这两个值中取1,a按1字节对齐;成员b是4个字节,默认是按4个字节对齐,这时就按4个字节对齐。所以sizeof(s1) = 8。
    s2中,c和s1中的a一样,按1字节对齐,而d是个结构,它是8个字节,它按什么对齐呢?对于结构来说,它的默认对齐方式就是它的所有成员使用的对齐参数中最大的一个,s1的就是4。所以,成员d就是按4字节对齐,成员e是8个字节对齐,它是默认按8个字节对齐,和指定的一样,所以它对到8字节的边界。这时,已经使用了12个字节了,所以又添了4个字节的空,从第16个字节开始放置成员e。这是,长度为24,已经可以按8(成员e按8字节对齐)整除。这样,一共使用了24个字节。
    a b
    s1的内存布局:11**,1111,
    c s1.a s1.b d
    s2的内存布局:1***,11**,1111,****11111111

    这里有三点很重要:
    (1)每个成员分别按自己的方式对齐,并能最小化长度;
    (2)复杂类型(如结构)的默认对齐方式是它最长的成员的对齐方式,这样在成员是复杂类型时,可以最小化长度;
    (3)对齐后的长度必须是成员中最大的对齐参数的整数倍,这样在处理数组时可以保证每一项都边界对齐;

    对于数组,比如:
    char a[3];这种,它的对齐方式和分别写3个char是一样的.也就是说它还是按1个字节对齐。
    如果写:typedef char Array3[3];
    Array3这种类型的对齐方式还是按1个字节对齐,而不是按它的长度。
    不论类型是什么,对齐的边界一定是1,2,4,8,16,32,64....中的一个。

    下面是从另外一篇文章(http://community.csdn.net/Expert/TopicView3.asp?id=3564500)的一些对各个类型以及结构体对齐方式的补充:
    为了能使CPU对变量进行高效快速的访问,变量的起始地址应该具有某些特性,即所谓的”对齐“。例如对于4字节的int类型变量,其起始地址应该位于4字节边界上,即起始地址能够被4整除。变量的对齐规则如下(32位系统):
    Type
    Alignment
    char
    在字节边界上对齐
    short (16-bit)
    在双字节边界上对齐
    int and long (32-bit)
    在4字节边界上对齐
    float
    在4字节边界上对齐
    double
    在8字节边界上对齐
    structures
    单独考虑结构体的每个成员,它们在不同的字节边界上对齐。
    其中最大的字节边界数就是该结构的字节边界数。
    
    如果结构体中有结构体,那么这是一个递归的过程。   
    设编译器设定的最大对齐字节边界数为n,对于结构体中的某一成员item,它相对于结构首地址的实际字节对齐数目x应该满足以下规则:
    x = min(n, sizeof(item)
    例如,对于结构体:

  1. struct 
  2. {
  3.    char a; 
  4.    long b;
  5. }T;

    当位于32位系统,n = 8时:
    a的偏移量为0,b的偏移量为4,中间填充了3个字节,b的x为4。

    当位于32位系统,n = 2时:
    a的偏移量为0,b的偏移为2,中间填充了1个字节,b的x为2。

    结构体的sizeof:
    设结构体的最后一个成员为LastItem,其相对于结构体首地址的偏移为offset(LastItem),其大小为sizeof(LastItem),结构体的字节对齐数为N,则:结构体的sizeof 为: 若offset(LastItem)+ sizeof(LastItem)能够被N整除,那么就是offset(LastItem)+ sizeof(LastItem),否则,在后面填充,直到能够被N整除。
    另外:
    1) 对于空结构体,sizeof == 1;因为必须保证结构体的每一个实例在内存中都有独一无二的地址.
    2)结构体的静态成员不对结构体的大小产生影响,因为静态变量的存储位置与结构体的实例地址无关。例如:
    struct {static int I;} T;
    struct {char a; static int I;} T1;
    sizeof(T) == 1; sizeof(T1) == 1;




    引用:blog.csdn.net/xuegao007/article/details/1708360

posted @ 2016-10-27 01:54  遥望星空  阅读(271)  评论(0编辑  收藏  举报