C语言学习笔记--字节对齐问题

    结构体中字节对齐问题
    为了提高CPU访问内存的效率,可能CPU在读取数据的时候回一次性读取4字节、或者2字节、8字节等大小的数据,所有编译器在把数据存放于内存的时候,会自动对齐。
    1、字节对齐规则:
    字节对齐有以下几种规律:
    1.1以最大的成员占据的空间大小对齐
        typedef struct
        {
            char a;   /*1*/
            char b;   /*1  b后面会有两个空字节*/
            int c;    /*4*/
        }test_1;
        4字节对齐,占据8字节,内存分布为:
        byte0    byte1    byte2    byte3    
        char    char    空        空
        int 
    1.2剩余的空间足够,后面的变量会往前填补
        typedef struct
        {
            int i;   /*4*/
            char a;  /*1*/
            char b;  /*1*/
            short d; /*2*/
        }test_2;
        4字节对齐,占据8字节,内存分布为:
        byte0    byte1    byte2    byte3
        int
        char    char        short
    1.3根据变量占据的空间从大到小,依次对齐
        typedef struct
        {
            double d;   /*8*/
            short s1;   /*2+2*/
            int i;      /*4*/
            short s2;   /*2+6*/
        }test_3;
        8字节对齐,占据24字节,内存分布为:
        byte0    byte1    byte2    byte3    byte4    byte5    byte6    byte7
                                double 
        short                            int
                                short        
    1.4包含其他结构体时,不会拿该结构体的总大小对齐
        typedef struct
        {
            int a;      /*4*/
            char b;     /*1*/
            char c ;    /*1*/
            short d;    /*2*/
            int e;      /*4*/
        }test_4;
        typedef struct
        {
            int aa;    /*4*/
            short bb;  /*2+2*/
            test_4 mm; /*12*/
        }test_5;
        结构体test_4总共占据12个字节,但在计算结构体test_5类型的大小时,并不会把m为整体看出一个整体的大小来计算对齐字节,而是拆开test_4结构体,依次拿test_4里面的成员存放于内存中;
    #include<stdio.h>

    typedef struct
    {
        char a;   /*1*/
        char b;   /*1  b后面会有两个空字节*/
        int c;    /*4*/
    }test_1;

    typedef struct
    {
        int i;   /*4*/
        char a;  /*1*/
        char b;  /*1*/
        short d; /*2*/
    }test_2;

    typedef struct
    {
        double d;   /*8*/
        short s1;   /*2+2*/
        int i;      /*4*/
        short s2;   /*2+6*/
    }test_3;

    typedef struct
    {
        int a;      /*4*/
        char b;     /*1*/
        char c ;    /*1*/
        short d;    /*2*/
        int e;      /*4*/
    }test_4;
    typedef struct
    {
        int aa;    /*4*/
        short bb;  /*2+2*/
        test_4 mm; /*12*/
    }test_5;
    int
    main(void)
    {
        printf("%d ",sizeof(test_1));/*程序输出结果:8*/
        printf("%d ",sizeof(test_2));/*程序输出结果:8*/
        printf("%d ",sizeof(test_3));/*程序输出结果:24*/
        printf("%d ",sizeof(test_4));/*程序输出结果:12*/
        printf("%d ",sizeof(test_5));/*程序输出结果:20*/

        return 0;
    }
    /*程序输出结果:8 8 24 12 20*/    
    由于字节对齐特性的存在,可能会使得我们设计的结构体占据的空间会变大。这在某些场合是应该要避免的,如网络通讯,如无避免则会产生流量损失。
所以合理设计结构体,即合理排放其成员变量十分重要。
    2. 去除编译器的字节对齐规则
    解决字节对齐带来的空间损耗问题,一种方法就是不要使用字节对齐规则,即使其1字节对齐:
    #include<stdio.h>
    #pragma pack(1)
    typedef struct
    {
        char a;
        char b;
        int c;
    }test_1;

    typedef struct
    {
        int i;
        char a;
        char b;
        short d;
    }test_2;

    typedef struct
    {
        double d;
        short s1;
        int i;
        short s2;
    }test_3;

    typedef struct
    {
        int a;
        char b;
        char c ;
        short d;
        int e;
    }test_4;
    typedef struct
    {
        int aa;    /*4*/
        short bb;  /*2+2*/
        test_4 mm; /*12*/
    }test_5;
    #pragma pack()

    int
    main(void)
    {
        printf("%d ",sizeof(test_1));
        printf("%d ",sizeof(test_2));
        printf("%d ",sizeof(test_3));
        printf("%d ",sizeof(test_4));
        printf("%d ",sizeof(test_5));

        return 0;
    }
    /*程序输出结果:6 8 16 12 18 */
    3. 结构体中的位域
    位域只能是在结构体中使用,其作用是为了节省空间。 
    比如我们要在结构体中定义一个变量来表示小学生的年龄(一般是6到14岁),显然,用char类型来定义变量都大了,所以可以借助位域的功能来限制char的位数。
14(岁)转为二进制是0b1110,共占据5位,再加上符号位共5位,所以在描述一个学生信息的结构体中,其年龄可以声明为:

    typedef struct _stu 
    {
        char age : 4;
        char* name;
    }stu;
    这样,当age等于二进制位超过4位的数值时,编译会产生溢出警告。限定了结构体成员的位域,那么其占据的空间也会随之改变:
    typedef struct _stu
    {
        char age : 4;
        char a : 2;
        char b : 2;
    }stu;  //4bit + 2bit + 2bit = 8bit = 1BYTE
    如上占据了1字节。当然,如果在最后再定义一个char v : 2;,那么其大小自然就是2字节了。虽然说限制位域确实节省了空间,但是它节省的是位级别的空间,十分小,如果连位都要考虑浪费问题,那就有点变态了。
        #include<stdio.h>
        char*      pointer1;
        short int* pointer2;
        int*       pointer3;
        double*    pointer4;
        float*     pointer5;

        typedef struct
        {
            char age : 4;
            char a : 2;
            char b : 2;
        }stu1;  //4bit + 2bit + 2bit = 8bit = 1BYTE

        typedef struct
        {
            char age : 4;
            char a : 2;
            char b : 2;
            char v : 2;
        }stu2;  //4bit + 2bit + 2bit = 8bit = 1BYTE

        typedef struct
        {
            char age : 4;
            char type;
            char * name;
        }stu3;

        int
        main(void)
        {

            printf("%d ",sizeof(pointer1));
            printf("%d ",sizeof(pointer2));
            printf("%d ",sizeof(pointer3));
            printf("%d ",sizeof(pointer4));
            printf("%d ",sizeof(pointer5));
            printf("\n");

            printf("%d ",sizeof(stu1));
            printf("%d ",sizeof(stu2));
            printf("%d ",sizeof(stu3));

            return 0;
        }
        /*   
        程序输出结果:
        4 4 4 4 4
        1 2 8

        所以不管是什么类型的指针变量都只占4个字节

        */

猜你喜欢

转载自blog.csdn.net/tyustli/article/details/84583786
今日推荐