c语言结构体字节对齐与位域,c语言字节对齐问题详解

发布时间:2022-12-01

本文目录一览:

  1. c语言 结构体位域问题
  2. C语言结构体长度字节对齐问题
  3. 关于C语言中的结构体字节对齐
  4. c语言结构体对齐的问题。
  5. C语言中位域和结构体得区别是什么?
  6. 什么是C语言结构体字节对齐,为什么要对齐

c语言 结构体位域问题

c存在第三个字节 sizeof结构体,这个要看结构体内变量是如何定义的,结构体存放数据有个对齐原则,找到占用最大字节的变量,然后都向它对齐,比如boolchar类型占用一个字节,short占两个字节,intfloat为4个字节,double为八个字节。 定义的顺序不同,sizeof的结果不同。我给你举个例子。

struct A {
    int a;
    char b;
    char c;
};

sizeof(A)应该为4+1+1,但是需要对齐,所以这个值就是 8。 图1 这个是struct A的变量存储

struct B {
    char a;
    int b;
    char c;
};

sizeof(B)这个值就是12。 图2 这个是struct B的变量存储。

C语言结构体长度字节对齐问题

因为当结构体中有多个数据类型时,结构体的长度对齐是按数据类型长度最长的那个来对齐的,double类型占8个字节,所以每个成员变量都按8个字节的长度来算,就是5*8=40,验证程序如下:

#include stdio.h
struct chji {
    char name[9];
    int number;
    char sex;
    double score;
    float aa;
};
struct chji stu;
int main() {
    printf("sizeof(struct chji)=%d\n", sizeof(struct chji));
    return 0;
}

运行结果:sizeof(struct chji)=40 如果要按单个字节的长度来对齐的话,代码如下:

#include stdio.h
#pragma pack(1)
struct chji {
    char name[9];  //9
    int number;    //4
    char sex;      //1
    double score;  //8
    float aa;      //4
};
struct chji stu;
#pragma pack()
int main() {
    printf("sizeof(struct chji)=%d\n", sizeof(struct chji));
    return 0;
}

运行结果:sizeof(struct chji)=269+4+1+8+4=26,你可以查下#pragma pack()相关的资料的,就会清楚了。

关于C语言中的结构体字节对齐

仔细看一下书中的说明吧,这三言两语介绍起来有点累 或从网上查阅相关的技术资料,有详细描述。 一般简单来说,结构体从第一个变量开始检查空间的“对齐字节数”, 默认以第一个字节大小作为对齐字节数,如果遇上的下一个字节与当前对齐字节数不同时,就按两者中较大的来进行结构体空间分配,接下来的对齐就全按此值来对齐,直到再遇上不同的才进行检查或改变。

c语言结构体对齐的问题。

这是个好问题! 为什么会有对齐的问题呢?简单的说就是为了提高访问内存的效率,这和CPU内部的机制有关,如果你想深入理解,需要阅读 Intel 开发者手册。对齐采用的总体原则是这样的:4字节变量的存放要对齐到可以被4整除的地址上,8字节变量的存放要对齐到可以被8整除的地址上。其他变量类推就行了。如果没对齐编译器就会将某个变量的存储往后推迟几个字节,以保证对齐后再存放。 具体到这个问题就是可以先假设结构体变量从地址0处开始存放,那么第一种情况就是这样的了:

  • cat 存放的位置是地址0-地址3
  • a数组存放的位置是地址4-地址23
  • dog存放的位置是地址24到地址31 这里可以看到它们都符合对齐的原则(即每个变量开始存放的地址可以除尽它们所占的字节数),所以是32。 第二种情况是这样的:
  • cat仍然存放到地址0-地址3处
  • a数组是地址4到地址27处
  • 存放dog时编译器计算除下一个地址28并不能除尽double的字节数8,于是它要将地址进行递增。29,30,31仍然不能除尽8,知道递增到32时可以将8除尽了,所以dog变量会被存放在地址32到地址39处,从地址0到地址39正好40个字节,这就解释了第二种情况了。 ps. 其实你这个问题还有一种变种就是一个结构体里在套一个结构体,这时会牵扯到内部的结构体对齐的问题。等你以后遇见了再给我提问吧,我给你解释。

C语言中位域和结构体得区别是什么?

C语言结构体对齐也是老生常谈的话题了。基本上是面试题的必考题。内容虽然很基础,但一不小心就会弄错。写出一个struct,然后sizeof,你会不会经常对结果感到奇怪?sizeof的结果往往都比你声明的变量总长度要大,这是怎么回事呢? 开始学的时候,也被此类问题困扰很久。其实相关的文章很多,感觉说清楚的不多。结构体到底怎样对齐? 有人给对齐原则做过总结,具体在哪里看到现在已记不起来,这里引用一下前人的经验(在没有#pragma pack宏的情况下): 原则1、数据成员对齐规则:结构(struct或联合union)的数据成员,第一个数据成员放在offset为0的地方,以后每个数据成员存储的起始位置要从该成员大小的整数倍开始(比如int在32位机为4字节,则要从4的整数倍地址开始存储)。 原则2、结构体作为成员:如果一个结构里有某些结构体成员,则结构体成员要从其内部最大元素大小的整数倍地址开始存储。(struct a里存有struct bb里有charintdouble等元素,那b应该从8的整数倍开始存储。) 原则3、收尾工作:结构体的总大小,也就是sizeof的结果,必须是其内部最大成员的整数倍,不足的要补齐。 这三个原则具体怎样理解呢?我们看下面几个例子,通过实例来加深理解。 例1

struct {
    short a1;
    short a2;
    short a3;
} A;
struct {
    long a1;
    short a2;
} B;
  • sizeof(A) = 6;这个很好理解,三个short都为2。
  • sizeof(B) = 8;这个比是不是比预想的大2个字节?long为4,short为2,整个为8,因为原则3。 例2
struct A {
    int a;
    char b;
    short c;
};
struct B {
    char b;
    int a;
    short c;
};
  • sizeof(A) = 8int为4,char为1,short为2,这里用到了原则1和原则3。
  • sizeof(B) = 12;是否超出预想范围?char为1,int为4,short为2,怎么会是12?还是原则1和原则3。 深究一下,为什么是这样,我们可以看看内存里的布局情况。
  • a b c
    A的内存布局:1111, 1*, 11
  • b a c
    B的内存布局:1***, 1111, 11**
    其中星号*表示填充的字节。A中,b后面为何要补充一个字节?因为cshort,其起始位置要为2的倍数,就是原则1。c的后面没有补充,因为bc正好占用4个字节,整个A占用空间为4的倍数,也就是最大成员int类型的倍数,所以不用补充。 B中,bchar为1,b后面补充了3个字节,因为aint为4,根据原则1,起始位置要为4的倍数,所以b后面要补充3个字节。c后面补充两个字节,根据原则3,整个B占用空间要为4的倍数,c后面不补充,整个B的空间为10,不符,所以要补充2个字节。 再看一个结构中含有结构成员的例子: 例3
struct A {
    int a;
    double b;
    float c;
};
struct B {
    char e[2];
    int f;
    double g;
    short h;
    struct A i;
};
  • sizeof(A) = 24;这个比较好理解,int为4,double为8,float为4,总长为8的倍数,补齐,所以整个A为24。
  • sizeof(B) = 48;看看B的内存布局。 e f g h i
    B的内存布局:11**, 1111, 11111111, 11***** , 1111****, 11111111, 1111****
    i其实就是A的内存布局。i的起始位置要为24的倍数,所以h后面要补齐。把B的内存布局弄清楚,有关结构体的对齐方式基本就算掌握了。 以上讲的都是没有#pragma pack宏的情况,如果有#pragma pack宏,对齐方式按照宏的定义来。比如上面的结构体前加#pragma pack(1),内存的布局就会完全改变。sizeof(A) = 16sizeof(B) = 32; 有了#pragma pack(1),内存不会再遵循原则1和原则3了,按1字节对齐。没错,这不是理想中的没有内存对齐的世界吗。
  • a b c
    A的内存布局:1111, 11111111, 1111
  • e f g h i
    B的内存布局:11, 1111, 11111111, 11 , 1111, 11111111, 1111 那#pragma pack(2)的结果又是多少呢?#pragma pack(4)呢?留给大家自己思考吧,相信没有问题。 还有一种常见的情况,结构体中含位域字段。位域成员不能单独被取sizeof值。C99规定intunsigned intbool可以作为位域类型,但编译器几乎都对此作了扩展,允许其它类型类型的存在。 使用位域的主要目的是压缩存储,其大致规则为:
  1. 如果相邻位域字段的类型相同,且其位宽之和小于类型的sizeof大小,则后面的字段将紧邻前一个字段存储,直到不能容纳为止;
  2. 如果相邻位域字段的类型相同,但其位宽之和大于类型的sizeof大小,则后面的字段将从新的存储单元开始,其偏移量为其类型大小的整数倍;
  3. 如果相邻的位域字段的类型不同,则各编译器的具体实现有差异,VC6采取不压缩方式,Dev-C++采取压缩方式;
  4. 如果位域字段之间穿插着非位域字段,则不进行压缩;
  5. 整个结构体的总大小为最宽基本类型成员大小的整数倍。 还是让我们来看看例子。 例4
struct A {
    char f1 : 3;
    char f2 : 4;
    char f3 : 5;
};

a b c
A的内存布局:111, 1111 *, 11111 ***
位域类型为char,第1个字节仅能容纳下f1f2,所以f2被压缩到第1个字节中,而f3只能从下一个字节开始。因此sizeof(A)的结果为2。 例5

struct B {
    char f1 : 3;
    short f2 : 4;
    char f3 : 5;
};

由于相邻位域类型不同,在VC6中其sizeof为6,在Dev-C++中为2。 例6

struct C {
    char f1 : 3;
    char f2;
    char f3 : 5;
};

非位域字段穿插在其中,不会产生压缩,在VC6和Dev-C++中得到的大小均为3。 考虑一个问题,为什么要设计内存对齐的处理方式呢?如果体系结构是不对齐的,成员将会一个挨一个存储,显然对齐更浪费了空间。那么为什么要使用对齐呢?体系结构的对齐和不对齐,是在时间和空间上的一个权衡。对齐节省了时间。假设一个体系结构的字长为w,那么它同时就假设了在这种体系结构上对宽度为w的数据的处理最频繁也是最重要的。它的设计也是从优先提高对w位数据操作的效率来考虑的。有兴趣的可以google一下,人家就可以跟你解释的,一大堆的道理。 最后顺便提一点,在设计结构体的时候,一般会尊照一个习惯,就是把占用空间小的类型排在前面,占用空间大的类型排在后面,这样可以相对节约一些对齐空间。 本篇文章来源于:开发学院 原文链接:

什么是C语言结构体字节对齐,为什么要对齐

对齐跟数据在内存中的位置有关。如果一个变量的内存地址正好位于它长度的整数倍,他就被称做自然对齐。比如在32位CPU下,假设一个整型变量的地址为0x00000004,那它就是自然对齐的。 需要字节对齐的根本原因在于CPU访问数据的效率问题。假设上面整型变量的地址不是自然对齐,比如为0x00000002,则CPU如果取它的值的话需要访问两次内存,第一次取从0x00000002-0x00000003的一个short,第二次取从0x00000004-0x00000005的一个short然后组合得到所要的数据,如果变量在0x00000003地址上的话则要访问三次内存,第一次为char,第二次为short,第三次为char,然后组合得到整型数据。而如果变量在自然对齐位置上,则只要一次就可以取出数据。一些系统对对齐要求非常严格,比如SPARC系统,如果取未对齐的数据会发生错误,举个例:

char ch[8];
char *p = ch[1];
int i = *(int *)p;

运行时会报segment error,而在x86上就不会出现错误,只是效率下降。