美文网首页
字节对齐与大端小端与内存区域划分

字节对齐与大端小端与内存区域划分

作者: 杰米 | 来源:发表于2016-10-18 02:38 被阅读310次

    字节对齐

    C语言字节对齐
    C语言字节对齐/7213465

    一、原则:
    
    1.结构体内成员按自身按自身长度自对齐。
    
    自身长度,如char=1,short=2,int=4,double=8,。所谓自对齐,指的是该成员的起始位置的内存地址必须是它自身长度的整数倍。如int只能以0,4,8这类的地址开始
    
    
    2.结构体的总大小为结构体的有效对齐值的整数倍
    
    结构体的有效对齐值的确定:
    
    1)当未明确指定时,以结构体中最长的成员的长度为其有效值
    
    2)当用#pragma pack(n)指定时,以n和结构体中最长的成员的长度中较小者为其值。
    
    3)当用__attribute__ ((__packed__))指定长度时,强制按照此值为结构体的有效对齐值
    
    二、例子
    
    1。
    
    struct AA{
    
       char a;
    
       int b;
    
       char c; 
    
    }aa
    
    结果,sizeof(aa)=12
    
    何解?首先假设结构体内存起始地址为0,那么地址的分布如下
    
    0  a
    
    1  
    
    2
    
    3
    
    4  b
    
    5  b
    
    6  b
    
    7  b
    
    8  c
    
    9
    
    10
    
    11
    
    char的字对齐长度为1,所以可以在任何地址开始,但是,int自对齐长度为4,必须以4的倍数地址开始。所以,尽管1-3空着,但b也只能从4开始。再加上c后,整个结构体的总长度为9,结构体的有效对齐值为其中最大的成员即int的长度4,所以,结构体的大小向上扩展到12,即9-11的地址空着。
    
    
    2.
    
    
    struct AA{
       char a;
    char c; 
       int b;    
    }aa
    sizeof(aa)=8,为什么呢
    0  a
    1  c
    2
    3
    4  b
    5  b
    6  b
    7  b
    因为c为char类型,字对齐长度为1,所以可以有效的利用1-3间的空格。看见了吧,变量定义的位置的不同时有可能影响结构体的大小的哦!
    
    3.
    #pragma pack(2)
    struct AA{
       char a;
       int b;
       char c; 
    }aa
    sizeof(aa)=10,
    为什么呢?a到c只占9字节长度,因为结构体的有效对齐长度在pack指定的2和int的4中取
    较小的值2。故取2的倍数10。
    如果当pack指定为8呢?那就仍然按4来对齐,结果仍然是12。
    
    
    4.
    struct AA{
       char a;
       int b;
       char c; 
    }__attribute__((__8__))aa
    sizeof(aa)=16,)
    为咩?其实a到c仍然只占9字节长度,但结构体以8对齐,故取8的倍数16.
    如果其指定2,则结果为10
    
    如果pragma pack和__attribute__
    同时指定呢?以__attribute__ 的为准。
    需要说明的是,不管pragma
    pack和__attribute__如何指定,结构体内部成员的自对齐仍然按照其自身的对齐值。
    

    大端小端

    字节序(大小端)详解从高低地址和高低位开始理解(转)

    字节序(大小端)详解从高低地址和高低位开始理解(转)  
    
    
    一、字节序定义
    字节序,顾名思义字节的顺序,再多说两句就是大于一个字节类型的数据在内存中的存放顺序(一个字节的数据当然就无需谈顺序的问题了)。
    其实大部分人在实际的开发中都很少会直接和字节序打交道。唯有在跨平台以及网络程序中字节序才是一个应该被考虑的问题。
    在所有的介绍字节序的文章中都会提到字节序分为两类:Big-Endian和Little-Endian。引用标准的Big-Endian和Little-Endian的定义如下:
    a) Little-Endian就是低位字节排放在内存的低地址端,高位字节排放在内存的高地址端。
    b) Big-Endian就是高位字节排放在内存的低地址端,低位字节排放在内存的高地址端。
    c) 网络字节序:4个字节的32 bit值以下面的次序传输:首先是0~7bit,其次8~15bit,然后16~23bit,最后是24~31bit。这种传输次序称作大端字节序。由于 TCP/IP首部中所有的二进制整数在网络中传输时都要求以这种次序,因此它又称作网络字节序。比如,以太网头部中2字节的“以太网帧类型”,表示后面数据的类型。对于ARP请求或应答的以太网帧类型来说,在网络传输时,发送的顺序是0x08,0x06。在内存中的映象如下图所示:
    栈底 (高地址)
    ---------------
    0x06 -- 低位 
    0x08 -- 高位
    ---------------
    栈顶 (低地址)
    该字段的值为0x0806。按照大端方式存放在内存中。
    二、高/低地址与高低字节
    首先我们要知道我们C程序映像中内存的空间布局情况:在《C专家编程》中或者《Unix环境高级编程》中有关于内存空间布局情况的说明,大致如下图:
    ----------------------- 最高内存地址 0xffffffff
     | 栈底
     .
     .              栈
     .
      栈顶
    -----------------------
     |
     |
    \|/
    NULL (空洞)
    /|\
     |
     |
    -----------------------
                    堆
    -----------------------
    未初始化的数据
    ----------------(统称数据段)
    初始化的数据
    -----------------------
    正文段(代码段)
    ----------------------- 最低内存地址 0x00000000
    以上图为例如果我们在栈上分配一个unsigned char buf[4],那么这个数组变量在栈上是如何布局的呢[注1]?看下图:
    栈底 (高地址)
    ----------
    buf[3]
    buf[2]
    buf[1]
    buf[0]
    ----------
    栈顶 (低地址)
    现在我们弄清了高低地址,接着来弄清高/低字节,如果我们有一个32位无符号整型0x12345678(呵呵,恰好是把上面的那4个字节buf看成一个整型),那么高位是什么,低位又是什么呢?其实很简单。在十进制中我们都说靠左边的是高位,靠右边的是低位,在其他进制也是如此。就拿 0x12345678来说,从高位到低位的字节依次是0x12、0x34、0x56和0x78。
    高低地址和高低字节都弄清了。我们再来回顾一下Big-Endian和Little-Endian的定义,并用图示说明两种字节序:
    以unsigned int value = 0x12345678为例,分别看看在两种字节序下其存储情况,我们可以用unsigned char buf[4]来表示value:
    Big-Endian: 低地址存放高位,如下图:
    栈底 (高地址)
    ---------------
    buf[3] (0x78) -- 低位
    buf[2] (0x56)
    buf[1] (0x34)
    buf[0] (0x12) -- 高位
    ---------------
    栈顶 (低地址)
    Little-Endian: 低地址存放低位,如下图:
    栈底 (高地址)
    ---------------
    buf[3] (0x12) -- 高位
    buf[2] (0x34)
    buf[1] (0x56)
    buf[0] (0x78) -- 低位
    ---------------
    栈顶 (低地址)
    在现有的平台上Intel的X86采用的是Little-Endian,而像Sun的SPARC采用的就是Big-Endian。
    三、例子
    嵌入式系统开发者应该对Little-endian和Big-endian模式非常了解。采用Little-endian模式的CPU对操作数的存放方式是从低字节到高字节,而Big-endian模式对操作数的存放方式是从高字节到低字节。
    例如,16bit宽的数0x1234在Little-endian模式CPU内存中的存放方式(假设从地址0x4000开始存放)为:
    内存地址  存放内容
     0x4001    0x12
     0x4000    0x34
    而在Big-endian模式CPU内存中的存放方式则为:
    内存地址  存放内容
     0x4001    0x34
     0x4000    0x12
     
    32bit宽的数0x12345678在Little-endian模式CPU内存中的存放方式(假设从地址0x4000开始存放)为:
    内存地址  存放内容
     0x4003     0x12
     0x4002     0x34
     0x4001     0x56
     0x4000     0x78
     
    而在Big-endian模式CPU内存中的存放方式则为:
    内存地址  存放内容
     0x4003     0x78
     0x4002     0x56
     0x4001     0x34
     0x4000     0x12
    

    整合字节对齐+大端小端+程序内存数据区域划分

    整合字节对齐+大端小端+程序数据区域划分

    内存分布

    ------高地址------


    • 栈又称堆栈, 是用户存放程序临时创建的局部变量,也就是说我们函数括弧“{}”中定义的变量(但不包括static声明的变量,static意味着在数据段中存放变量)。除此以外,在函数被调用时,其参数也会被压入发起调用的进程栈中,并且待到调用结束后,函数的返回值也会被存放回栈中。由于栈的先进先出特点,所以栈特别方便用来保存/恢复调用现场。从这个意义上讲,我们可以把堆栈看成一个寄存、交换临时数据的内存区。

    • 堆是用于存放进程运行中被动态分配的内存段,它的大小并不固定,可动态扩张或缩减。当进程调用malloc等函数分配内存时,新分配的内存就被动态添加到堆上(堆被扩张);当利用free等函数释放内存时,被释放的内存从堆中被剔除(堆被缩减)
    • BBS
      BSS段(bss segment)通常是指用来存放程序中未初始化,或初始化为0的全局变量,静态局部变量的一块内存区域。BSS是英文Block Started by Symbol的简称。BSS段属于静态内存分配。
    • 数据
      数据段(data segment)通常是指用来存放程序中已初始化为非0的全局变量的一块内存区域。数据段属于静态内存分配。
    • 代码段(code segment/text segment)通常是指用来存放程序执行代码的一块内存区域。这部分区域的大小在程序运行前就已经确定,并且内存区域通常属于只读, 某些架构也允许代码段为可写,即允许修改程序。在代码段中,也有可能包含一些只读的常数变量,例如字符串常量等。

    ------低地址------

    相关文章

      网友评论

          本文标题:字节对齐与大端小端与内存区域划分

          本文链接:https://www.haomeiwen.com/subject/bfizyttx.html