精华内容
下载资源
问答
  • C语言结构体字节对齐规则

    千次阅读 2019-04-12 10:55:40
    C语言结构体字节对齐规则 基本规则 规则1 :结构体(struct)的数据成员,第一个数据成员放在offset为0的地方,以后每个数据成员存放在offset为该数据成员大小的整数倍的地方(比如int在32位机为4字节,则要从4的...

    C语言结构体字节对齐规则

    基本规则

    规则1 :结构体(struct)的数据成员,第一个数据成员放在offset为0的地方,以后每个数据成员存放在offset为该数据成员大小的整数倍的地方(比如int在32位机为4字节,则要从4的整数倍地址开始存储)。

    规则2:如果一个结构体B里嵌套另一个结构体A,则结构体A应从offset为A内部最大成员的整数倍的地方开始存储。(struct B里存有struct A,A里有char,int,double等成员,那A应该从8的整数倍开始存储。),结构体A中的成员的对齐规则仍满足原则1、原则2。

    Tips:

    • 结构体A所占的大小为该结构体成员内部最大元素的整数倍,不足补齐。
    • 不是直接将结构体A的成员直接移动到结构体B中

    规则3:结构体的总大小,也就是sizeof的结果,必须是其内部最大成员的整数倍,不足的要补齐。

    案例解析

    例1

    typedef struct A{
        int a;
        short b;
    }A;
    /** 
      * 内存对齐规则:
      * 按最长的类型的长度为最长长度
      * 4*0 = 0 所以从 0 位置开始放
      * 2*0 = 0 0位置已经有内容
      * 2*1 = 2 2位置已经有内容
      * 2*2 = 4 4位置空,可以存放
      * short长度 为2 而基准长度为4,因此 需要将剩余的两字节补空
      * 故 A大小为8字节
      * a a a a
      * b b - - 
      */ 
     printf("%d\n",sizeof(A));//8
    

    例2

    typedef struct B{
       double a;
       short b;
       int c;
    }B;
    /** 
      * 最长基准长度为8
      * 8*0 = 0 放于0位置起的8个字节
      * 2*0 = 0 此处有内容
      * ...
      * 2*4 = 8 此处空闲可以存放 8-10用于存放 short
      * 4*0 = 0 此处已经有内容
      * ...
      * 4*3 = 12 此处可以存放 12-15
      * a a a a a a a a
      * b b - - c c c c
      * 所以B共占用 16字节
      */
     printf("%d\n",sizeof(B)); 
    

    例3

    typedef struct C{
      short a;
      double b;
      int c;
    }C;
    /** 
     * 最长基准长度为:8
     * 2*0 = 0 0-1位置用于存放a
     * 8*0 = 0 此处已有内容
     * 8*1 = 8 8-16位置用于存放b
     * 4*0 = 0 此处已有内容
     * 4*3 = 12 12-16存放c
     * a a - - - - - -
     * b b b b b b b b
     * c c c c - - - -
     */
    printf("%d\n",sizeof(C)); //24
    

    例4

    typedef struct D{
      int a;
      double b;
    }D;
    typedef struct E{
      short a;
      int b;
      D c;
    }E;
    /** 
       * 当结构体内部嵌套结构体时,以两个结构体内部最长的类型为基准长度
       * 此处结构体D与E最长的长度为D中的double,因此基准长度为:8
       * 2 * 0 = 0 0-1位置存放E.a
       * 4 * 0 = 0 
       * 4 * 1 = 4 4-7位置存放E.b
       * 8 + 4 * 0 = 8 8-11位置存放D.a
       * 8 + 8 * 0 = 8
       * 8 + 8 * 1 = 16 16-23位置存放D.b
       */
      printf("%d\n",sizeof(E)); //24
    
    展开全文
  • C语言结构体字节对齐

    2021-06-11 00:10:36
    默认字节对齐C语言结构体字节对齐是老生常谈的问题了,也是高频面试题,现在我们来深入研究这个问题,彻底弄懂到底是怎么回事,给你一个结构体定义和平台机器位数就能手动计算出结构体占用字节数,...

      默认字节对齐

    C语言结构体字节对齐是老生常谈的问题了,也是高频面试题,现在我们来深入研究这个问题,彻底弄懂到底是怎么回事,给你一个结构体定义和平台机器位数就能手动计算出结构体占用字节数,现在我们不使用宏#pragma pack,采用默认字节对齐方式。

    先抛出结论:

    • 在一个结构体中第一个成员变量放在偏移为0的位置,以后的变量都存储在该变量占用字节数整数倍的地址上。

    • 结构体总大小,必须是内部最大成员变量的整数倍,不足的补齐。

    好了,现在我们直接写个小程序验证并分析是否真是这样一回事。

    struct st{
        short a1;
        short a2;
        short a3;
    };
    
    struct st2{
        long a1;
        short a2;
    };
    

    这里我们定义了两个很简单的结构体,short占用2个字节,struct st我们一眼就知道大小了6个字节,但是struct st2呢?笔者电脑是64位,那么long占用8个字节,short占用2个字节。我们先来按照结论进行分析,在struct st2中成员变量a1在偏移0处存储且占用8个字节,成员变量a2占用2个字节,由于8是2的倍数,所以a2在偏移8的位置存储,又因为有结论2,我们根据结论2可以得出,struct st2必须占用8的倍数大小,所以struct st2总大小是16个字节,不足的后面补齐。现在我分别打印出struct st1和struct st2占用字节数大小和struct st2各个成员变量地址,观察是否和分析的一样。

    int main() {
        struct st2 st_val2;
    
        printf("sizeof(long) = %d\n", sizeof(long));
        printf("sizeof(struct st) = %d\n", sizeof(struct st));
        printf("sizeof(struct st2) = %d\n", sizeof(struct st2));
        printf("st_val2 addr = %p\n", &st_val2);
        printf("st_val2 a1 addr = %p\n", &st_val2.a1);
        printf("st_val2 a2 addr = %p\n", &st_val2.a2);
    
        return 0;
    }
    

    编译运行输出:

    sizeof(long) = 8
    sizeof(struct st) = 6
    sizeof(struct st2) = 16
    st_val2 addr = 0x7ffee107f3b8
    st_val2 a1 addr = 0x7ffee107f3b8
    st_val2 a2 addr = 0x7ffee107f3c0
    

    现在我们看一下输出结果,struct st如我们所愿占用6个字节大小,struct st2也按照我们分析的一样占用16个字节。我们在程序中定义了一个struct st2类型变量st_val2,从输出中可以看出变量st_val2的a1成员变量和st_val2变量地址一样,成员变量a2在偏移8处存储(0x c0 = 0xb8 8)。一切如我们所愿,看起来好像挺简单的,我们知道C语言有丰富的数据类型,下面我们再定义一个更复杂的结构体。

    struct st3{
        int a1;
        char a2;
        short a3;
        long a4;
        char a5;
    };
    

    这个结构体包含了大量数据类型成员变量,再复杂的结构体也能按照我们的结论分析到底占用了几个字节。

    在struct st3中int型成员变量a1占用4个字节,在偏移0处存储,char型成员变量a2占用2个字节那么应该放在2的倍数地址处存储,a1已经占用了4个字节,所以a2应该在偏移4的地址存储。
    short型成员变量a3占用2个字节,也应该放在2的倍数地址处存储,所以a3在偏移6的地址处存储,a2后面填充1个字节。

    long型成员变量a4占用8个字节,应该放在8的倍数地址上存储,前面我们已经知道a3在偏移6的地址处存储,且占用2个字节8 = 6 2,所以a4应该在偏移8的地址处存储。

    最后一个char型成员变量a5占用一个字节,那么a5在偏移16地址处存储。

    现在我们计算一下struct st3结构体占用空间大小,从a5偏移出计算16 1 = 17。在struct st3中最大成员变量占用8个字节,所以结构体总大小应该是8的倍数,最后结构体总大小是17 7 = 24,这里的7个字节在最后补齐。

    我们依旧写一个小程序输出struct st3类型变量各个成员变量地址和结构体总大小。

    int main() {
        struct st3 st_val3;
        printf("sizeof(struct st3) = %d\n", sizeof(struct st3));
        printf("st_val3 addr = %p\n", &st_val3);
        printf("st_val3.a1 addr = %p\n", &st_val3.a1);
        printf("st_val3.a2 addr = %p\n", &st_val3.a2);
        printf("st_val3.a3 addr = %p\n", &st_val3.a3);
        printf("st_val3.a4 addr = %p\n", &st_val3.a4);
        printf("st_val3.a5 addr = %p\n", &st_val3.a5);
    
        return 0;
    }
    

    编译运行输出:

    sizeof(struct st3) = 24
    st_val3 addr = 0x7ffeed0c33b0
    st_val3.a1 addr = 0x7ffeed0c33b0
    st_val3.a2 addr = 0x7ffeed0c33b4
    st_val3.a3 addr = 0x7ffeed0c33b6
    st_val3.a4 addr = 0x7ffeed0c33b8
    st_val3.a5 addr = 0x7ffeed0c33c0
    

    从输出我们可以看出,和我们分析的完全一样。

    枚举类型变量和联合体类型变量都可以作为结构体的成员变量,在分析这些结构体占用大小时,分析方法和我们上面的一模一样,只需要把内部任何一种数据类型变量当做一个普通变量看待即可,但是结构体类型成员变量有点不一样,它不适用于结论2,我们举个例子。

    struct st4{
        char a1[3];
        int a2;
        long a3;
        struct st3 a4;
    };
    

    在struct st4中我们定义了一个struct st3类型成员变量,前面我们已经分析过了struct st3占用24个字节。成员变量a1占用3个字节,成员变量a2占用4个字节,所以a2存储在偏移4的地址上,在a1后面填充一个字节。成员变量a3占用8个字节,则a3存储在偏移8的地址上。那么结构体总共占用字节数大小是:8   8 24 = 40。

    最后我们写一个程序验证一下是否如此。

    int main() {
        struct st4 st_val4;
        printf("sizeof(struct st4) = %d\n", sizeof(struct st4));
        printf("st4 addr = %p\n", &st_val4);
        printf("st_val4.a1 addr = %p\n", &st_val4.a1);
        printf("st_val4.a2 addr = %p\n", &st_val4.a2);
        printf("st_val4.a3 addr = %p\n", &st_val4.a3);
        printf("st_val4.a4 addr = %p\n", &st_val4.a4);
    
        return 0;
    }
    

    编译运行输出:

    sizeof(struct st4) = 40
    st4 addr = 0x7ffeec1263a0
    st_val4.a1 addr = 0x7ffeec1263a0
    st_val4.a2 addr = 0x7ffeec1263a4
    st_val4.a3 addr = 0x7ffeec1263a8
    st_val4.a4 addr = 0x7ffeec1263b0
    

    和我们分析的一模一样。

    声明:

    本文于网络整理,版权归原作者所有,如来源信息有误或侵犯权益,请联系我们删除或授权事宜。

    展开全文
  • C语言结构体字节对齐与位域结构体字节对齐一个例子用pragma pack()宏定义之后的结构体示例pragma pack()结构体大小计算法一般的结构体一般结构体的大小计算写出最小的结构体技巧位域 结构体字节对齐 一个例子 #...

    结构体字节对齐

    一个例子

    #include<stdio.h>
    struct stu1
    {
    	int a;//4字节
    	char b;//1字节
    	float c;//4字节
    };//12字节
    int main()
    {
    
    	printf("int类型的字节数为:%d\n", sizeof(int));
    	printf("char类型的字节数为:%d\n", sizeof(char));
    	printf("float类型的字节数为:%d\n", sizeof(float));
    	printf("结构体stu1类型的字节数为:%d\n", sizeof(struct stu1));
    	return 0;
    }
    

    在这里插入图片描述
    可以看到,结构体大小并不是简单的字节相加。那么,
    结构体为什么不设置成字节简单相加而去为难我们呢?
    解:为了提高寻址速度
    我们可以试想结构体在调用成员的情况。比如调用成员c。它先找到我们定义的一大块内存结构体s的首地址,float的大小是四个字节,计算机找到第一个成员的首地址,它是int占四个字节。可以每次都一个字节一个字节的寻址如

    用pragma pack()宏定义之后的结构体

    示例

    #include<stdio.h>
    #pragma pack(1)//定义寻址长度(本人的说法,有风险)
    struct stu1
    {
    	int a;//4字节
    	char b;//1字节
    	short s;//2字节
    	float c;//4字节
    	double d;//8字节
    };//19字节
    int main()
    {
    	printf("int类型的字节数为:%d\n", sizeof(int));
    	printf("char类型的字节数为:%d\n", sizeof(char));
    	printf("short类型的字节数为:%d\n", sizeof(short));
    	printf("float类型的字节数为:%d\n", sizeof(float));
    	printf("double类型的字节数为:%d\n", sizeof(double));
    	printf("结构体stu1类型的字节数为:%d\n", sizeof(struct stu1));
    	return 0;
    }
    

    在这里插入图片描述
    此处用到#pragma pack()宏定义。笔者将其理解为定义寻址长度。[^1]

    注解如下[^1]
    1.括号中的数字决定其寻址长度。(1的话就一个字节一个字节的寻,就成了一个变量一个变量的存。所以长度就是变量长度和)
    2.括号内的值只能从已存在的类型中(如1,2,4,8)选取且不超过结构体中最大的成员。其余无意义。(如:若括号内填了3,5,7,相当没填,对结构体大小不产生影响)
    3.按照寻址长度和变量中较大的寻址。(如寻址长度为4,要寻char,还是会一个一个的寻。寻址长度为4,要寻double的址,4个4个的寻。)

    pragma pack()结构体大小计算法

    1.变量前(面)字节总数为该变量的整数倍,结构体大小为寻址长度整数倍(寻址长度无意义除外);
    2.能放下不补齐,内嵌首尾只能补。

    #include<stdio.h>
    #pragma pack(4)
    
    struct stu1
    {
    	int a;//4字节
    	char b;//1字节
    	short c;//2字节
    	short d;//2字节
    	float e;//4字节
    	double f;//8字节
    };//24字节
    struct stu2
    {
    
    	char i;//1
    	struct
    	{
    		char j;
    		char k;//1
    		double l;//8
    		char m;//1
    		int n;//4
    		char r;
    	};
    	char s;//1
    	float t;//4
    };//36
    
    
    int main()
    {
    	printf("int类型的字节数为:%d\n", sizeof(int));
    	printf("char类型的字节数为:%d\n", sizeof(char));
    	printf("short类型的字节数为:%d\n", sizeof(short));
    	printf("float类型的字节数为:%d\n", sizeof(float));
    	printf("double类型的字节数为:%d\n", sizeof(double));
    	printf("结构体stu1类型的字节数为:%d\n", sizeof(struct stu1));
    	printf("结构体stu2类型的字节数为:%d\n", sizeof(struct stu2));
    	printf("i的地址%d\tj的地址%d\tk的地址%d\tl的地址%d\tm的地址%d\tn的地址%d\tr的地址%d\ns的地址%d\nt的地址是%d\n", (int)(&s.i), (int)(&s.j), (int)(&s.k), (int)(&s.l), (int)(&s.m), (int)(&s.n), (int)(&s.r),(int)(&s.s),(int)(&s.t));
    	return 0;
    }
    
    

    在这里插入图片描述

    试试stu2的计算
    1.变量前(面)字节总数为该变量的整数倍,结构体大小为寻址长度整数倍(寻址长度无意义除外);
    2.能放下不补齐,内嵌首尾只能补。
    对着如上说法:i和j之间内嵌了一个结构体,尽管两个char小于四,能放下,但是只能给char补到四字节。i元素占了四字节。然后继续看内嵌的结构体,char和char能在寻址长度4中放下,那就放,double来了放不下了,所以就把j,k后面补齐了两字节,再放double。然后m,n同理。r属于内嵌尾部,只能补足四个字节。所以其长度为4+1+3+8+4+4+4+4+4=36;

    一般的结构体

    可是要是每次都一个字节一个字节的寻址多慢啊。第一个int类型数据它就得寻四次,那可能会想到既然要寻的是四个字节,我就四个字节四个字节的寻,看了首地址要是不是我要找的数据我就跨四个字节,再寻。这样速度就快点了。如本文开始的例子,它占了12个字节。

    一般结构体的大小计算

    1.变量前(面)字节总数为该变量的整数倍(也有人说偏移量),结构体大小为最大成员的倍数;
    2.能放下不补齐,有内嵌的内嵌之前的字节总数为内嵌最大成员的整数倍。

    #include<stdio.h>
    struct stu2
    {
    
    	char i;//1+7由于有内嵌,内嵌最大为8
    	struct
    	{
    		char j;//1
    		char k;//1+6和j补齐凑成8位
    		double l;//8
    		char m;//4
    		int n;//4
    		char r;//8不能和内嵌之外的补
    	};
    	char s;//4
    	float t;//4//s后面能放下t,不补齐。哪怕把t删了,结构体大小不变。
    };//48
    
    

    在这里插入图片描述

    写出最小的结构体技巧

    我们写一般结构体时,有没有什么规则,只要我们遵从,就可以提高空间利用率,减少占位。简单来说,一凑二,二凑四,四凑八。先给数据变量按大小分类。把最大的写最后面。一层一层的凑,越往上越小(二能凑到四视为四。当整体,不是说上面的就用占位小的了)。

    位域

    我们知道,char有一个字节,有八位二进制数,同理,int可以存32位。其所存的值为所占字节能表示的值。如int a:1;就只能存0或1;
    一个例子

    #include<stdio.h>
    
    struct stu3
    {
    	
    	int f : 2;
    	int g : 1;
    	int k : 3;
    	//float f : 1;//会报错
    	//double c:1;//会报错
    	
    };
    
    int main()
    {
    	struct stu3 t;
    	t = {-2,-1,3};
    	printf("%d\t%d\t%d\t\n", t.f,t.g,t.k);
    	printf("结构体stu3的字节数为:%d\t", sizeof(struct stu3));
    	//printf("f的地址为%d\n",(int)(&t.f))//报错,不允许采用位域的地址
    	
    	return 0;
    }
    

    我们知道int类型存值的范围为$ -2^{31} 到23112^{31}-1所以对于n位,所能存的值为2n12n11-2^{n-1}到2^{n-1}-1

    在这里插入图片描述

    展开全文
  • 这就牵扯到结构体字节对齐的概念。 (什么叫字节对齐结构体是一种构造数据类型,里面可以有不同的数据类型的成员,在这些数据成员中,不同的数据类型所占的内存空间是不同的,那么这些成员是...

    首先我们来看一个结构体
    例题1

    struct STUDENT
    {
    char a;
    int b;
    }data;
    

    如上结构体占多少个字节呢?
    思考:char占一个字节,int占四个字节,所以总共占5个字节吗?
    其实不是。这就牵扯到结构体中字节对齐的概念。
    (什么叫字节对齐?结构体是一种构造数据类型,里面可以有不同的数据类型的成员,在这些数据成员中,不同的数据类型所占的内存空间是不同的,那么这些成员是怎么存储的呢?其实就是按字节对齐方式存储的,即以结构体成员中占内存最多的数据类型所占的字节数为标准,所有的成员在分配内存时,都要与这个长度对齐)
    正如上面这个例子:char占一个字节,int占四个字节,与4字节的长度对齐,也就是说,虽然char是一个字节,但是为了与4字节的长度对齐,所以其后面的3字节都会空着(但空并不是什么都没有就同定义了一个变量没有初始化一样)。
    在这里插入图片描述
    所以data不是占5个字节,而是占8个字节。
    又如三个成员对齐:
    例题2:

      struct STUDENT
            {
            char a;
            char b;
            int c;
            }data;
    

    在这里插入图片描述
    char a占一个字节,b占一个字节,b接着a后面填充
    此时data 仍占8个字节。
    由上思考:下面结构体占几个字节呢??
    例题3:

     struct STUDENT
            {
            char a;
            int b;
            char c;
            
            }data;
    

    在这里插入图片描述
    首先最长类型所占字节数为 4,所以是以 4 对齐。分配内存的时候 a 占 1 字节,然后 b 想紧接着 a 后面存储,但 a 后面还剩 3 字节,小于 b 的 4 字节,所以 b 另起一行分配。然后 c 想紧接着 b 后面分配,但是 b 后面没空了,所以 c 另起一行分配。所以总共 12 字节。

    总结:同样三个数据类型,只不过交换了一下位置,结构体变量data所占的内存空间就由8字节变成12字节,多了4字节。这就告诉我们,在声明结构体类型时,各类型成员的前后位置会对该结构体类型定义的结构体变量所占的字节数产生影响。没有规律的定义会增加系统给结构体变量分配的字节数,降低内存分配的效率。但这种影响对操作系统来说几乎是可以忽略不计的!

    又如多个成员对齐:

    例题4:

    struct STUDENT
            {
            char a;
            char b;
            char c;
            char d;
            chae e;
            int f;
            }data;
    

    在这里插入图片描述

    首先最长的数据类型占 4 字节,所以是以 4 对齐。然后 a 占 1 字节,b 接在 a 后面占 1 字节,c 接在 b 后面占 1 字节,d 接在 c 后面占 1 字节,此时满 4 字节了,e 再来就要另起一行。f 想紧接着 e 后面分配,但 e 后面还剩 3 字节,小于 int 类型的 4 字节,所以 f 另起一行。总共占12个字节。
    又如当出现数组怎样对齐:
    例题5:

    struct STUDENT
    {
        char name[10];
        int age;
        char sex;
        float score;
    }data;
    

    结构体变量 data 中成员最长类型占 4 字节,还是以 4 对齐。所以总共占24个字节。

    在这里插入图片描述

    展开全文
  • c语言结构体里面一般会按照某种规则去进行字节对齐。 我们先看一段代码: struct st1 { char name; double age; char sex; }; //32位下 sizeof(struct st1) = 16 //64位下 sizeof(struct st1) = 24 struct st...
  •   在计算结构体类型变量的大小时...原则1:数据成员的对齐规则(以最大的类型字节为单位)。   结构体(struct)的数据成员,第一个数据成员放在offset为0的地方,以后每个数据成员存放在offset为该数据成员大小的...
  • 如上结构体变量 data 占多少字节?char 占 1 字节,int 占 4 字节,所以总共占 5 字节吗?我们写一个程序验证一下: # include <stdio.h> struct STUDENT { char a; int b; }data; int main(void) { ...
  • 如上结构体变量 data 占多少字节?char 占 1 字节,int 占 4 字节,所以总共占 5 字节吗?我们写一个程序验证一下: # include <stdio.h> struct STUDENT { char a; int b; }data; int main(void) { printf...
  • C语言结构体字节对齐详解

    千次阅读 多人点赞 2019-05-22 18:23:43
    问大家一个问题: struct STUDENT { char a;...如上结构体变量 data 占多少字节?char 占 1 字节,int 占 4 字节,所以总共占 5 字节吗?我们写一个程序验证一下: #include <stdio.h> s...
  • 1.在C语言里面每一种数据类型都有字节对齐比如在32位操作系统下:整型的自身对齐数就是 4 字节,字符型就是 1 字节,double就是 8 字节。 但是结构体的计算方式就和普通的数据类型不一样。  在C语言里面字节对齐...
  • 学弟最近咨询结构体的问题,这里...如上结构体变量 data 占多少字节?char 占 1 字节,int 占 4 字节,所以总共占 5 字节吗?我们写一个程序验证一下: # include &lt;stdio.h&gt; struct STUDENT { cha...
  • 原则1 数据成员对齐规则:结构(struct或联合union)的数据成员,第一个数据成员放在offset为0的地方,以后每个数据成员存储的起始位置要从该成员大小的整数倍开始(比如int在32位机为4字节,则要从4的整数倍地址...
  • 以int型数据为例,如果它在内存中存放的位置按4字节对齐,也就是说1个int的数据全部落在计算机一次取数的区间内,那么只需要取一次就可以了。如果不对齐,很不巧,这个int数据刚好跨越了取数的边界,这样就需要...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 15,393
精华内容 6,157
关键字:

c语言结构体字节对齐规则

c语言 订阅