结构体的秘密

来源:互联网 发布:pdf转word for mac 编辑:程序博客网 时间:2024/05/02 10:48

      结构体,这样的一种数据结构,可能曾经在我们学习C语言的时候,给我们带来过不小的新鲜感与神秘感。当然,随着我们与它越来越多的接触,这种新鲜感与神秘感或许已经慢慢地被我们淡忘掉了,因为它与我们已经、正在或将要学习的一些的东西相比,显得是那样单纯与朴素。不过,任何一种东西都有它可以研究的东西在里面,如果不太了解一些细节的话,可能会遇到一些很奇怪的问题。结构体也是这样的一种东西,这也是这一篇文字所要表达的内容。

      问题

      为了讲述结构体的秘密,这里先以一个问题作为思考的线索:

      以上声明了一个结构体,其中有三个成员变量,它们分别属于不同的类型。现在的问题是,这个结构体的大小是多少字节?

      相信读者在思考这个问题时,会从以下不同的两方面的考虑来解决这个问题:

      1、不同位数的计算机基本数据类型所占的字节数是不同的,我还需要知道使用的机器的位数是多少。

      2、理论计算太麻烦了,我还是用编译器编译一下就知道了。

      思考

      接下来这里会对以上两种思路进行一些思考,从而引出对结构体的一些疑问:

      1、知道机器的位数就一定能够计算出结构体的大小么?

      为了使编写的程序能够在不同环境(平台位数、操作系统等)的机器上运行,其实人们已经想出了许多办法来解决不同机器上基本数据类型占用空间也不同的问题。比如我们可以采取某种方式直接在声明时定义类型所占的位数(在网络通信编程当中会用到,因为必须保证通信双方对数据类型大小理解的相同),如下面这个头文件就利用条件编译的原理实现了这种方法,并且这种方法是跨操作系统平台的,该头文件的文件名为stdint.h(点击expand source展开代码)。

      在包含了stdint.h这个头文件后,我们就可以定制相应长度的变量了。如一个变量所占空间为8位(1个字节),我们可以声明它为uint8_t variableA;另一个变量占用64位(8个字节),我们可以声明它为uint64_t variableB。假设我们一般的32位平台上编译本文一开始的结构体,由于bool类型占用1个字节(计算机最小的存储单位为1个字节),char类型占用1个字节,int类型占用4个字节,利用stdint.h头文件我们可以直观的将结构体改写为如下的形式:

      这样自然而然就可以计算出结构体的大小为6个字节(48位)了,当然这个结果不能是算正确的。以VS编译器编译出的结果为例,使用sizeof得出的结果为8个字节(原来的与修改后的结构体均为这个大小)。这个例子表示,我们虽然知道了每个数据类型所占用的空间大小,把它们放到结构体当中后,我们还是不能从理论上准确地计算出其在程序运行时的大小,这其中一定有一些原因。

      2、编译器得出的结果一定可靠么?

      回到本文开头的例子,对于第二种解决方法的思路,用下面这个例子就会让人对编译器编译出的结果感到疑惑。

      在32位平台上使用VS编译器编译上述代码,并用sizeof获取结构体的大小,得到结果SMyStructA的大小为8个比特,SMyStructB的大小居然为12个比特,均不为理论上的6个比特。这真是太奇怪了,仅仅是改变了声明对象的顺序,结构体的大小却发生了改变。

      分析

      编译器在对结构体编译时,会进行字节对齐的处理,而对不同操作系统下的不同编译器而言,默认字节对齐方式可能也不相同。以VS编译器为例,从多次实验的结果上来看,编译器在编译时会默认对齐结构体中长度最大基本类型,即结构体的大小一定会是该最大基本类型长度的整数倍,这个长度这里先暂时称为单位长度。相邻n(n>=2)个元素的大小之和若不大于这个单位长度,则这n个元素会合并占用一个单位长度;否则前n-1各元素将单独占用一个单位长度。对于SMyStructA与SMyStructB的例子,这里n等于2,SMyStructA对应合并占用的例子,SMyStructB对应单独占用的例子。

      为了在平台下的不同编译器实现相同的字节对齐方式,可以使用如下代码声明结构体:

      设置按1个字节对齐后,得到的结果就都为6个字节了。

      结构体字节对齐的设置在网络通信编程中相当重要,正如上文所说网络通信编程当中必须保证通信双方对数据类型大小理解的相同,否则交换的通信数据自然也就不对了。