分享
 
 
 

C语言结构体中的数据对齐

王朝c/c++·作者佚名  2006-01-30
窄屏简体版  字體: |||超大  

上C++实验课的时候,坐在我旁边的同学问了我一个问题:

struct

{

char a;

int b;

double c;

} sa;

其中sizeof(char) =1; sizeof(int) = 4; sizeof(double) = 8; 所以sizeof(sa)应该等于1+4+8=13才对,但是当她编程输出sizeof(sa)的值却是16,她问我为什么?

我把我所知道的告诉了她:为了加快CPU的存取速度,C++编译器在处理数据时经常把结构变量中的成员的大小按照4或8的倍数计算,这就叫数据对齐(data alignment)。sizeof(sa)=16就是因为这个原因,并不是程序的错误,而是编译器为了使数据对齐而在结构中加上了3个字节的空位造成的。这样做可能会浪费一些内存,但理论上速度快了。

回答完她的问题后,我又给自己提出了一个问题,数据究竟是怎么对其的呢?这个问题,我也不知道,所以回到寝室我做了几个实验,并且查了相关的资料,最终得出了结论。

首先,应该说明的我的开发环境是WinXP+VC6,因为不同的操作系统和不同的编译器,得出的结果可能不一样。但是不同系统的原理应该是一样的,所以希望我所做的这些努力也能对使用其它开发环境的朋友有所帮助。

struct

{

int a;

char b;

} sa;

struct

{

int a;

char b;

double c;

} sb;

struct

{

int a;

double c;

char b;

} sc;

输出结果:sizeof(sa)=8, sizeof(sb)=16, sizeof(sc)=24;

只看sizeof(sa)的结果,我当时认为,结构体内部的数据是按4个字节一组存放的,所以尽管sizeof(char)=1但是,sa中的char b还是占用了4个字节,使得sizeof(sa)=8,为了验证我的“猜想”,我针对sa又做了一个实验:

struct

{

int a;

char b, c, d, e,;

} sa1;

struct

{

int a;

char b;

int c;

} sa2;

输出结果:sizeof(sa1)=8, sizeof(sa2)=12;

看来输出结果验证了我的刚才的“猜想”,于是,我试着用我推测出的结论解释sizeof(sb)=16, sizeof(sc)=24;

sb中的int a 占4个字节,紧接着的char b占用1个字节,但是char b随后的double c需要占用8个字节,所以char b和double c不能挤在一个4个字节的单位里面。于是在char b和double c之间填了3个字节的空位,实现与其后的数据的对齐,这样就不难理解为什么sizeof(sa1)=8了。

我试图按着同样的思路理解sizeof(sc)=24,但是却遇到了问题。sc中的int a占用4个字节,紧接着的double c占用8个字节,最后char b占用一个字节,但是为了填满4个字节的单位,char b后面应该填补3个字节的空位。这样下来sizeof(sc)应该等于4+8+4=16才对,可是输出的结果却是sizeof(sc)=24。看来我刚才的“猜想”是有问题的。问题究竟出在哪里呢?为什么根据我的“猜想”分析sa和和sb都没有问题,到sc就出现问题了呢?

sb和sc的唯一区别就是char b和double c的定义次序颠倒了,结果使得sizeof(sb)和sizeof(sc)产生了差别。能想到的唯一解释是sb和sc的对齐的最小单位不一样了。于是我又设计了一组对比:

struct

{

int a;

int d;

double c;

char b;

} sc1;

struct

{

int a;

double c;

} sc2;

输出结果:sizeof(sc1)=24, sizeof(sc2)=16;

sc1在sc的基础上在int a后面紧接着加上了int d;但是结果sizeof(sc1)=24= sizeof(sc),这说明在int a的后面存在4个字节的空位。看来这次系统给结构体内的变量分配空间的时候,不再像sa那样以4个字节为一组了。

再看sc2,只是去掉了一个char b,结果使sizeof(sc2)=16,这说明char b后面存在着7个字节的空位。由此可见这次系统是以8个字节为一组给结构体内的变量分配空间的。

综合分析以上的试验结果,sa以4个字节为一组给结构体内的变量分配空间,从而达到数据对齐;sb和sc以8个字节为一组给结构体内的变量分配空间,从而达到数据对齐。为什么会有差别呢?仔细观察不难发现:sa中占用空间最多的类型是int型,而sizeof(int)=4;sb和sc中占用空间最多的类型是double型,而sizeof(double)=8;由此可间,系统是根据结构体内所包含的类型制定分配空间的单位的。

之后我又针对这个问题做了几组试验,得到的结果与以上的分析结论相同。

这样,以后再遇到sizeof的时候,我就不用等到输出才能看到sizeof的值了,只要用看到struct内部的定义格式就能计算出sizeof的实际值了。

得出结论后,我又查阅了与sizeof相关的资料,发现可以分别在程序内部和用编译指令指定结构体内部对齐的具体方式:

1、在程序内部用#pragma pack(n)指定

#pragma pack(1)可以使编译器不在struct内留空位。

2、 用编译指令

使用CL的/Zp开关,例如:CL /Zp8 指定结构体内以8个字节为单位包装结构体内的数据。

例子:

CL /Zp8

#include <iostream>

using namespace std;

struct // packing is 8

{

int a;

char b;

} sa;

#pragma pack(push,1) // packing is now 1

struct

{

int a;

char b;

double c;

} sb;

#pragma pack(pop) // packing is 8

struct

{

int a;

double c;

char b;

} sc;

int main(void)

{

cout << "sizeof(sa) =" << sizeof(sa) << endl;

cout << "sizeof(sb) =" << sizeof(sb) << endl;

cout << "sizeof(sc) =" << sizeof(sc) << endl;

return 0;

}

输出结果:sizeof(sa)=8,sizeof(sb)=13,sizeof(sc)=24

还有一点必须指出:由#pragma pack(n)设定packing后,编译器实际排列结构体内部成员的时候,并不一定是按照#pragma pack(n)中的n指定长度为单位的,还取决于结构体内部占用空间最大的类型的长度。这么做可能是为了节省空间。例如:

struct // packing is 8

{

int a;

char b;

} sa;

在排列成员int a和char b的时候是以4个字节为单位的。因为此时sizeof(int)=4<8,所以排列的时候以4为单位。但是当packing值小于4的时候则以packing值为单位排列。所以:

struct // packing is 8

{

int a;

int c;

char b;

} sa;

输出结果为:sizeof(sa)=12;(以4个字节为单位)

当packing值小于结构体内占用空间最多成员占用的字节数的时候,以packing值为准。

#pragma pack(push,1) // packing is now 1

struct

{

int a;

char b;

double c;

} sb;

输出结果为:sizeof(sb)=13;(以1个字节为单位)

C++Builder可以在Options对话框中修改Advanced compiler页中的Data alignment为按字节对齐。

附:MSDN中关于pack的说明

#pragma pack( show | [ n] )

Specifies packing alignment for structure, union, and class members. Whereas the packing alignment of structures, unions, and classes is set for an entire translation unit by the /Zp option, the packing alignment is set at the data-declaration level by the pack pragma. The pragma takes effect at the first structure, union, or class declaration after the pragma is seen; the pragma has no effect on definitions.

When you use #pragma pack(n), where n is 1, 2, 4, 8, or 16, each structure member after the first is stored on the smaller member type or n-byte boundaries. If you use #pragma pack without an argument, structure members are packed to the value specified by /Zp. The default /Zp packing size is /Zp8.

The show option causes the C4810 warning to issue, displaying the current pack value.

#pragma pack(show)

#pragma pack(4)

#pragma pack(show)

void main(){}

The compiler also supports the following enhanced syntax:

#pragma pack( [ [ { push | pop}, ] [ identifier, ] ] [ n ] )

This syntax allows you to combine program components into a single translation unit if the different components use pack pragmas to specify different packing alignments.

Each occurrence of a pack pragma with a push argument stores the current packing alignment on an internal compiler stack. The pragma’s argument list is read from left to right. If you use push, the current packing value is stored. If you provide a value for n, that value becomes the new packing value. If you specify an identifier, a name of your choosing, the identifier is associated with the new packing value.

Each occurrence of a pack pragma with a pop argument retrieves the value at the top of an internal compiler stack and makes that value the new packing alignment. If you use pop and the internal compiler stack is empty, the alignment value is that set from the command-line and a warning is issued. If you use pop and specify a value for n, that value becomes the new packing value. If you use pop and specify an identifier, all values stored on the stack are removed from the stack until a matching identifier is found. The packing value associated with the identifier is also removed from the stack and the packing value that existed just before the identifier was pushed becomes the new packing value. If no matching identifier is found, the packing value set from the command line is used and a level-one warning is issued. The default packing alignment is 8.

The new, enhanced functionality of the pack pragma allows you to write header files that ensure that packing values are the same before and after the header file is encountered:

/* File name: include1.h

*/

#pragma pack( push, enter_include1 )

/* Your include-file code ... */

#pragma pack( pop, enter_include1 )

/* End of include1.h */

In the previous example, the current pack value is associated with the identifier enter_include1 and pushed, remembered, on entry to the header file. The pack pragma at the end of the header file removes all intervening pack values that may have occurred in the header file and removes the pack value associated with enter_include1. The header file thus ensures that the pack value is the same before and after the header file.

The new functionality also allows you to use code, such as header files, that uses pack pragmas to set packing alignments that differ from the packing value set in your code:

#pragma pack( push, before_include1 )

#include "include1.h"

#pragma pack( pop, before_include1 )

In the previous example, your code is protected from any changes to the packing value that might occur in include.h.

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有