﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>IT博客-Winter-随笔分类-C、C++语言</title><link>http://www.cnitblog.com/Winter/category/4124.html</link><description /><language>zh-cn</language><lastBuildDate>Thu, 29 Sep 2011 22:25:51 GMT</lastBuildDate><pubDate>Thu, 29 Sep 2011 22:25:51 GMT</pubDate><ttl>60</ttl><item><title>sizeof用法 [转贴 2006-09-13 14:48:15 ] 发表者: goodzyz </title><link>http://www.cnitblog.com/Winter/archive/2006/11/06/18869.html</link><dc:creator>winter</dc:creator><author>winter</author><pubDate>Mon, 06 Nov 2006 01:28:00 GMT</pubDate><guid>http://www.cnitblog.com/Winter/archive/2006/11/06/18869.html</guid><wfw:comment>http://www.cnitblog.com/Winter/comments/18869.html</wfw:comment><comments>http://www.cnitblog.com/Winter/archive/2006/11/06/18869.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/Winter/comments/commentRss/18869.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/Winter/services/trackbacks/18869.html</trackback:ping><description><![CDATA[
		<p>
				<span lang="EN-US">sizeof</span>
				<span>，一个其貌不扬的家伙，引无数菜鸟竟折腰，小虾我当初也没少犯迷糊，秉着“</span>
				<span lang="EN-US">
						<br />
				</span>
				<span>辛苦我一个，幸福千万人”的伟大思想，我决定将其尽可能详细的总结一下。</span>
				<span lang="EN-US">
						<br />
				</span>
				<span>但当我总结的时候才发现，这个问题既可以简单，又可以复杂，所以本文有的地方并不</span>
				<span lang="EN-US">
						<br />
				</span>
				<span>适合初学者，甚至都没有必要大作文章。但如果你想“知其然，更知其所以然”的话，</span>
				<span lang="EN-US">
						<br />
				</span>
				<span>那么这篇文章对你或许有所帮助。</span>
				<span lang="EN-US">
						<br />
				</span>
				<span>菜鸟我对</span>
				<span lang="EN-US">C++</span>
				<span>的掌握尚未深入，其中不乏错误，欢迎各位指正啊</span>
		</p>
		<p>
				<span lang="EN-US">1. </span>
				<span>定义：</span>
				<span lang="EN-US">
						<br />sizeof</span>
				<span>是何方神圣</span>
				<span lang="EN-US">sizeof</span>
				<span>乃</span>
				<span lang="EN-US">C/C++</span>
				<span>中的一个操作符（</span>
				<span lang="EN-US">operator</span>
				<span>）是也，简单的说其作</span>
				<span lang="EN-US">
						<br />
				</span>
				<span>用就是返回一个对象或者类型所占的内存字节数。</span>
				<span lang="EN-US">
						<br />MSDN</span>
				<span>上的解释为：</span>
				<span lang="EN-US">
						<br />The sizeof keyword gives the amount of storage, in bytes, associated with a<br />variable or a type (including aggregate types). <br />This keyword returns a value of type size_t.<br /></span>
				<span>其返回值类型为</span>
				<span lang="EN-US">size_t</span>
				<span>，在头文件</span>
				<span lang="EN-US">stddef.h</span>
				<span>中定义。这是一个依赖于编译系统的值，一</span>
				<span lang="EN-US">
						<br />
				</span>
				<span>般定义为</span>
				<span lang="EN-US">
						<br />typedef unsigned int size_t;<br /></span>
				<span>世上编译器林林总总，但作为一个规范，它们都会保证</span>
				<span lang="EN-US">char</span>
				<span>、</span>
				<span lang="EN-US">signed char</span>
				<span>和</span>
				<span lang="EN-US">unsigned<br />char</span>
				<span>的</span>
				<span lang="EN-US">sizeof</span>
				<span>值为</span>
				<span lang="EN-US">1</span>
				<span>，毕竟</span>
				<span lang="EN-US">char</span>
				<span>是我们编程能用的最小数据类型。</span>
				<span lang="EN-US">
						<br />2. </span>
				<span>语法：</span>
				<span lang="EN-US">
						<br />sizeof</span>
				<span>有三种语法形式，如下：</span>
				<span lang="EN-US">
						<br />1) sizeof( object ); // sizeof( </span>
				<span>对象</span>
				<span lang="EN-US">);<br />2) sizeof( type_name ); // sizeof( </span>
				<span>类型</span>
				<span lang="EN-US">);<br />3) sizeof object; // sizeof </span>
				<span>对象</span>
				<span lang="EN-US">;<br /></span>
				<span>所以，</span>
				<span lang="EN-US">
						<br />int i;<br />sizeof( i ); // ok<br />sizeof i; // ok<br />sizeof( int ); // ok<br />sizeof int; // error<br /></span>
				<span>既然写法</span>
				<span lang="EN-US">3</span>
				<span>可以用写法</span>
				<span lang="EN-US">1</span>
				<span>代替，为求形式统一以及减少我们大脑的负担，第</span>
				<span lang="EN-US">3</span>
				<span>种写法，忘</span>
				<span lang="EN-US">
						<br />
				</span>
				<span>掉它吧！</span>
				<span lang="EN-US">
						<br />
				</span>
				<span>实际上，</span>
				<span lang="EN-US">sizeof</span>
				<span>计算对象的大小也是转换成对对象类型的计算，也就是说，同种类型的</span>
				<span lang="EN-US">
						<br />
				</span>
				<span>不同对象其</span>
				<span lang="EN-US">sizeof</span>
				<span>值都是一致的。这里，对象可以进一步延伸至表达式，即</span>
				<span lang="EN-US">sizeof</span>
				<span>可以</span>
				<span lang="EN-US">
						<br />
				</span>
				<span>对一个表达式求值，编译器根据表达式的最终结果类型来确定大小，一般不会对表达式</span>
				<span lang="EN-US">
						<br />
				</span>
				<span>进行计算。如：</span>
				<span lang="EN-US">
						<br />sizeof( 2 );// 2</span>
				<span>的类型为</span>
				<span lang="EN-US">int</span>
				<span>，所以等价于</span>
				<span lang="EN-US">sizeof( int );<br />sizeof( 2 + 3.14 ); // 3.14</span>
				<span>的类型为</span>
				<span lang="EN-US">double</span>
				<span>，</span>
				<span lang="EN-US">2</span>
				<span>也会被提升成</span>
				<span lang="EN-US">double</span>
				<span>类型，所以等价</span>
				<span lang="EN-US">
						<br />
				</span>
				<span>于</span>
				<span lang="EN-US">sizeof( double );<br />sizeof</span>
				<span>也可以对一个函数调用求值，其结果是函数返回类型的大小，函数并不会被调用</span>
				<span lang="EN-US">
						<br />
				</span>
				<span>，我们来看一个完整的例子：</span>
				<span lang="EN-US">
						<br />char foo()<br />{<br />printf("foo() has been called.\n");<br />return 'a';<br />}<br />int main()<br />{<br />size_t sz = sizeof( foo() ); // foo() </span>
				<span>的返回值类型为</span>
				<span lang="EN-US">char</span>
				<span>，所以</span>
				<span lang="EN-US">sz = sizeof(<br />char )</span>
				<span>，</span>
				<span lang="EN-US">foo()</span>
				<span>并不会被调用</span>
				<span lang="EN-US">
						<br />printf("sizeof( foo() ) = %d\n", sz);<br />}<br />C99</span>
				<span>标准规定，函数、不能确定类型的表达式以及位域（</span>
				<span lang="EN-US">bit-field</span>
				<span>）成员不能被计算</span>
				<span lang="EN-US">s<br />izeof</span>
				<span>值，即下面这些写法都是错误的：</span>
				<span lang="EN-US">
						<br />sizeof( foo );// error<br />void foo2() { }<br />sizeof( foo2() );// error<br />struct S<br />{<br />unsigned int f1 : 1;<br />unsigned int f2 : 5;<br />unsigned int f3 : 12;<br />};<br />sizeof( S.f1 );// error<br />3. sizeof</span>
				<span>的常量性</span>
				<span lang="EN-US">
						<br />sizeof</span>
				<span>的计算发生在编译时刻，所以它可以被当作常量表达式使用，如：</span>
				<span lang="EN-US">
						<br />char ary[ sizeof( int ) * 10 ]; // ok<br /></span>
				<span>最新的</span>
				<span lang="EN-US">C99</span>
				<span>标准规定</span>
				<span lang="EN-US">sizeof</span>
				<span>也可以在运行时刻进行计算，如下面的程序在</span>
				<span lang="EN-US">Dev-C++</span>
				<span>中可以</span>
				<span lang="EN-US">
						<br />
				</span>
				<span>正确执行：</span>
				<span lang="EN-US">
						<br />int n;<br />n = 10; // n</span>
				<span>动态赋值</span>
				<span lang="EN-US">
						<br />char ary[n]; // C99</span>
				<span>也支持数组的动态定义</span>
				<span lang="EN-US">
						<br />printf("%d\n", sizeof(ary)); // ok. </span>
				<span>输出</span>
				<span lang="EN-US">10<br /></span>
				<span>但在没有完全实现</span>
				<span lang="EN-US">C99</span>
				<span>标准的编译器中就行不通了，上面的代码在</span>
				<span lang="EN-US">VC6</span>
				<span>中就通不过编译。</span>
				<span lang="EN-US">
						<br />
				</span>
				<span>所以我们最好还是认为</span>
				<span lang="EN-US">sizeof</span>
				<span>是在编译期执行的，这样不会带来错误，让程序的可移植</span>
				<span lang="EN-US">
						<br />
				</span>
				<span>性强些。</span>
				<span lang="EN-US">
						<br />4. </span>
				<span>基本数据类型的</span>
				<span lang="EN-US">sizeof<br /></span>
				<span>这里的基本数据类型指</span>
				<span lang="EN-US">short</span>
				<span>、</span>
				<span lang="EN-US">int</span>
				<span>、</span>
				<span lang="EN-US">long</span>
				<span>、</span>
				<span lang="EN-US">float</span>
				<span>、</span>
				<span lang="EN-US">double</span>
				<span>这样的简单内置数据类型，</span>
				<span lang="EN-US">
						<br />
				</span>
				<span>由于它们都是和系统相关的，所以在不同的系统下取值可能不同，这务必引起我们的注</span>
				<span lang="EN-US">
						<br />
				</span>
				<span>意，尽量不要在这方面给自己程序的移植造成麻烦。</span>
				<span lang="EN-US">
						<br />
				</span>
				<span>一般的，在</span>
				<span lang="EN-US">32</span>
				<span>位编译环境中，</span>
				<span lang="EN-US">sizeof(int)</span>
				<span>的取值为</span>
				<span lang="EN-US">4</span>
				<span>。</span>
				<span lang="EN-US">
						<br />5. </span>
				<span>指针变量的</span>
				<span lang="EN-US">sizeof<br /></span>
				<span>学过数据结构的你应该知道指针是一个很重要的概念，它记录了另一个对象的地址。既</span>
				<span lang="EN-US">
						<br />
				</span>
				<span>然是来存放地址的，那么它当然等于计算机内部地址总线的宽度。所以在</span>
				<span lang="EN-US">32</span>
				<span>位计算机中</span>
				<span lang="EN-US">
						<br />
				</span>
				<span>，一个指针变量的返回值必定是</span>
				<span lang="EN-US">4</span>
				<span>（注意结果是以字节为单位），可以预计，在将来的</span>
				<span lang="EN-US">6<br />4</span>
				<span>位系统中指针变量的</span>
				<span lang="EN-US">sizeof</span>
				<span>结果为</span>
				<span lang="EN-US">8</span>
				<span>。</span>
				<span lang="EN-US">
						<br />char* pc = "abc";<br />int* pi;<br />string* ps;<br />char** ppc = &amp;pc;<br />void (*pf)();// </span>
				<span>函数指针</span>
				<span lang="EN-US">
						<br />sizeof( pc ); // </span>
				<span>结果为</span>
				<span lang="EN-US">4<br />sizeof( pi ); // </span>
				<span>结果为</span>
				<span lang="EN-US">4<br />sizeof( ps ); // </span>
				<span>结果为</span>
				<span lang="EN-US">4<br />sizeof( ppc ); // </span>
				<span>结果为</span>
				<span lang="EN-US">4<br />sizeof( pf );// </span>
				<span>结果为</span>
				<span lang="EN-US">4<br /></span>
				<span>指针变量的</span>
				<span lang="EN-US">sizeof</span>
				<span>值与指针所指的对象没有任何关系，正是由于所有的指针变量所占内</span>
				<span lang="EN-US">
						<br />
				</span>
				<span>存大小相等，所以</span>
				<span lang="EN-US">MFC</span>
				<span>消息处理函数使用两个参数</span>
				<span lang="EN-US">WPARAM</span>
				<span>、</span>
				<span lang="EN-US">LPARAM</span>
				<span>就能传递各种复杂的消</span>
				<span lang="EN-US">
						<br />
				</span>
				<span>息结构（使用指向结构体的指针）。</span>
				<span lang="EN-US">
						<br />6. </span>
				<span>数组的</span>
				<span lang="EN-US">sizeof<br /></span>
				<span>数组的</span>
				<span lang="EN-US">sizeof</span>
				<span>值等于数组所占用的内存字节数，如：</span>
				<span lang="EN-US">
						<br />char a1[] = "abc";<br />int a2[3];<br />sizeof( a1 ); // </span>
				<span>结果为</span>
				<span lang="EN-US">4</span>
				<span>，字符</span>
				<span>
				</span>
				<span>末尾还存在一个</span>
				<span lang="EN-US">NULL</span>
				<span>终止符</span>
				<span lang="EN-US">
						<br />sizeof( a2 ); // </span>
				<span>结果为</span>
				<span lang="EN-US">3*4=12</span>
				<span>（依赖于</span>
				<span lang="EN-US">int</span>
				<span>）</span>
				<span lang="EN-US">
						<br />
				</span>
				<span>一些朋友刚开始时把</span>
				<span lang="EN-US">sizeof</span>
				<span>当作了求数组元素的个数，现在，你应该知道这是不对的，</span>
				<span lang="EN-US">
						<br />
				</span>
				<span>那么应该怎么求数组元素的个数呢</span>
				<span lang="EN-US">Easy</span>
				<span>，通常有下面两种写法：</span>
				<span lang="EN-US">
						<br />int c1 = sizeof( a1 ) / sizeof( char ); // </span>
				<span>总长度</span>
				<span lang="EN-US">/</span>
				<span>单个元素的长度</span>
				<span lang="EN-US">
						<br />int c2 = sizeof( a1 ) / sizeof( a1[0] ); // </span>
				<span>总长度</span>
				<span lang="EN-US">/</span>
				<span>第一个元素的长度</span>
				<span lang="EN-US">
						<br />
				</span>
				<span>写到这里，提一问，下面的</span>
				<span lang="EN-US">c3</span>
				<span>，</span>
				<span lang="EN-US">c4</span>
				<span>值应该是多少呢</span>
				<span lang="EN-US">
						<br />void foo3(char a3[3])<br />{<br />int c3 = sizeof( a3 ); // c3 == <br />}<br />void foo4(char a4[])<br />{<br />int c4 = sizeof( a4 ); // c4 == <br />}<br /></span>
				<span>也许当你试图回答</span>
				<span lang="EN-US">c4</span>
				<span>的值时已经意识到</span>
				<span lang="EN-US">c3</span>
				<span>答错了，是的，</span>
				<span lang="EN-US">c3!=3</span>
				<span>。这里函数参数</span>
				<span lang="EN-US">a3</span>
				<span>已不</span>
				<span lang="EN-US">
						<br />
				</span>
				<span>再是数组类型，而是蜕变成指针，相当于</span>
				<span lang="EN-US">char* a3</span>
				<span>，为什么仔细想想就不难明白，我</span>
				<span lang="EN-US">
						<br />
				</span>
				<span>们调用函数</span>
				<span lang="EN-US">foo1</span>
				<span>时，程序会在栈上分配一个大小为</span>
				<span lang="EN-US">3</span>
				<span>的数组吗不会！数组是“传址”的</span>
				<span lang="EN-US">
						<br />
				</span>
				<span>，调用者只需将实参的地址传递过去，所以</span>
				<span lang="EN-US">a3</span>
				<span>自然为指针类型（</span>
				<span lang="EN-US">char*</span>
				<span>），</span>
				<span lang="EN-US">c3</span>
				<span>的值也就为</span>
				<span lang="EN-US">
						<br />4</span>
				<span>。</span>
				<span lang="EN-US">
						<br />7. </span>
				<span>结构体的</span>
				<span lang="EN-US">sizeof<br /></span>
				<span>这是初学者问得最多的一个问题，所以这里有必要多费点笔墨。让我们先看一个结构体</span>
				<span lang="EN-US">
						<br />
				</span>
				<span>：</span>
				<span lang="EN-US">
						<br />struct S1<br />{<br />char c;<br />int i;<br />};<br /></span>
				<span>问</span>
				<span lang="EN-US">sizeof(s1)</span>
				<span>等于多少聪明的你开始思考了，</span>
				<span lang="EN-US">char</span>
				<span>占</span>
				<span lang="EN-US">1</span>
				<span>个字节，</span>
				<span lang="EN-US">int</span>
				<span>占</span>
				<span lang="EN-US">4</span>
				<span>个字节，那么</span>
				<span lang="EN-US">
						<br />
				</span>
				<span>加起来就应该是</span>
				<span lang="EN-US">5</span>
				<span>。是这样吗你在你机器上试过了吗也许你是对的，但很可能你是错</span>
				<span lang="EN-US">
						<br />
				</span>
				<span>的！</span>
				<span lang="EN-US">VC6</span>
				<span>中按默认设置得到的结果为</span>
				<span lang="EN-US">8</span>
				<span>。</span>
				<span lang="EN-US">
						<br />Why</span>
				<span>为什么受伤的总是我</span>
				<span lang="EN-US">
						<br />
				</span>
				<span>请不要沮丧，我们来好好琢磨一下</span>
				<span lang="EN-US">sizeof</span>
				<span>的定义——</span>
				<span lang="EN-US">sizeof</span>
				<span>的结果等于对象或者类型所</span>
				<span lang="EN-US">
						<br />
				</span>
				<span>占的内存字节数，好吧，那就让我们来看看</span>
				<span lang="EN-US">S1</span>
				<span>的内存分配情况：</span>
				<span lang="EN-US">
						<br />S1 s1 = { 'a', 0xFFFFFFFF };<br /></span>
				<span>定义上面的变量后，加上断点，运行程序，观察</span>
				<span lang="EN-US">s1</span>
				<span>所在的内存，你发现了什么</span>
				<span lang="EN-US">
						<br />
				</span>
				<span>以我的</span>
				<span lang="EN-US">VC6.0</span>
				<span>为例，</span>
				<span lang="EN-US">s1</span>
				<span>的地址为</span>
				<span lang="EN-US">0x0012FF78</span>
				<span>，其数据内容如下：</span>
				<span lang="EN-US">
						<br />0012FF78: 61 CC CC CC FF FF FF FF<br /></span>
				<span>发现了什么怎么中间夹杂了</span>
				<span lang="EN-US">3</span>
				<span>个字节的</span>
				<span lang="EN-US">CC</span>
				<span>看看</span>
				<span lang="EN-US">MSDN</span>
				<span>上的说明：</span>
				<span lang="EN-US">
						<br />When applied to a structure type or variable, sizeof returns the actual siz<br />e, which may include padding bytes inserted for alignment.<br /></span>
				<span>原来如此，这就是传说中的字节对齐啊！一个重要的话题出现了。</span>
				<span lang="EN-US">
						<br />
				</span>
				<span>为什么需要字节对齐计算机组成原理教导我们这样有助于加快计算机的取数速度，否</span>
				<span lang="EN-US">
						<br />
				</span>
				<span>则就得多花指令周期了。为此，编译器默认会对结构体进行处理（实际上其它地方的数</span>
				<span lang="EN-US">
						<br />
				</span>
				<span>据变量也是如此），让宽度为</span>
				<span lang="EN-US">2</span>
				<span>的基本数据类型（</span>
				<span lang="EN-US">short</span>
				<span>等）都位于能被</span>
				<span lang="EN-US">2</span>
				<span>整除的地址上，</span>
				<span lang="EN-US">
						<br />
				</span>
				<span>让宽度为</span>
				<span lang="EN-US">4</span>
				<span>的基本数据类型（</span>
				<span lang="EN-US">int</span>
				<span>等）都位于能被</span>
				<span lang="EN-US">4</span>
				<span>整除的地址上，以此类推。这样，两个</span>
				<span lang="EN-US">
						<br />
				</span>
				<span>数中间就可能需要加入填充字节，所以整个结构体的</span>
				<span lang="EN-US">sizeof</span>
				<span>值就增长了。</span>
				<span lang="EN-US">
						<br />
				</span>
				<span>让我们交换一下</span>
				<span lang="EN-US">S1</span>
				<span>中</span>
				<span lang="EN-US">char</span>
				<span>与</span>
				<span lang="EN-US">int</span>
				<span>的位置：</span>
				<span lang="EN-US">
						<br />struct S2<br />{<br />int i;<br />char c;<br />};<br /></span>
				<span>看看</span>
				<span lang="EN-US">sizeof(S2)</span>
				<span>的结果为多少，怎么还是</span>
				<span lang="EN-US">8</span>
				<span>再看看内存，原来成员</span>
				<span lang="EN-US">c</span>
				<span>后面仍然有</span>
				<span lang="EN-US">3</span>
				<span>个填</span>
				<span lang="EN-US">
						<br />
				</span>
				<span>充字节，这又是为什么啊别着急，下面总结规律。</span>
		</p>
		<p>
				<span>字节对齐的细节和编译器实现相关，但一般而言，满足三个准则：</span>
				<span lang="EN-US">
						<br />1) </span>
				<span>结构体变量的首地址能够被其最宽基本类型成员的大小所整除；</span>
				<span lang="EN-US">
						<br />2) </span>
				<span>结构体每个成员相对于结构体首地址的偏移量（</span>
				<span lang="EN-US">offset</span>
				<span>）都是成员大小的整数倍，</span>
				<span lang="EN-US">
						<br />
				</span>
				<span>如有需要编译器会在成员之间加上填充字节（</span>
				<span lang="EN-US">internal adding</span>
				<span>）；</span>
				<span lang="EN-US">
						<br />3) </span>
				<span>结构体的总大小为结构体最宽基本类型成员大小的整数倍，如有需要编译器会在最</span>
				<span lang="EN-US">
						<br />
				</span>
				<span>末一个成员之后加上填充字节（</span>
				<span lang="EN-US">trailing padding</span>
				<span>）。</span>
				<span lang="EN-US">
						<br />
				</span>
				<span>对于上面的准则，有几点需要说明：</span>
				<span lang="EN-US">
						<br />1) </span>
				<span>前面不是说结构体成员的地址是其大小的整数倍，怎么又说到偏移量了呢因为有</span>
				<span lang="EN-US">
						<br />
				</span>
				<span>了第</span>
				<span lang="EN-US">1</span>
				<span>点存在，所以我们就可以只考虑成员的偏移量，这样思考起来简单。想想为什么。</span>
		</p>
		<p>
				<span>结构体某个成员相对于结构体首地址的偏移量可以通过宏</span>
				<span lang="EN-US">offsetof()</span>
				<span>来获得，这个宏也</span>
				<span lang="EN-US">
						<br />
				</span>
				<span>在</span>
				<span lang="EN-US">stddef.h</span>
				<span>中定义，如下：</span>
				<span lang="EN-US">
						<br />#define offsetof(s,m) (size_t)&amp;(((s *)0)-&gt;m)<br /></span>
				<span>例如，想要获得</span>
				<span lang="EN-US">S2</span>
				<span>中</span>
				<span lang="EN-US">c</span>
				<span>的偏移量，方法为</span>
				<span lang="EN-US">
						<br />size_t pos = offsetof(S2, c);// pos</span>
				<span>等于</span>
				<span lang="EN-US">4<br />2) </span>
				<span>基本类型是指前面提到的像</span>
				<span lang="EN-US">char</span>
				<span>、</span>
				<span lang="EN-US">short</span>
				<span>、</span>
				<span lang="EN-US">int</span>
				<span>、</span>
				<span lang="EN-US">float</span>
				<span>、</span>
				<span lang="EN-US">double</span>
				<span>这样的内置数据类型</span>
				<span lang="EN-US">
						<br />
				</span>
				<span>，这里所说的“数据宽度”就是指其</span>
				<span lang="EN-US">sizeof</span>
				<span>的大小。由于结构体的成员可以是复合类型</span>
				<span lang="EN-US">
						<br />
				</span>
				<span>，比如另外一个结构体，所以在寻找最宽基本类型成员时，应当包括复合类型成员的子</span>
				<span lang="EN-US">
						<br />
				</span>
				<span>成员，而不是把复合成员看成是一个整体。但在确定复合类型成员的偏移位置时则是将</span>
				<span lang="EN-US">
						<br />
				</span>
				<span>复合类型作为整体看待。</span>
				<span lang="EN-US">
						<br />
				</span>
				<span>这里叙述起来有点拗口，思考起来也有点挠头，还是让我们看看例子吧（具体数值仍以</span>
				<span lang="EN-US">
						<br />VC6</span>
				<span>为例，以后不再说明）：</span>
				<span lang="EN-US">
						<br />struct S3<br />{<br />char c1;<br />S1 s;<br />char c2<br />};<br />S1</span>
				<span>的最宽简单成员的类型为</span>
				<span lang="EN-US">int</span>
				<span>，</span>
				<span lang="EN-US">S3</span>
				<span>在考虑最宽简单类型成员时是将</span>
				<span lang="EN-US">S1</span>
				<span>“打散”看的，</span>
				<span lang="EN-US">
						<br />
				</span>
				<span>所以</span>
				<span lang="EN-US">S3</span>
				<span>的最宽简单类型为</span>
				<span lang="EN-US">int</span>
				<span>，这样，通过</span>
				<span lang="EN-US">S3</span>
				<span>定义的变量，其存储空间首地址需要被</span>
				<span lang="EN-US">4</span>
				<span>整</span>
				<span lang="EN-US">
						<br />
				</span>
				<span>除，整个</span>
				<span lang="EN-US">sizeof(S3)</span>
				<span>的值也应该被</span>
				<span lang="EN-US">4</span>
				<span>整除。</span>
				<span lang="EN-US">
						<br />c1</span>
				<span>的偏移量为</span>
				<span lang="EN-US">0</span>
				<span>，</span>
				<span lang="EN-US">s</span>
				<span>的偏移量呢这时</span>
				<span lang="EN-US">s</span>
				<span>是一个整体，它作为结构体变量也满足前面三个</span>
				<span lang="EN-US">
						<br />
				</span>
				<span>准则，所以其大小为</span>
				<span lang="EN-US">8</span>
				<span>，偏移量为</span>
				<span lang="EN-US">4</span>
				<span>，</span>
				<span lang="EN-US">c1</span>
				<span>与</span>
				<span lang="EN-US">s</span>
				<span>之间便需要</span>
				<span lang="EN-US">3</span>
				<span>个填充字节，而</span>
				<span lang="EN-US">c2</span>
				<span>与</span>
				<span lang="EN-US">s</span>
				<span>之间就不需</span>
				<span lang="EN-US">
						<br />
				</span>
				<span>要了，所以</span>
				<span lang="EN-US">c2</span>
				<span>的偏移量为</span>
				<span lang="EN-US">12</span>
				<span>，算上</span>
				<span lang="EN-US">c2</span>
				<span>的大小为</span>
				<span lang="EN-US">13</span>
				<span>，</span>
				<span lang="EN-US">13</span>
				<span>是不能被</span>
				<span lang="EN-US">4</span>
				<span>整除的，这样末尾还得补</span>
				<span lang="EN-US">
						<br />
				</span>
				<span>上</span>
				<span lang="EN-US">3</span>
				<span>个填充字节。最后得到</span>
				<span lang="EN-US">sizeof(S3)</span>
				<span>的值为</span>
				<span lang="EN-US">16</span>
				<span>。</span>
				<span lang="EN-US">
						<br />
				</span>
				<span>通过上面的叙述，我们可以得到一个公式：</span>
				<span lang="EN-US">
						<br />
				</span>
				<span>结构体的大小等于最后一个成员的偏移量加上其大小再加上末尾的填充字节数目，即：</span>
		</p>
		<p>
				<span lang="EN-US">sizeof( struct ) = offsetof( last item ) + sizeof( last item ) + sizeof( tr<br />ailing padding )</span>
		</p>
		<p>
				<span>到这里，朋友们应该对结构体的</span>
				<span lang="EN-US">sizeof</span>
				<span>有了一个全新的认识，但不要高兴得太早，有</span>
				<span lang="EN-US">
						<br />
				</span>
				<span>一个影响</span>
				<span lang="EN-US">sizeof</span>
				<span>的重要参量还未被提及，那便是编译器的</span>
				<span lang="EN-US">pack</span>
				<span>指令。它是用来调整结构</span>
				<span lang="EN-US">
						<br />
				</span>
				<span>体对齐方式的，不同编译器名称和用法略有不同，</span>
				<span lang="EN-US">VC6</span>
				<span>中通过</span>
				<span lang="EN-US">#pragma pack</span>
				<span>实现，也可以</span>
				<span lang="EN-US">
						<br />
				</span>
				<span>直接修改</span>
				<span lang="EN-US">/Zp</span>
				<span>编译开关。</span>
				<span lang="EN-US">#pragma pack</span>
				<span>的基本用法为：</span>
				<span lang="EN-US">#pragma pack( n )</span>
				<span>，</span>
				<span lang="EN-US">n</span>
				<span>为字节对齐</span>
				<span lang="EN-US">
						<br />
				</span>
				<span>数，其取值为</span>
				<span lang="EN-US">1</span>
				<span>、</span>
				<span lang="EN-US">2</span>
				<span>、</span>
				<span lang="EN-US">4</span>
				<span>、</span>
				<span lang="EN-US">8</span>
				<span>、</span>
				<span lang="EN-US">16</span>
				<span>，默认是</span>
				<span lang="EN-US">8</span>
				<span>，如果这个值比结构体成员的</span>
				<span lang="EN-US">sizeof</span>
				<span>值小，那么</span>
				<span lang="EN-US">
						<br />
				</span>
				<span>该成员的偏移量应该以此值为准，即是说，结构体成员的偏移量应该取二者的最小值，</span>
				<span lang="EN-US">
						<br />
				</span>
				<span>公式如下：</span>
				<span lang="EN-US">
						<br />offsetof( item ) = min( n, sizeof( item ) )<br /></span>
				<span>再看示例：</span>
				<span lang="EN-US">
						<br />#pragma pack(push) // </span>
				<span>将当前</span>
				<span lang="EN-US">pack</span>
				<span>设置压栈保存</span>
				<span lang="EN-US">
						<br />#pragma pack(2)// </span>
				<span>必须在结构体定义之前使用</span>
				<span lang="EN-US">
						<br />struct S1<br />{<br />char c;<br />int i;<br />};<br />struct S3<br />{<br />char c1;<br />S1 s;<br />char c2<br />};<br />#pragma pack(pop) // </span>
				<span>恢复先前的</span>
				<span lang="EN-US">pack</span>
				<span>设置</span>
				<span lang="EN-US">
						<br />
				</span>
				<span>计算</span>
				<span lang="EN-US">sizeof(S1)</span>
				<span>时，</span>
				<span lang="EN-US">min(2, sizeof(i))</span>
				<span>的值为</span>
				<span lang="EN-US">2</span>
				<span>，所以</span>
				<span lang="EN-US">i</span>
				<span>的偏移量为</span>
				<span lang="EN-US">2</span>
				<span>，加上</span>
				<span lang="EN-US">sizeof(i)<br /></span>
				<span>等于</span>
				<span lang="EN-US">6</span>
				<span>，能够被</span>
				<span lang="EN-US">2</span>
				<span>整除，所以整个</span>
				<span lang="EN-US">S1</span>
				<span>的大小为</span>
				<span lang="EN-US">6</span>
				<span>。</span>
				<span lang="EN-US">
						<br />
				</span>
				<span>同样，对于</span>
				<span lang="EN-US">sizeof(S3)</span>
				<span>，</span>
				<span lang="EN-US">s</span>
				<span>的偏移量为</span>
				<span lang="EN-US">2</span>
				<span>，</span>
				<span lang="EN-US">c2</span>
				<span>的偏移量为</span>
				<span lang="EN-US">8</span>
				<span>，加上</span>
				<span lang="EN-US">sizeof(c2)</span>
				<span>等于</span>
				<span lang="EN-US">9</span>
				<span>，不能</span>
				<span lang="EN-US">
						<br />
				</span>
				<span>被</span>
				<span lang="EN-US">2</span>
				<span>整除，添加一个填充字节，所以</span>
				<span lang="EN-US">sizeof(S3)</span>
				<span>等于</span>
				<span lang="EN-US">10</span>
				<span>。</span>
				<span lang="EN-US">
						<br />
				</span>
				<span>现在，朋友们可以轻松的出一口气了，</span>
				<span lang="EN-US">:)<br /></span>
				<span>还有一点要注意，“空结构体”（不含数据成员）的大小不为</span>
				<span lang="EN-US">0</span>
				<span>，而是</span>
				<span lang="EN-US">1</span>
				<span>。试想一个“不</span>
				<span lang="EN-US">
						<br />
				</span>
				<span>占空间”的变量如何被取地址、两个不同的“空结构体”变量又如何得以区分呢于是</span>
				<span lang="EN-US">
						<br />
				</span>
				<span>，“空结构体”变量也得被存储，这样编译器也就只能为其分配一个字节的空间用于占</span>
				<span lang="EN-US">
						<br />
				</span>
				<span>位了。如下：</span>
				<span lang="EN-US">
						<br />struct S5 { };<br />sizeof( S5 ); // </span>
				<span>结果为</span>
				<span lang="EN-US">1</span>
		</p>
		<p>
				<span lang="EN-US">8. </span>
				<span>含位域结构体的</span>
				<span lang="EN-US">sizeof<br /></span>
				<span>前面已经说过，位域成员不能单独被取</span>
				<span lang="EN-US">sizeof</span>
				<span>值，我们这里要讨论的是含有位域的结构</span>
				<span lang="EN-US">
						<br />
				</span>
				<span>体的</span>
				<span lang="EN-US">sizeof</span>
				<span>，只是考虑到其特殊性而将其专门列了出来。</span>
				<span lang="EN-US">
						<br />C99</span>
				<span>规定</span>
				<span lang="EN-US">int</span>
				<span>、</span>
				<span lang="EN-US">unsigned int</span>
				<span>和</span>
				<span lang="EN-US">bool</span>
				<span>可以作为位域类型，但编译器几乎都对此作了扩展，</span>
				<span lang="EN-US">
						<br />
				</span>
				<span>允许其它类型类型的存在。</span>
				<span lang="EN-US">
						<br />
				</span>
				<span>使用位域的主要目的是压缩存储，其大致规则为：</span>
				<span lang="EN-US">
						<br />1) </span>
				<span>如果相邻位域字段的类型相同，且其位宽之和小于类型的</span>
				<span lang="EN-US">sizeof</span>
				<span>大小，则后面的字</span>
				<span lang="EN-US">
						<br />
				</span>
				<span>段将紧邻前一个字段存储，直到不能容纳为止；</span>
				<span lang="EN-US">
						<br />2) </span>
				<span>如果相邻位域字段的类型相同，但其位宽之和大于类型的</span>
				<span lang="EN-US">sizeof</span>
				<span>大小，则后面的字</span>
				<span lang="EN-US">
						<br />
				</span>
				<span>段将从新的存储单元开始，其偏移量为其类型大小的整数倍；</span>
				<span lang="EN-US">
						<br />3) </span>
				<span>如果相邻的位域字段的类型不同，则各编译器的具体实现有差异，</span>
				<span lang="EN-US">VC6</span>
				<span>采取不压缩方</span>
				<span lang="EN-US">
						<br />
				</span>
				<span>式，</span>
				<span lang="EN-US">Dev-C++</span>
				<span>采取压缩方式；</span>
				<span lang="EN-US">
						<br />4) </span>
				<span>如果位域字段之间穿插着非位域字段，则不进行压缩；</span>
				<span lang="EN-US">
						<br />5) </span>
				<span>整个结构体的总大小为最宽基本类型成员大小的整数倍。</span>
		</p>
		<p>
				<span>还是让我们来看看例子。</span>
				<span lang="EN-US">
						<br />
				</span>
				<span>示例</span>
				<span lang="EN-US">1</span>
				<span>：</span>
				<span lang="EN-US">
						<br />struct BF1<br />{<br />char f1 : 3;<br />char f2 : 4;<br />char f3 : 5;<br />};<br /></span>
				<span>其内存布局为：</span>
				<span lang="EN-US">
						<br />|_f1__|__f2__|_|____f3___|____|<br />|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|<br />0 3 7 8 1316<br /></span>
				<span>位域类型为</span>
				<span lang="EN-US">char</span>
				<span>，第</span>
				<span lang="EN-US">1</span>
				<span>个字节仅能容纳下</span>
				<span lang="EN-US">f1</span>
				<span>和</span>
				<span lang="EN-US">f2</span>
				<span>，所以</span>
				<span lang="EN-US">f2</span>
				<span>被压缩到第</span>
				<span lang="EN-US">1</span>
				<span>个字节中，而</span>
				<span lang="EN-US">f3</span>
				<span>只</span>
				<span lang="EN-US">
						<br />
				</span>
				<span>能从下一个字节开始。因此</span>
				<span lang="EN-US">sizeof(BF1)</span>
				<span>的结果为</span>
				<span lang="EN-US">2</span>
				<span>。</span>
				<span lang="EN-US">
						<br />
				</span>
				<span>示例</span>
				<span lang="EN-US">2</span>
				<span>：</span>
				<span lang="EN-US">
						<br />struct BF2<br />{<br />char f1 : 3;<br />short f2 : 4;<br />char f3 : 5;<br />};<br /></span>
				<span>由于相邻位域类型不同，在</span>
				<span lang="EN-US">VC6</span>
				<span>中其</span>
				<span lang="EN-US">sizeof</span>
				<span>为</span>
				<span lang="EN-US">6</span>
				<span>，在</span>
				<span lang="EN-US">Dev-C++</span>
				<span>中为</span>
				<span lang="EN-US">2</span>
				<span>。</span>
				<span lang="EN-US">
						<br />
				</span>
				<span>示例</span>
				<span lang="EN-US">3</span>
				<span>：</span>
				<span lang="EN-US">
						<br />struct BF3<br />{<br />char f1 : 3;<br />char f2;<br />char f3 : 5;<br />};<br /></span>
				<span>非位域字段穿插在其中，不会产生压缩，在</span>
				<span lang="EN-US">VC6</span>
				<span>和</span>
				<span lang="EN-US">Dev-C++</span>
				<span>中得到的大小均为</span>
				<span lang="EN-US">3</span>
				<span>。</span>
				<span lang="EN-US">
						<br />9. </span>
				<span>联合体的</span>
				<span lang="EN-US">sizeof<br /></span>
				<span>结构体在内存组织上是顺序式的，联合体则是重叠式，各成员共享一段内存，所以整个</span>
				<span lang="EN-US">
						<br />
				</span>
				<span>联合体的</span>
				<span lang="EN-US">sizeof</span>
				<span>也就是每个成员</span>
				<span lang="EN-US">sizeof</span>
				<span>的最大值。结构体的成员也可以是复合类型，这</span>
				<span lang="EN-US">
						<br />
				</span>
				<span>里，复合类型成员是被作为整体考虑的。</span>
				<span lang="EN-US">
						<br />
				</span>
				<span>所以，下面例子中，</span>
				<span lang="EN-US">U</span>
				<span>的</span>
				<span lang="EN-US">sizeof</span>
				<span>值等于</span>
				<span lang="EN-US">sizeof(s)</span>
				<span>。</span>
				<span lang="EN-US">
						<br />union U<br />{<br />int i;<br />char c;<br />S1 s;<br />};</span>
		</p>
<img src ="http://www.cnitblog.com/Winter/aggbug/18869.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/Winter/" target="_blank">winter</a> 2006-11-06 09:28 <a href="http://www.cnitblog.com/Winter/archive/2006/11/06/18869.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>