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

原创 2004年08月03日 17:48:00

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++编译器在处理数据时经常把结构变量中的成员的大小按照48的倍数计算,这就叫数据对齐(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 bdouble c不能挤在一个4个字节的单位里面。于是在char bdouble 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就出现问题了呢?

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

struct

{

       int a;

int d;

       double c;

       char b;

} sc1;

struct

{

       int a;

       double c;

} sc2;

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

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

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

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

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

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

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

1、在程序内部用#pragma pack(n)指定<?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" />

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

2、  用编译指令

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

例子:

CL /Zp8

#include

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 achar 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的说明

 

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.

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:

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:

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

C语言对其方式--struct数据结构必学

相同的对齐方式下 ,结构体内部数据 定义的顺序不同 ,结构体整体占据内存空间也 不同 ,如下: 设结构体如下定义: struct A {      int      a;      char    b...
  • jk110333
  • jk110333
  • 2014年02月15日 13:21
  • 4630

C语言字节对齐、结构体对齐最详细的解释

文章最后本人做了一幅图,一看就明白了,这个问题网上讲的不少,但是都没有把问题说透。   一、概念       对齐跟数据在内存中的位置有关。如果一个变量的内存地址正好位于它长度的整数倍,他就...
  • lanzhihui_10086
  • lanzhihui_10086
  • 2015年03月17日 21:57
  • 9326

结构体struct 联合体union 及内存对齐

原文链接:http://blog.csdn.net/coolingcoding/article/details/7278555 当在C中定义了一个结构类型时,它的大小是否等于各字段(...
  • Aiphis
  • Aiphis
  • 2015年09月24日 10:33
  • 2466

C语言中的结构体数据存储-对齐规则

(1)什么是字节对齐   一个变量占用 n 个字节,则该变量的起始地址必须能够被 n 整除,即: 每个变量的起始存放地址 % n = 0,      对于结构体,这个 n 取其成员种的数据类型...
  • yangxiaochun318
  • yangxiaochun318
  • 2014年02月03日 22:03
  • 630

java 与C语言使用socket通信传递结构体数据

  • 2013年09月27日 23:55
  • 3KB
  • 下载

java 与C语言传递结构体数据

  • 2017年05月23日 14:29
  • 46B
  • 下载

C语言数据类型大小和结构体中变量…

【注释】本博文介绍十分详细,本人收益良多,特此收藏。 【出处】源自网络,原始出处未知。   C语言数据类型大小和结构体中变量的地址分配方法 数据类型:   char  short   i...
  • shangyaowei
  • shangyaowei
  • 2013年12月19日 20:41
  • 279

C语言入门(15)——结构体与数据抽象

大多数的计算机运算是对现实世界的模拟,如果想用计算机来模拟现实世界需要用到数据抽象的方法。所谓抽象是从实际的人、物、事和概念中抽取所关心的共同特征,,忽略非本质的细节,吧这些特征用各种概念精确的加以描...
  • yincheng01
  • yincheng01
  • 2014年06月30日 12:25
  • 1387

php读取二进制流(C语言结构体struct数据文件)

尽管php是用C语言开发的,不过令我不解的是php没有提供对结构体struct的直接支持。 不过php提供了pack和unpack函数,用来进行二进制数据(binary data)和php内部数...
  • climb_up
  • climb_up
  • 2014年04月09日 09:40
  • 597

黑马程序员----C语言复杂数据类型之结构体与枚举篇

-------android培训、java培训、IOS培训、期待与您交流!------- 在iOS开发中,结构体是经常用到的数据类型,使用频率不亚于指针...
  • xiaoqinqinqin0
  • xiaoqinqinqin0
  • 2015年04月19日 11:20
  • 234
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:C语言结构体中的数据对齐
举报原因:
原因补充:

(最多只允许输入30个字)