﻿<?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>C++博客-I love C++</title><link>http://www.cppblog.com/jxgxy/</link><description>I love C++</description><language>zh-cn</language><lastBuildDate>Sun, 19 Apr 2026 13:48:02 GMT</lastBuildDate><pubDate>Sun, 19 Apr 2026 13:48:02 GMT</pubDate><ttl>60</ttl><item><title>闲扯原码、反码、补码</title><link>http://www.cppblog.com/jxgxy/archive/2010/08/25/124682.html</link><dc:creator>eboy</dc:creator><author>eboy</author><pubDate>Wed, 25 Aug 2010 07:26:00 GMT</pubDate><guid>http://www.cppblog.com/jxgxy/archive/2010/08/25/124682.html</guid><wfw:comment>http://www.cppblog.com/jxgxy/comments/124682.html</wfw:comment><comments>http://www.cppblog.com/jxgxy/archive/2010/08/25/124682.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/jxgxy/comments/commentRss/124682.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/jxgxy/services/trackbacks/124682.html</trackback:ping><description><![CDATA[相信大家看到这个标题都不屑一顾，因为在任何一本计算机基础知识书的第一章都有他们的解释，但是在书上我们只能找到一些简单的定义，没次看过之后不久就忘了。最近论坛里有人问起这些概念，看到很多人的回复是以前看过现在忘了去看看某某书之类，很少有给出一个合理的解释。于是本人就开始思考（虽然上帝会发笑，我还是要思考。），于是得出了以下的结论。
<p>&#160;</p>
<p><span><span>&nbsp; &nbsp;&nbsp; </span></span><span>数值在计算机中表示形式为机器数</span><span>,</span><span>计算机只能识别</span><span>0</span><span>和</span><span>1,</span><span>使用的是二进制</span><span>,</span><span>而在日常生活中人们使用的是十进制</span><span>,"</span><span>正如亚里士多德早就指出的那样</span><span>,</span><span>今天十进制的广泛采用</span><span>,</span><span>只不过我们绝大多数人生来具有</span><span>10</span><span>个手指头这个解剖学事实的结果</span><span>.</span><span>尽管在历史上手指计数</span><span>(5,10</span><span>进制</span><span>)</span><span>的实践要比二或三进制计数出现的晚</span><span>."(</span><span>摘自</span><span>&lt;&lt;</span><span>数学发展史</span><span>&gt;&gt;</span><span>有空大家可以看看哦</span><span>~,</span><span>很有意思的</span><span>).</span><span>为了能方便的与二进制转换</span><span>,</span><span>就使用了十六进制</span><span>(2<span> <font size=1>4</font></span>)</span><span>和八进制</span><span>(2<span><font size=1>3</font></span>).</span><span>下面进入正题</span><span>.</span></p>
<p><span>数值有正负之分</span><span>,</span><span>计算机就用一个数的最高位存放符号</span><span>(0</span><span>为正</span><span>,1</span><span>为负</span><span>).</span><span>这就是机器数的原码了</span><span>.</span><span>假设机器能处理的位数为</span><span>8.</span><span>即字长为</span><span>1byte,</span><span>原码能表示数值的范围为</span></p>
<p align=center><span>(-127~-0 +0~127)</span><span>共</span><span>256</span><span>个</span><span>.</span></p>
<p><span>&nbsp;</span><span>有了数值的表示方法就可以对数进行算术运算</span><span>.</span><span>但是很快就发现用带符号位的原码进行乘除运算时结果正确</span><span>,</span><span>而在加减运算的时候就出现了问题</span><span>,</span><span>如下</span><span>: </span><span>假设字长为</span><span>8bits</span></p>
<p><span>( 1 )<span>&nbsp;<sub>10</sub></span>-&nbsp;( 1 )<sub>10</sub>&nbsp;=&nbsp;( 1 )<span><sub>10</sub>&nbsp;</span>+ ( -1 )<span><sub>10</sub>&nbsp;</span>= &nbsp;( 0 )<sub>10</sub></span></p>
<p><span>(00000001)<sub>原</sub>&nbsp;+ (10000001)<sub>原</sub>&nbsp;= (10000010)<span><sub>原</sub>&nbsp;</span>= ( -2 )&nbsp;</span><span>显然不正确</span><span>.</span></p>
<p><span>&nbsp;</span><span>因为在两个整数的加法运算中是没有问题的</span><span>,</span><span>于是就发现问题出现在带符号位的负数身上</span><span>,</span><span>对除符号位外的其余各位逐位取反就产生了反码</span><span>.</span><span>反码的取值空间和原码相同且一一对应</span><span>. </span><span>下面是反码的减法运算</span><span>:</span></p>
<p><span>&nbsp;( 1 )<sub>10</sub>&nbsp;-&nbsp;( 1 )<span>&nbsp;<sub>10</sub></span>=&nbsp;( 1 )<span>&nbsp;<sub>10</sub></span>+ ( -1 )<span>&nbsp;<sub>10</sub></span>= &nbsp;( 0 )<sub>10</sub></span></p>
<p><span>&nbsp;(00000001)<span>&nbsp;<sub>反</sub></span>+ (11111110)<span><sub>反</sub>&nbsp;</span>=&nbsp;(11111111)<sub>反</sub>&nbsp;=&nbsp;( -0 )<span> </span>&nbsp;</span><span>有问题</span><span>.</span></p>
<p><span>( 1 )<span><sub>10</sub></span>&nbsp;-&nbsp;( 2)<span><sub>10</sub>&nbsp;</span>=&nbsp;( 1 )<span><sub>10</sub>&nbsp;</span>+ ( -2 )<span><sub>10</sub>&nbsp;</span>= &nbsp;( -1 )<sub>10</sub></span></p>
<p><span>(00000001)<span>&nbsp;<sub>反</sub></span>+ (11111101)<span><sub>反</sub>&nbsp;</span>=&nbsp;(11111110)<sub>反</sub>&nbsp;=&nbsp;( -1 )&nbsp;</span><span>正确</span></p>
<p><span>问题出现在</span><span>(+0)</span><span>和</span><span>(-0)</span><span>上</span><span>,</span><span>在人们的计算概念中零是没有正负之分的</span><span>.(</span><span>印度人首先将零作为标记并放入运算之中</span><span>,</span><span>包含有零号的印度数学和十进制计数对人类文明的贡献极大</span><span>).</span></p>
<p><span>于是就引入了补码概念</span><span>. </span><span>负数的补码就是对反码加一</span><span>,</span><span>而正数不变</span><span>,</span><span>正数的原码反码补码是一样的</span><span>.</span><span>在补码中用</span><span>(-128)</span><span>代替了</span><span>(-0),</span><span>所以补码的表示范围为</span><span>:</span></p>
<p align=center><span>(-128~0~127)</span><span>共</span><span>256</span><span>个</span><span>.</span></p>
<p><span>注意</span><span>:(-128)</span><span>没有相对应的原码和反码</span><span>, (-128) = (10000000)<span> </span>&nbsp;</span><span>补码的加减运算如下</span><span>:</span></p>
<p><span>( 1 )<span>&nbsp;<sub>10</sub></span>-&nbsp;( 1 )<span>&nbsp;<sub>10</sub></span>=&nbsp;( 1 )<span><sub>10</sub>&nbsp;</span>+ ( -1 )<span><sub>10</sub>&nbsp;</span>= &nbsp;( 0 )<sub>10</sub></span></p>
<p><span>(00000001)<sub>补</sub>&nbsp;+ (11111111)<span><sub>补</sub>&nbsp;</span>=&nbsp;(00000000)<sub>补</sub>&nbsp;= ( 0 )&nbsp;</span><span>正确</span></p>
<p><span>( 1 )<span>&nbsp;<sub>10</sub></span>-&nbsp;( 2)<span>&nbsp;<sub>10</sub></span>=&nbsp;( 1 )<span><sub>10</sub>&nbsp;</span>+ ( -2 )<span><sub>10</sub>&nbsp;</span>= &nbsp;( -1 )<sub>10</sub></span></p>
<p><span>(00000001)<span>&nbsp;<sub>补</sub></span>+ (11111110)<span>&nbsp;<sub>补</sub></span>=&nbsp;(11111111)<span><sub>补</sub>&nbsp;</span>= ( -1 )<span> </span>&nbsp;</span><span>正确</span></p>
<p><span><span>&nbsp;&nbsp; </span></span><span>所以补码的设计目的是</span><span>:</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>⑴使符号位能与有效值部分一起参加运算</span><span>,</span><span>从而简化运算规则</span><span>.</span></p>
<p><span>⑵使减法运算转换为加法运算</span><span>,</span><span>进一步简化计算机中运算器的线路设计</span></p>
<p><span>&nbsp;</span><span>所有这些转换都是在计算机的最底层进行的，而在我们使用的汇编、</span><span>C</span><span>等其他高级语言中使用的都是原码。看了上面这些大家应该对原码、反码、补码有了新的认识了吧！</span></p>
<img src ="http://www.cppblog.com/jxgxy/aggbug/124682.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/jxgxy/" target="_blank">eboy</a> 2010-08-25 15:26 <a href="http://www.cppblog.com/jxgxy/archive/2010/08/25/124682.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>ddkwizard 的安装</title><link>http://www.cppblog.com/jxgxy/archive/2010/08/02/121951.html</link><dc:creator>eboy</dc:creator><author>eboy</author><pubDate>Mon, 02 Aug 2010 05:06:00 GMT</pubDate><guid>http://www.cppblog.com/jxgxy/archive/2010/08/02/121951.html</guid><wfw:comment>http://www.cppblog.com/jxgxy/comments/121951.html</wfw:comment><comments>http://www.cppblog.com/jxgxy/archive/2010/08/02/121951.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/jxgxy/comments/commentRss/121951.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/jxgxy/services/trackbacks/121951.html</trackback:ping><description><![CDATA[先下载ddkwizard及ddkwizard cmd文件.<br><br>安装ddkwizard.<br><br>系统变量,增加&nbsp; WXPBASE = DDK目录,我的是 C:\WinDDK\7600.16385.1<br><br>把ddkwizard.cmd复制到任意目录,我的是C:\WinDDK.<br><br>打开vs2008,点工具,选项,项目和解决方案,vc++目录,在可执行文件中添加ddkwizard.cmd所在目录,比如我的是C:\WinDDK<br><br>确定.就可以了.<br><br>点击文件,新建项目,vc++,ddk project,driver,去掉create prefast configuration<br><br>确定.即可直接编译驱动了.
<img src ="http://www.cppblog.com/jxgxy/aggbug/121951.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/jxgxy/" target="_blank">eboy</a> 2010-08-02 13:06 <a href="http://www.cppblog.com/jxgxy/archive/2010/08/02/121951.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>__declspec(dllexport)的意思与DEF导出函数的区别</title><link>http://www.cppblog.com/jxgxy/archive/2010/07/30/121739.html</link><dc:creator>eboy</dc:creator><author>eboy</author><pubDate>Fri, 30 Jul 2010 15:09:00 GMT</pubDate><guid>http://www.cppblog.com/jxgxy/archive/2010/07/30/121739.html</guid><wfw:comment>http://www.cppblog.com/jxgxy/comments/121739.html</wfw:comment><comments>http://www.cppblog.com/jxgxy/archive/2010/07/30/121739.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/jxgxy/comments/commentRss/121739.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/jxgxy/services/trackbacks/121739.html</trackback:ping><description><![CDATA[<p>先看代码:以下是在dev-c++里建立自已的dll时的dll.h里面的代码,这里面有一个:_declspec(dllexport)</p>
<p>#ifndef _DLL_H_<br>#define _DLL_H_//防重复定义</p>
<p>#if BUILDING_DLL<br># define DLLIMPORT __declspec (dllexport)<br>#else /* Not BUILDING_DLL */<br># define DLLIMPORT __declspec (dllimport)<br>#endif /* Not BUILDING_DLL */</p>
<p><br>DLLIMPORT void HelloWorld (void);</p>
<p><br>#endif /* _DLL_H_ */<br></p>
<p>&nbsp;</p>
<p>上面代码里面的_delcspce(dllexport)被定义为宏,这样可以提高程序的可读性!这个的作是是将函数定义为导出函数,也就是说这个函数要被包含这个函数的程序之外的程序调用!本语句中就是:void Helloword(void):</p>
<p>摘自msdn:在 32 位编译器版本中，可以使用 <strong>__declspec(dllexport)</strong> 关键字从 DLL 导出数据、函数、类或类成员函数。<strong>__declspec(dllexport)</strong> 将导出指令添加到对象文件</p>
<p>若要导出函数，<strong>__declspec(dllexport)</strong> 关键字必须出现在调用约定关键字的左边（如果指定了关键字）。例如：</p>
<pre class=code>__declspec(dllexport) void __cdecl Function1(void);</pre>
<p>若要导出类中的所有公共数据成员和成员函数，关键字必须出现在类名的左边，如下所示：</p>
<pre class=code>class __declspec(dllexport) CExampleExport : public CObject
{ ... class definition ... };</pre>
<p>生成 DLL 时，通常创建一个包含正在导出的函数原型和/或类的头文件，并将 <strong>__declspec(dllexport)</strong> 添加到头文件中的声明。若要提高代码的可读性，请为 <strong>__declspec(dllexport)</strong> 定义一个宏并对正在导出的每个符号使用该宏：</p>
<pre class=code>#define DllExport   __declspec( dllexport ) </pre>
<p><strong>__declspec(dllexport)</strong> 将函数名存储在 DLL 的导出表中。如果希望优化表的大小<br><br>更多详细信息请看：<br><a href="http://msdn.microsoft.com/library/chs/default.asp?url=/library/CHS/vccore/html/_core_export_from_a_dll_using___declspec.28.dllexport.29.asp"><font color=#1a8bc8>http://msdn.microsoft.com/library/chs/default.asp?url=/library/CHS/vccore/html/_core_export_from_a_dll_using___declspec.28.dllexport.29.asp</font></a></p>
<div id=c_jquery_test style="DISPLAY: none"></div>
<script type=text/javascript>
if ($ != jQuery) {
$ = jQuery.noConflict();
}
</script>
<img src ="http://www.cppblog.com/jxgxy/aggbug/121739.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/jxgxy/" target="_blank">eboy</a> 2010-07-30 23:09 <a href="http://www.cppblog.com/jxgxy/archive/2010/07/30/121739.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>#ifdef __cplusplus 倒底是什么意思？</title><link>http://www.cppblog.com/jxgxy/archive/2010/07/30/121738.html</link><dc:creator>eboy</dc:creator><author>eboy</author><pubDate>Fri, 30 Jul 2010 15:05:00 GMT</pubDate><guid>http://www.cppblog.com/jxgxy/archive/2010/07/30/121738.html</guid><wfw:comment>http://www.cppblog.com/jxgxy/comments/121738.html</wfw:comment><comments>http://www.cppblog.com/jxgxy/archive/2010/07/30/121738.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/jxgxy/comments/commentRss/121738.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/jxgxy/services/trackbacks/121738.html</trackback:ping><description><![CDATA[<p>时常在cpp的代码之中看到这样的代码: </p>
<p>#ifdef __cplusplus <br>extern "C" { <br>#endif </p>
<p>//一段代码 </p>
<p>#ifdef __cplusplus <br>} <br>#endif <br>　　这样的代码到底是什么意思呢？首先，__cplusplus是cpp中的自定义宏，那么定义了这个宏的话表示这是一段cpp的代码，也就是说，上面的代码的含义是:如果这是一段cpp的代码，那么加入extern "C"{和}处理其中的代码。 </p>
<p>　　要明白为何使用extern "C"，还得从cpp中对函数的重载处理开始说起。在c++中，为了支持重载机制，在编译生成的汇编码中，要对函数的名字进行一些处理，加入比如函数的返回类型等等.而在C中，只是简单的函数名字而已，不会加入其他的信息.也就是说:C++和C对产生的函数名字的处理是不一样的. </p>
<p>　　比如下面的一段简单的函数，我们看看加入和不加入extern "C"产生的汇编代码都有哪些变化: </p>
<p>int f(void) <br>{ <br>return 1; <br>} <br>　　在加入extern "C"的时候产生的汇编代码是: </p>
<p>.file "test.cxx" <br>.text <br>.align 2 <br>.globl _f <br>.def _f; .scl 2; .type 32; .endef <br>_f: <br>pushl %ebp <br>movl %esp， %ebp <br>movl $1， %eax <br>popl %ebp <br>ret <br>　　但是不加入了extern "C"之后 </p>
<p>.file "test.cxx" <br>.text <br>.align 2 <br>.globl __Z1fv <br>.def __Z1fv; .scl 2; .type 32; .endef <br>__Z1fv: <br>pushl %ebp <br>movl %esp， %ebp <br>movl $1， %eax <br>popl %ebp <br>ret <br>　　两段汇编代码同样都是使用gcc -S命令产生的，所有的地方都是一样的，唯独是产生的函数名，一个是_f，一个是__Z1fv。 </p>
<p>　　明白了加入与不加入extern "C"之后对函数名称产生的影响，我们继续我们的讨论:为什么需要使用extern "C"呢？C++之父在设计C++之时，考虑到当时已经存在了大量的C代码，为了支持原来的C代码和已经写好C库，需要在C++中尽可能的支持C，而extern "C"就是其中的一个策略。 </p>
<p>　　试想这样的情况:一个库文件已经用C写好了而且运行得很良好，这个时候我们需要使用这个库文件，但是我们需要使用C++来写这个新的代码。如果这个代码使用的是C++的方式链接这个C库文件的话，那么就会出现链接错误.我们来看一段代码:首先，我们使用C的处理方式来写一个函数，也就是说假设这个函数当时是用C写成的: </p>
<p>//f1.c <br>extern "C" <br>{ <br>void f1() <br>{ <br>return; <br>} <br>} <br>　　编译命令是:gcc -c f1.c -o f1.o 产生了一个叫f1.o的库文件。再写一段代码调用这个f1函数: </p>
<p>// test.cxx <br>//这个extern表示f1函数在别的地方定义，这样可以通过 <br>//编译，但是链接的时候还是需要 <br>//链接上原来的库文件. <br>extern void f1(); </p>
<p>int main() <br>{ <br>f1(); </p>
<p>return 0; <br>} <br>　　通过gcc -c test.cxx -o test.o 产生一个叫test.o的文件。然后，我们使用gcc test.o f1.o来链接两个文件，可是出错了，错误的提示是: </p>
<p>test.o(.text + 0x1f):test.cxx: undefine reference to 'f1()' <br>　　也就是说，在编译test.cxx的时候编译器是使用C++的方式来处理f1()函数的，但是实际上链接的库文件却是用C的方式来处理函数的，所以就会出现链接过不去的错误:因为链接器找不到函数。 </p>
<p>　　因此，为了在C++代码中调用用C写成的库文件，就需要用extern "C"来告诉编译器:这是一个用C写成的库文件，请用C的方式来链接它们。 </p>
<p>　　比如，现在我们有了一个C库文件，它的头文件是f.h，产生的lib文件是f.lib，那么我们如果要在C++中使用这个库文件，我们需要这样写: </p>
<p>extern "C" <br>{ <br>#include "f.h" <br>} <br>　　回到上面的问题，如果要改正链接错误，我们需要这样子改写test.cxx: </p>
<p>extern "C" <br>{ <br>extern void f1(); <br>} </p>
<p>int main() <br>{ <br>f1(); </p>
<p>return 0; <br>} <br>　　重新编译并且链接就可以过去了. </p>
<p>　　总结 </p>
<p>　　C和C++对函数的处理方式是不同的.extern "C"是使C++能够调用C写作的库文件的一个手段，如果要对编译器提示使用C的方式来处理函数的话，那么就要使用extern "C"来说明。</p>
<div id=c_jquery_test style="DISPLAY: none"></div>
<script type=text/javascript>
if ($ != jQuery) {
$ = jQuery.noConflict();
}
</script>
<img src ="http://www.cppblog.com/jxgxy/aggbug/121738.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/jxgxy/" target="_blank">eboy</a> 2010-07-30 23:05 <a href="http://www.cppblog.com/jxgxy/archive/2010/07/30/121738.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>细说 #pragma pack(n)</title><link>http://www.cppblog.com/jxgxy/archive/2010/07/29/121597.html</link><dc:creator>eboy</dc:creator><author>eboy</author><pubDate>Thu, 29 Jul 2010 08:36:00 GMT</pubDate><guid>http://www.cppblog.com/jxgxy/archive/2010/07/29/121597.html</guid><wfw:comment>http://www.cppblog.com/jxgxy/comments/121597.html</wfw:comment><comments>http://www.cppblog.com/jxgxy/archive/2010/07/29/121597.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/jxgxy/comments/commentRss/121597.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/jxgxy/services/trackbacks/121597.html</trackback:ping><description><![CDATA[<p><font size=2>在C语言中，结构是一种复合数据类型，其构成元素既可以是基本数据类型（如int、long、float等）的变量，也可以是一些复合数据类型（如数组、结构、联合等）的数据单元。在结构中，编译器为结构的每个成员按其自然对界（alignment）条件分配空间。各个成员按照它们被声明的顺序在内存中顺序存储，第一个成员的地址和整个结构的地址相同。</font></p>
<p><font size=2>例如，下面的结构各成员空间分配情况：<br>struct test <br>{<br>&nbsp;&nbsp;&nbsp;&nbsp; char x1;<br>&nbsp;&nbsp;&nbsp;&nbsp; short x2;<br>&nbsp;&nbsp;&nbsp;&nbsp; float x3;<br>&nbsp;&nbsp;&nbsp;&nbsp; char x4;<br>};</font></p>
<p><font size=2>结构的第一个成员x1，其偏移地址为0，占据了第1个字节。第二个成员x2为short类型，其起始地址必须2字节对界，因此，编译器在x2和x1之间填充了一个空字节。结构的第三个成员x3和第四个成员x4恰好落在其自然对界地址上，在它们前面不需要额外的填充字节。在test结构中，成员x3要求4字节对界，是该结构所有成员中要求的最大对界单元，因而test结构的自然对界条件为4字节，编译器在成员x4后面填充了3个空字节。整个结构所占据空间为12字节。</font></p>
<p><font size=2><strong>更改C编译器的缺省字节对齐方式<br></strong>在缺省情况下，C编译器为每一个变量或是数据单元按其自然对界条件分配空间。一般地，可以通过下面的方法来改变缺省的对界条件：<br>&nbsp;&nbsp;&nbsp;&nbsp; &#183; 使用伪指令#pragma pack (n)，C编译器将按照n个字节对齐。<br>&nbsp;&nbsp;&nbsp;&nbsp; &#183; 使用伪指令#pragma pack ()，取消自定义字节对齐方式。</font></p>
<p><font size=2>另外，还有如下的一种方式：<br>&nbsp;&nbsp;&nbsp;&nbsp; &#183; __attribute((aligned (n)))，让所作用的结构成员对齐在n字节自然边界上。<font color=#ff0000>如果结构中有成员的长度大于n，则按照最大成员的长度来对齐。</font><br>&nbsp;&nbsp;&nbsp;&nbsp; &#183; __attribute__ ((packed))，取消结构在编译过程中的优化对齐，按照实际占用字节数进行对齐。</font></p>
<p><font size=2>以上的n = 1, 2, 4, 8, 16... 第一种方式较为常见。</font></p>
<p><font size=2>(&nbsp;via </font><a href="http://blog.csdn.net/wenddy112/articles/300583.aspx"><u><font color=#0000ff size=2>http://blog.csdn.net/wenddy112/articles/300583.aspx</font></u></a><font size=2>&nbsp;)</font></p>
<p><font size=2>下面有一道在 </font><a href="http://www.csdn.net/" target=_blank><u><font color=#800080 size=2>CSDN论坛</font></u></a><font size=2> 上讨论火热的题：</font></p>
<p><strong><u>Intel和微软和本公司同时出现的面试题</u></strong></p>
<p>#pragma pack(8)</p>
<p>struct s1{<br>short a;<br>long b;<br>};</p>
<p>struct s2{<br>char c;<br>s1 d;<br>long long e;<br>};</p>
<p>#pragma pack()</p>
<p>问 <br>1.sizeof(s2) = ?<br>2.s2的c后面空了几个字节接着是d?</p>
<p>感谢 <a href="http://community.csdn.net/Message_Board/Send.asp?sendto=redleaves" target=_blank><font color=#3366cc size=2><strong><u>redleaves(ID最吊的网友)</u></strong></font></a>&nbsp;的解答，结果如下：</p>
<p>sizeof(S2)结果为24.<br>成员对齐有一个重要的条件,即每个成员分别对齐.即每个成员按自己的方式对齐.<br>也就是说上面虽然指定了按8字节对齐,但并不是所有的成员都是以8字节对齐.其对齐的规则是,每个成员按其类型的对齐参数(通常是这个类型的大小)和指定对齐参数(这里是8字节)中较小的一个对齐.并且结构的长度必须为所用过的所有对齐参数的整数倍,不够就补空字节.<br>S1中,成员a是1字节默认按1字节对齐,指定对齐参数为8,这两个值中取1,a按1字节对齐;成员b是4个字节,默认是按4字节对齐,这时就按4字节对齐,所以sizeof(S1)应该为8;<br>S2中,c和S1中的a一样,按1字节对齐,而d 是个结构,它是8个字节,它按什么对齐呢?对于结构来说,它的默认对齐方式就是它的所有成员使用的对齐参数中最大的一个,S1的就是4.所以,成员d就是按4字节对齐.成员e是8个字节,它是默认按8字节对齐,和指定的一样,所以它对到8字节的边界上,这时,已经使用了12个字节了,所以又添加了4个字节的空,从第16个字节开始放置成员e.这时,长度为24,已经可以被8(成员e按8字节对齐)整除.这样,一共使用了24个字节.<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;a&nbsp;&nbsp;&nbsp;&nbsp;b<br>S1的内存布局：11**,1111,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;c&nbsp;&nbsp;&nbsp; S1.a S1.b&nbsp;&nbsp;&nbsp;&nbsp; d<br>S2的内存布局：1***,11**,1111,****11111111</p>
<p>这里有三点很重要:<br>1.每个成员分别按自己的方式对齐,并能最小化长度<br>2.复杂类型(如结构)的默认对齐方式是它最长的成员的对齐方式,这样在成员是复杂类型时,可以最小化长度<br>3.对齐后的长度必须是成员中最大的对齐参数的整数倍,这样在处理数组时可以保证每一项都边界对齐</p>
<p>补充一下,对于数组,比如:<br>char a[3];这种,它的对齐方式和分别写3个char是一样的.也就是说它还是按1个字节对齐.<br>如果写: typedef char Array3[3];<br>Array3这种类型的对齐方式还是按1个字节对齐,而不是按它的长度.<br>不论类型是什么,对齐的边界一定是1,2,4,8,16,32,64....中的一个.</p>
<p><strong>测试的编译器：</strong></p>
<p>GCC 2.95 3.1 3.3 3.4 4.0<br>MS C/C++ 7.0 7.1 8.0 beta<br>Borland C/C++ 5.6 6.0<br>Intel C/C++ 7.0 8.0 8.1<br>DigitalMars C/C++ 8.4<br>OpenWatcom 1.3<br>Codeplay C/C++ 2.1.7</p>
<img src ="http://www.cppblog.com/jxgxy/aggbug/121597.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/jxgxy/" target="_blank">eboy</a> 2010-07-29 16:36 <a href="http://www.cppblog.com/jxgxy/archive/2010/07/29/121597.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>