C语言结构体对齐也是老生常谈的话题了。基本上是面试题的必考题。内容虽然很基础,但一不小心就会弄错。写出一个struct,然后sizeof,你会不会经常对结果感到奇怪?sizeof的结果往往都比你声明的变量总长度要大,这是怎么回事呢?
开始学的时候,也被此类问题困扰很久。其实相关的文章很多,感觉说清楚的不多。结构体到底怎样对齐?
有人给对齐原则做过总结,具体在哪里看到现在已记不起来,这里引用一下前人的经验(在没有#pragma pack宏的情况下):
&&原则1、数据成员对齐规则:结构(struct或联合union)的数据成员,第一个数据成员放在offset为0的地方,以后每个数据成员存储的起始位置要从该成员大小的整数倍开始(比如int在32位机为4字节,则要从4的整数倍地址开始存储)。
&&原则2、结构体作为成员:如果一个结构里有某些结构体成员,则结构体成员要从其内部最大元素大小的整数倍地址开始存储。(struct a里存有struct b,b里有char,int,double等元素,那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) = 8; int为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后面为何要补充一个字节?因为c为short,其起始位置要为2的倍数,就是原则1。c的后面没有补充,因为b和c正好占用4个字节,整个A占用空间为4的倍数,也就是最大成员int类型的倍数,所以不用补充。
B中,b是char为1,b后面补充了3个字节,因为a是int为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) = 16; sizeof(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规定int、unsigned int和bool可以作为位域类型,但编译器几乎都对此作了扩展,允许其它类型类型的存在。
使用位域的主要目的是压缩存储,其大致规则为:
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个字节仅能容纳下f1和f2,所以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一下,人家就可以跟你解释的,一大堆的道理。
最后顺便提一点,在设计结构体的时候,一般会尊照一个习惯,就是把占用空间小的类型排在前面,占用空间大的类型排在后面,这样可以相对节约一些对齐空间。
该文地址: http://hi.baidu.com/klcdyx2008/blog/item/4d9f9fd9e4c9812111df9b13.html
分享到:
相关推荐
当应用于`struct`结构体时,它会返回整个结构体占据的总字节数,这涉及到内存对齐的问题。内存对齐是编译器为了提高数据访问效率而采取的一种策略,它规定了不同类型的数据在内存中的存储方式和位置。 首先,我们来...
C 结构体对齐详解 C 结构体对齐详解是指在 C 语言中,结构体数据成员的内存对齐方式。结构体数据成员的内存对齐是指结构体成员在内存中的存放位置,需要满足某个对齐模数的要求,以便简化处理器与内存之间的数据...
C语言中的结构体(Struct)是一种用户定义的数据类型,允许将不同类型的数据项聚集在一起构成一个单一类型。结构体在C语言中扮演着极其重要的角色,它使得程序员可以创建复杂的自定义数据结构,以更贴近现实世界的...
### C语言结构体的大小计算方法与结构体对齐 #### 一、使用`sizeof`计算结构体的大小 在C语言中,我们通常使用`sizeof`运算符来计算结构体的大小。例如,考虑以下结构体定义: ```c struct Student { int id; ...
在C++编程中,结构体(struct)字节对齐是一项重要的优化技术,它涉及到内存管理、性能优化以及跨平台兼容性。字节对齐是指在内存中安排数据时,确保数据的起始地址能够被特定数值(对齐模数)整除,这样做的目的是...
C 语言结构体对齐也是老生常谈的话题了。基本上是面试题的必考题。内容 虽然很基础,但一不小心就会弄错。写出一个 struct,然后 sizeof,你会不会经 常对结果感到奇怪? sizeof的结果往往都比你声明的变量总长度要...
了解结构体对齐对于编写高效的C语言代码至关重要,特别是在处理大量数据结构或跨平台开发时。通过使用#pragma pack或其他编译器提供的对齐控制选项,程序员可以直接干预对齐策略,以适应特定的需求。在实际编程中,...
### 结构体对齐、typedef、结构体指针与C++引用详解 #### 一、结构体对齐 **结构体对齐**是指在结构体中的成员变量在内存中的排列方式,这种排列是为了提高CPU访问内存的速度。由于现代处理器的设计倾向于以一定的...
在编程领域,结构体对齐是一项重要的内存管理技术,它涉及到如何在计算机内存中安排数据结构,以提高访问效率和优化存储空间。`#pragma pack` 是 C 和 C++ 编译器提供的一种预处理指令,用于控制结构体成员的对齐...
结构体字节对齐规则,主要是介绍结构体字节对齐规则,内容有点长,但是很全面.
结构体对齐是编程语言中一个重要的概念,特别是在C/C++中。它是计算机科学中用于优化内存访问效率的一种策略,确保数据在内存中按照特定的规则排列,以便CPU能更高效地读取和写入数据。对齐的主要目标是减少访问时间...
然而,在处理结构体时,一个重要的概念是结构体对齐,这涉及到内存分配和效率优化。对齐规则确保了数据在内存中的布局能够最大化地利用处理器的访问效率。 首先,我们要理解结构体对齐的三个关键概念: 1. **数据...
【结构体对齐详解】 结构体对齐是编程中一种重要的优化策略,它涉及到计算机内存管理的基本原理。在计算机系统中,数据存储受到特定规则的约束,这些规则要求不同类型的数据必须按照特定的对齐模数(alignment ...
C语言结构体的字节对齐及指定对齐方式 在C语言中,结构体的字节对齐是非常重要的,它直接影响着程序的性能和可移植性。本文将详细介绍结构体的字节对齐、指定对齐方式和相关概念。 一、字节对齐的作用和原因 字节...
C语言结构体大小及对齐问题 字节序(Endianness):大端和小端 二、应用 结构体是一种数据的归类方式,相比数组或变量更具有整体全面性,例如一个数组只可以放一些按照元素顺序存放的单元变量,即 buffer = {x, x, ...
- **定义**: 结构体(Struct)是C语言中的一个复合数据类型,它能够将不同类型的数据整合到一个单元之中,形成一个新的数据结构。这有助于管理和组织复杂的程序数据。 - **语法**: 在C语言中,使用`struct`关键字来...
### 结构体以及结构体的对齐 #### 一、结构体的概念 在C语言中,**结构体**是一种复合数据类型,它允许开发者将不同类型的数据组合在一起形成...同时,了解结构体对齐的原理对于优化内存使用和提高程序性能至关重要。