结构体中的字节数

来源:互联网 发布:dota2玩家数据 编辑:程序博客网 时间:2024/04/30 11:03
C字节对齐与结构体大小
说明:


结构体的sizeof值,并不是简单的将其中各元素所占字节相加,而是要考虑到存 储空间的字节对齐问题。
这些问题在平时编程的时候也确实不怎么用到,但在一些笔试面试题目中出是常常出现,对sizeof我们将在另一篇文章中总结,
一、解释
    现代计算机中内存空间都是按照byte划分的,从理论上讲似乎对任何类型的变量的访问可以从任何地址开始,
    但实际情况是在访问特定类型变量的时候经常在特定的内存地址访问,这就需要各种类型数据按照一定的规则在空间上排列,而不是顺序的一个接一个的排放,这就是对齐。
    各个硬件平台对存储空间的处理上有很大的不同。一些平台对某些特定类型的数据只能从某些特定地址开始存取。
    比如有些架构的CPU在访问 一个没有进行对齐的变量的时候会发生错误,那么在这种架构下编程必须保证字节对齐.
    其他平台可能没有这种情况,但是最常见的是如果不按照适合其平台要求对 数据存放进行对齐,会在存取效率上带来损失。
    比如有些平台每次读都是从偶地址开始,如果一个int型(假设为32位系统)如果存放在偶地址开始的地方,那么一个读周期就可以读出这32bit,
    而如果存放在奇地址开始的地方,就需要2个读周期,并对两次读出的结果的高低字节进行拼凑才能得到该32bit数 据。


二、准则
    其实字节对齐的细节和具体编译器实现相关,但一般而言,满足三个准则:
    1. 结构体变量的首地址能够被其最宽基本类型成员的大小所整除;
    2. 结构体每个成员相对于结构体首地址的偏移量都是成员大小的整数倍,如有需要编译器会在成员之间加上填充字节;
    3. 结构体的总大小为结构体最宽基本类型成员大小的整数倍,如有需要编译器会在最末一个成员之后加上填充字节。


三、基本概念
    字节对齐:计算机存储系统中以Byte为单位存储数据,不同数据类型所占的空间不同,
    如:整型(int)数据占4个字节,字符型(char)数据占一个字 节,短整型(short)数据占两个字节,等等。
    计算机为了快速的读写数据,默认情况下将数据存放在某个地址的起始位置,如:整型数据(int)默认存储在地址能被4整除的起始位置,
    字符型数据(char)可以存放在任何地址位置(被1整除),短整型(short)数据存储在地址能被2整除的起始位置。这 就是默认字节对齐方式。
四、结构体长度求法


    1.成员都相同时(或含数组且数组数据类型同结构体其他成员数据类型):
    结构体长度=成员数据类型长度×成员个数(各成员长度之和);
    结构体中数组长度=数组数据类型长度×数组元素个数;


2.成员不同且不含其它结构体时;
    (1).分析各个成员长度;
    (2).找出最大长度的成员长度M(结构体的长度一定是该成员的整数倍);
    (3).并按最大成员长度出现的位置将结构体分为若干部分;
    (4).各个部分长度一次相加,求出大于该和的最小M的整数倍即为该部分长度
    (5).将各个部分长度相加之和即为结构体长度


3.含有其他结构体时:
    (1).分析各个成员长度;
    (2).对是结构体的成员,其长度按b来分析,且不会随着位置的变化而变化;
    (3).分析各个成员的长度(成员为结构体的分析其成员长度),求出最大值;
    (4).若长度最大成员在为结构体的成员中,则按结构体成员为分界点分界;
        其他成员中有最大长度的成员,则该成员为分界点;
        求出各段长度,求出大于该和的最小M的整数倍即为该部分长度
    (5).将各个部分长度相加之和即为结构体长度
五、空结构体
    
struct S5 { };    sizeof( S5 ); // 结果为1
    “空结构体”(不含数据成员)的大小不为0,而是1。试想一个“不占空间”的变量如何被取地址、两个不同的“空结构体”变量又如何得以区分呢于是,
    “空结构体”变量也得被存储,这样编译器也就只能为其分配一个字节的空间用于占位了。
六、有static的结构体
   
 struct S4    {    char a;    long b;    static long c; //静态    };
    静态变量存放在全局数据区内,而sizeof计算栈中分配的空间的大小,故不计算在内,S4的大小为4+4=8。
七、举例说明
1.举例1
    很显然默认对齐方式会浪费很多空间,例如如下结构:
    
struct student    {    char name[5];    int num;    short score;    }
    本来只用了11bytes(5+4+2)的空间,但是由于int型默认4字节对齐,存放在地址能被4整除的起始位置,
    即:如果name[5]从0开始存放, 它占5bytes,而num则从第8(偏移量)个字节开始存放。所以sizeof(student)=16。于是中间空出几个字节闲置着。
    但这样便于计算 机快速读写数据,是一种以空间换取时间的方式。其数据对齐如下图:
    |char|char|char|char|
    |char|----|----|----|
    |--------int--------|
    |--short--|----|----|
    如果我们将结构体中变量的顺序改变为:
  
  struct student    {    int num;    char name[5];    short score;    }
    则,num从0开始存放,而name从第4(偏移量)个字节开始存放,连续5个字节,score从第10(偏移量)开始存放,故sizeof(student)=12。其数据对齐如下图:
    |--------int--------|
    |char|char|char|char|
    |char|----|--short--|
    如果我们将结构体中变量的顺序再次改为为:
    
struct student    {    int num;    short score;    char name[5];    }
    则,sizeof(student)=12。其数据对齐如下图:
    |--------int--------|
    |--short--|char|char|
    |char|char|char|----|
2.举例2
(1)
   
 struct test1  { int a;   int b[4];  };    sizeof(test1)=sizeof(int)+4*sizeof(int)=4+4*4=20;
    (2)
  
  struct test2  { char a;   int b;   double c;   bool d;  };
    分析:该结构体最大长度double型,长度是8,因此结构体长度分两部分:
    第一部分是a、 b、 c的长度和,长度分别为1,4,8,则该部分长度和为13,取8的大于13的最小倍数为16;
    第二部分为d,长度为1,取大于1的8的最小倍数为8,
    两部分和为24,故sizeof(test2)=24;
    (3)
   
 struct test3    {    char a;    test2 bb;//见上题    int cc;    }
    分析:该结构体有三个成员,其中第二个bb是类型为test2的结构体,长度为24,且该结构体最大长度成员类型为double型,以后成员中没有double型,
    所以按bb分界为两部分:
    第一部分有a 、bb两部分,a长度为1,bb长度为24,取8的大于25的最小倍数32;
    第二部分有cc,长度为4,去8的大于4的最小倍数为8;
    两部分之和为40,故sizeof(test3)=40;
    (4)
    
struct test4    {    char a;    int b;    };    struct test5    { char c;    test4 d;    double e;    bool f;    };
求sizeof(test5)
    分析:test5明显含有结构体test4,按例2容易知道sizeof(test4)=8,且其成员最大长度为4;则结构体test5的最大成员长度为8(double 型),
    考试.大提示e是分界点,分test5为两部分:
    第一部分由c 、d、e组成,长度为1、8、8,故和为17,取8的大于17的最小倍数为24;
    第二部分由f组成,长度为1,取8的大于1的最小倍数为8,

    两部分和为32,故sizeof(test5)=24+8=32;



0 0
原创粉丝点击