﻿<?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++博客-爱生活 爱技术-随笔分类-C++</title><link>http://www.cppblog.com/hktk/category/7091.html</link><description /><language>zh-cn</language><lastBuildDate>Fri, 25 Sep 2009 05:38:12 GMT</lastBuildDate><pubDate>Fri, 25 Sep 2009 05:38:12 GMT</pubDate><ttl>60</ttl><item><title>C++运算符优先级</title><link>http://www.cppblog.com/hktk/archive/2009/09/25/97190.html</link><dc:creator>海 阔 天 空</dc:creator><author>海 阔 天 空</author><pubDate>Thu, 24 Sep 2009 18:30:00 GMT</pubDate><guid>http://www.cppblog.com/hktk/archive/2009/09/25/97190.html</guid><description><![CDATA[<p>&nbsp;</p>
<table style="BORDER-RIGHT: black 1px solid; BORDER-TOP: black 1px solid; FONT-SIZE: 10pt; BORDER-LEFT: black 1px solid; COLOR: #4b4b4b; BORDER-BOTTOM: black 1px solid; BORDER-COLLAPSE: collapse" borderColor=black cellSpacing=1 cellPadding=1 border=1>
    <tbody>
        <tr class=row0>
            <th class="col0 centeralign">Precedence </th>
            <th class="col1 centeralign">Operator </th>
            <th class="col2 centeralign">Description </th>
            <th class="col3 centeralign">Example </th>
            <th class="col4 centeralign">Overloadable </th>
            <th class="col5 centeralign">Associativity </th>
        </tr>
        <tr class=row1>
            <td class=col0>1</td>
            <td class=col1><code>::</code></td>
            <td class=col2>Scope resolution operator</td>
            <td class=col3>Class::age = 2;</td>
            <td class=col4>no</td>
            <td class=col5>none</td>
        </tr>
        <tr class=row2>
            <td class=col0>2</td>
            <td class=col1><code>()</code><br><code>()</code><br><code>[]</code><br><code>-&gt;</code><br><code>.</code><br><code>++</code><br><code>--</code><br><code>const_cast</code><br><code>dynamic_cast</code><br><code>static_cast</code><br><code>reinterpret_cast</code><br><code>typeid</code></td>
            <td class=col2>Function call<br>Member initalization <br>Array access<br>Member access from a pointer<br>Member access from an object<br>Post-increment<br>Post-decrement<br>Special cast<br>Special cast<br>Special cast<br>Special cast<br>Runtime type information</td>
            <td class=col3>isdigit('1')<br>c_tor(int x, int y) : _x(x), _y(y*10){};<br>array[4] = 2;<br>ptr-&gt;age = 34;<br>obj.age = 34;<br>for( int i = 0; i &lt; 10; i++ ) cout &lt;&lt; i;<br>for( int i = 10; i &gt; 0; i-- ) cout &lt;&lt; i;<br>const_cast&lt;type_to&gt;(type_from);<br>dynamic_cast&lt;type_to&gt;(type_from);<br>static_cast&lt;type_to&gt;(type_from);<br>reinterpret_cast&lt;type_to&gt;(type_from);<br>cout &#171; typeid(type).name();</td>
            <td class=col4>yes<br>yes<br>yes<br>yes<br>no<br>yes<br>yes<br>no<br>no<br>no<br>no<br>no</td>
            <td class=col5>left to right</td>
        </tr>
        <tr class=row3>
            <td class=col0>3</td>
            <td class=col1><code>!</code><br><code>not</code><br><code>~</code><br><code>compl</code><br><code>++</code><br><code>--</code><br><code>-</code><br><code>+</code><br><code>*</code><br><code>&amp;</code><br><code>new</code><br><code>new []</code><br><code>delete</code><br><code>delete []</code><br><code>(type)</code><br><code>sizeof</code></td>
            <td class=col2>Logical negation<br>Alternate spelling for ! <br>Bitwise complement<br>Alternate spelling for ~ <br>Pre-increment<br>Pre-decrement<br>Unary minus<br>Unary plus<br>Dereference<br>Address of<br>Dynamic memory allocation<br>Dynamic memory allocation of array<br>Deallocating the memory<br>Deallocating the memory of array<br>Cast to a given type<br>Return size of an object or type</td>
            <td class=col3>if( !done ) &#8230;<br><br>flags = ~flags;<br><br>for( i = 0; i &lt; 10; ++i ) cout &lt;&lt; i;<br>for( i = 10; i &gt; 0; --i ) cout &lt;&lt; i;<br>int i = -1;<br>int i = +1;<br>int data = *intPtr;<br>int *intPtr = &amp;data;<br>long *pVar = new long; <br>MyClass *ptr = new MyClass(args);<br>delete pVar;<br>delete [] array;<br>int i = (int) floatNum;\\int size = sizeof(float);</td>
            <td class=col4>yes<br><br>yes<br><br>yes<br>yes<br>yes<br>yes<br>yes<br>yes<br>yes<br>yes<br>yes<br>yes<br>yes<br>no</td>
            <td class=col5>right to left</td>
        </tr>
        <tr class=row4>
            <td class=col0>4</td>
            <td class=col1><code>-&gt;*</code><br><code>.*</code></td>
            <td class=col2>Member pointer selector<br>Member object selector</td>
            <td class=col3>ptr-&gt;*var = 24;<br>obj.*var = 24;</td>
            <td class=col4>yes<br>no</td>
            <td class=col5>left to right</td>
        </tr>
        <tr class=row5>
            <td class=col0>5</td>
            <td class=col1><code>*</code><br><code>/</code><br><code>%</code></td>
            <td class=col2>Multiplication<br>Division<br>Modulus</td>
            <td class=col3>int i = 2 * 4;<br>float f = 10.0 / 3.0;<br>int rem = 4 % 3;</td>
            <td class=col4>yes<br>yes<br>yes</td>
            <td class=col5>left to right</td>
        </tr>
        <tr class=row6>
            <td class=col0>6</td>
            <td class=col1><code>+</code><br><code>-</code></td>
            <td class=col2>Addition<br>Subtraction</td>
            <td class=col3>int i = 2 + 3;<br>int i = 5 - 1;</td>
            <td class=col4>yes<br>yes</td>
            <td class=col5>left to right</td>
        </tr>
        <tr class=row7>
            <td class=col0>7</td>
            <td class=col1><code>&lt;&lt;</code><br><code>&gt;&gt;</code></td>
            <td class=col2>Bitwise shift left<br>Bitwise shift right</td>
            <td class=col3>int flags = 33 &lt;&lt; 1;<br>int flags = 33 &gt;&gt; 1;</td>
            <td class=col4>yes<br>yes</td>
            <td class=col5>left to right</td>
        </tr>
        <tr class=row8>
            <td class=col0>8</td>
            <td class=col1><code>&lt;</code><br><code>&lt;=</code><br><code>&gt;</code><br><code>&gt;=</code></td>
            <td class=col2>Comparison less-than<br>Comparison less-than-or-equal-to<br>Comparison greater-than<br>Comparison greater-than-or-equal-to</td>
            <td class=col3>if( i &lt; 42 ) &#8230;<br>if( i &lt;= 42 ) ...<br>if( i &gt; 42 ) &#8230;<br>if( i &gt;= 42 ) ...</td>
            <td class=col4>yes<br>yes<br>yes<br>yes</td>
            <td class=col5>left to right</td>
        </tr>
        <tr class=row9>
            <td class=col0>9</td>
            <td class=col1><code>==</code><br><code>eq</code><br><code>!=</code><br><code>not_eq</code></td>
            <td class=col2>Comparison equal-to<br>Alternate spelling for == <br>Comparison not-equal-to<br>Alternate spelling for != </td>
            <td class=col3>if( i == 42 ) ...<br><br>if( i != 42 ) &#8230;<br></td>
            <td class=col4>yes<br>-<br>yes<br></td>
            <td class=col5>left to right</td>
        </tr>
        <tr class=row10>
            <td class=col0>10</td>
            <td class=col1><code>&amp;</code><br><code>bitand</code></td>
            <td class=col2>Bitwise AND<br>Alternate spelling for &amp; </td>
            <td class=col3>flags = flags &amp; 42;<br></td>
            <td class=col4>yes<br></td>
            <td class=col5>left to right</td>
        </tr>
        <tr class=row11>
            <td class=col0>11</td>
            <td class=col1><code>^</code><br><code>xor</code></td>
            <td class=col2>Bitwise exclusive OR (XOR)<br>Alternate spelling for ^ </td>
            <td class=col3>flags = flags ^ 42;<br></td>
            <td class=col4>yes<br></td>
            <td class=col5>left to right</td>
        </tr>
        <tr class=row12>
            <td class=col0>12</td>
            <td class=col1><code>|</code><br><code>bitor</code></td>
            <td class=col2>Bitwise inclusive (normal) OR<br>Alternate spelling for |</td>
            <td class=col3>flags = flags | 42;<br></td>
            <td class=col4>yes<br></td>
            <td class=col5>left to right</td>
        </tr>
        <tr class=row13>
            <td class=col0>13</td>
            <td class=col1><code>&amp;&amp;</code><br><code>and</code></td>
            <td class=col2>Logical AND<br>Alternate spelling for &amp;&amp; </td>
            <td class=col3>if( conditionA &amp;&amp; conditionB ) &#8230;<br></td>
            <td class=col4>yes<br></td>
            <td class=col5>left to right</td>
        </tr>
        <tr class=row14>
            <td class=col0>14</td>
            <td class=col1><code>||</code><br><code>or</code></td>
            <td class=col2>Logical OR<br>Alternate spelling for ||</td>
            <td class=col3>if( conditionA || conditionB ) ...<br></td>
            <td class=col4>yes<br></td>
            <td class=col5>left to right</td>
        </tr>
        <tr class=row15>
            <td class=col0>15</td>
            <td class=col1><code>? :</code></td>
            <td class=col2>Ternary conditional (if-then-else)</td>
            <td class=col3>int i = (a &gt; b) ? a : b;</td>
            <td class=col4>no</td>
            <td class=col5>right to left</td>
        </tr>
        <tr class=row16>
            <td class=col0>16</td>
            <td class=col1><code>=</code><br><code>+=</code><br><code>-=</code><br><code>*=</code><br><code>/=</code><br><code>%=</code><br><code>&amp;=</code><br><code>and_eq</code><br><code>^=</code><br><code>xor_eq</code><br><code>|=</code><br><code>or_eq</code><br><code>&lt;&lt;=</code><br><code>&gt;&gt;=</code></td>
            <td class=col2>Assignment operator<br>Increment and assign<br>Decrement and assign<br>Multiply and assign<br>Divide and assign<br>Modulo and assign<br>Bitwise AND and assign<br>Alternate spelling for &amp;= <br>Bitwise exclusive or (XOR) and assign<br>Alternate spelling for ^= <br>Bitwise normal OR and assign<br>Alternate spelling for |=<br>Bitwise shift left and assign<br>Bitwise shift right and assign</td>
            <td class=col3>int a = b;<br>a += 3;<br>b -= 4;<br>a *= 5;<br>a /= 2;<br>a %= 3;<br>flags &amp;= new_flags;<br><br>flags ^= new_flags;<br><br>flags |= new_flags;<br><br>flags &lt;&lt;= 2;<br>flags &gt;&gt;= 2;</td>
            <td class=col4>yes<br>yes<br>yes<br>yes<br>yes<br>yes<br>yes<br><br>yes<br><br>yes<br><br>yes<br>yes</td>
            <td class=col5>right to left</td>
        </tr>
        <tr class=row17>
            <td class=col0>17</td>
            <td class=col1><code>throw</code></td>
            <td class=col2>throw exception</td>
            <td class=col3>throw EClass(&#8220;Message&#8221;);</td>
            <td class=col4 colSpan=3>no</td>
        </tr>
        <tr class=row18>
            <td class=col0>18</td>
            <td class=col1><code>,</code></td>
            <td class=col2>Sequential evaluation operator</td>
            <td class=col3>for( i = 0, j = 0; i &lt; 10; i++, j++ ) &#8230;</td>
            <td class=col4>yes</td>
            <td class=col5>left to right</td>
        </tr>
    </tbody>
</table>
<img src ="http://www.cppblog.com/hktk/aggbug/97190.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/hktk/" target="_blank">海 阔 天 空</a> 2009-09-25 02:30 <a href="http://www.cppblog.com/hktk/archive/2009/09/25/97190.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>C++宏定义</title><link>http://www.cppblog.com/hktk/archive/2009/09/21/96874.html</link><dc:creator>海 阔 天 空</dc:creator><author>海 阔 天 空</author><pubDate>Mon, 21 Sep 2009 11:06:00 GMT</pubDate><guid>http://www.cppblog.com/hktk/archive/2009/09/21/96874.html</guid><description><![CDATA[C++提供的编译预处理功能主要有以下三种：<br>
<div class=spctrl></div>
　　（一） 宏定义<br>
<div class=spctrl></div>
　　（二） 文件包含<br>
<div class=spctrl></div>
　　（三） 条件编译<br>
<div class=spctrl></div>
　　在C++中，我们一般用const定义符号常量。很显然，用const定义常量比用define定义常量更好。<br>
<div class=spctrl></div>
　　在使用宏定义时应注意的是：<br>
<div class=spctrl></div>
　　（a） 在书写#define 命令时，注意&lt;宏名&gt;和&lt;字符串&gt;之间用空格分开，而不是用等号连接。<br>
<div class=spctrl></div>
　　（b） 使用#define定义的标识符不是变量，它只用作宏替换，因此不占有内存。<br>
<div class=spctrl></div>
　　（c） 习惯上用大写字母表示&lt;宏名&gt;，这只是一种习惯的约定，其目的是为了与变量名区分，因为变量名<br>
<div class=spctrl></div>
　　通常用小写字母。<br>
<div class=spctrl></div>
　　如果某一个标识符被定义为宏名后，在取消该宏定义之前，不允许重新对它进行宏定义。取消宏定义使用如下命令：<br>
<div class=spctrl></div>
　　#undef&lt;标识符&gt;<br>
<div class=spctrl></div>
　　其中，undef是关键字。该命令的功能是取消对&lt;标识符&gt;已有的宏定义。被取消了宏定义的标识符，可以对它重新进行定义。<br>
<div class=spctrl></div>
　　宏定义可以嵌套，已被定义的标识符可以用来定义新的标识符。例如：<br>
<div class=spctrl></div>
　　#define PI 3.14159265<br>
<div class=spctrl></div>
　　#define R 10<br>
<div class=spctrl></div>
　　#define AREA (PI*R*R)<br>
<div class=spctrl></div>
　　单的宏定义将一个标识符定义为一个字符串，源程序中的该标识符均以指定的字符串来代替。前面已经说过，预处理命令不同于一般C++语句。因此预处理命令后通常不加分号。这并不是说所有的预处理命令后都不能有分号出现。由于宏定义只是用宏名对一个字符串进行简单的替换，因此如果在宏定义命令后加了分号，将会连同分号一起进行置换。<br>
<div class=spctrl></div>
　　带参数的宏定义<br>
<div class=spctrl></div>
　　带参数的宏定义的一般形式如下：<br>
<div class=spctrl></div>
　　#define &lt;宏名&gt;（&lt;参数表&gt;） &lt;宏体&gt;<br>
<div class=spctrl></div>
　　其中， &lt;宏名&gt;是一个标识符，&lt;参数表&gt;中的参数可以是一个，也可以是多个，视具体情况而定，当有多个参数的时候，每个参数之间用逗号分隔。&lt;宏体&gt;是被替换用的字符串，宏体中的字符串是由参数表中的各个参数组成的表达式。例如：<br>
<div class=spctrl></div>
　　#define SUB(a,b) a-b<br>
<div class=spctrl></div>
　　如果在程序中出现如下语句：<br>
<div class=spctrl></div>
　　result=SUB(2, 3)<br>
<div class=spctrl></div>
　　则被替换为：<br>
<div class=spctrl></div>
　　result=2-3；<br>
<div class=spctrl></div>
　　如果程序中出现如下语句：<br>
<div class=spctrl></div>
　　result= SUB（x+1, y+2）;<br>
<div class=spctrl></div>
　　则被替换为：<br>
<div class=spctrl></div>
　　result=x+1-y+2;<br>
<div class=spctrl></div>
　　在这样的宏替换过程中，其实只是将参数表中的参数代入到宏体的表达式中去，上述例子中，即是将表达式中的a和b分别用2和3代入。<br>
<div class=spctrl></div>
　　我们可以发现：带参的宏定义与函数类似。如果我们把宏定义时出现的参数视为形参，而在程序中引用宏定义时出现的参数视为实参。那么上例中的a和b就是形参，而2和3以及x+1和y+2都为实参。在宏替换时，就是用实参来替换&lt;宏体&gt;中的形参。<br>
<div class=spctrl></div>
　　在使用带参数的宏定义时需要注意的是：<br>
<div class=spctrl></div>
　　（1）带参数的宏定义的&lt;宏体&gt;应写在一行上，如果需要写在多行上时，在每行结束时，使用续行符 "\"结<br>
<div class=spctrl></div>
　　束，并在该符号后按下回车键，最后一行除外。<br>
<div class=spctrl></div>
　　（2）在书写带参数的宏定义时，&lt;宏名&gt;与左括号之间不能出现空格，否则空格右边的部分都作为宏体。<br>
<div class=spctrl></div>
　　例如：<br>
<div class=spctrl></div>
　　#define ADD (x,y) x+y<br>
<div class=spctrl></div>
　　将会把"（x,y）x+y"的一个整体作为被定义的字符串。<br>
<div class=spctrl></div>
　　（3）定义带参数的宏时，宏体中与参数名相同的字符串适当地加上圆括号是十分重要的，这样能够避免<br>
<div class=spctrl></div>
　　可能产生的错误。例如,对于宏定义：<br>
<div class=spctrl></div>
　　#define SQ(x) x*x<br>
<div class=spctrl></div>
　　当程序中出现下列语句：<br>
<div class=spctrl></div>
　　m=SQ(a+b);<br>
<div class=spctrl></div>
　　替换结果为：<br>
<div class=spctrl></div>
　　m=a+b*a+b;<br>
<div class=spctrl></div>
　　这可能不是我们期望的结果，如果需要下面的替换结果：<br>
<div class=spctrl></div>
　　m=(a+b)*(a+b);<br>
<div class=spctrl></div>
　　应将宏定义修改为：<br>
<div class=spctrl></div>
　　#define SQ(x) (x)*(x) <br>
<div class=spctrl></div>
　　对于带参的宏定义展开置换的方法是：在程序中如果有带实参的宏（如"SUB（2,3）"），则按"#define"命令行中指定的字符串从左到右进行置换。如果串中包含宏中的形参（如a、b），则将程序语句中相应的实参（可以是常量、变量或者表达式）代替形参，如果宏定义中的字符串中的字符不是参数字符（如a-b中的-号），则保留。这样就形成了置换的字符串。
<img src ="http://www.cppblog.com/hktk/aggbug/96874.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/hktk/" target="_blank">海 阔 天 空</a> 2009-09-21 19:06 <a href="http://www.cppblog.com/hktk/archive/2009/09/21/96874.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>如何理解C和C++的复杂类型声明</title><link>http://www.cppblog.com/hktk/archive/2009/09/21/96873.html</link><dc:creator>海 阔 天 空</dc:creator><author>海 阔 天 空</author><pubDate>Mon, 21 Sep 2009 11:04:00 GMT</pubDate><guid>http://www.cppblog.com/hktk/archive/2009/09/21/96873.html</guid><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 曾经碰到过让你迷惑不解、类似于int * (* (*fp1) (int) ) [10];这样的变量声明吗？本文将由易到难，一步一步教会你如何理解这种复杂的C/C++声明。 　　我们将从每天都能碰到的较简单的声明入手，然后逐步加入const修饰符和typedef，还有函数指针，最后介绍一个能够让你准确地理解任何C/C++声明的&#8220;右左法则&#8221;。 　　需要强调一下的是，复杂的C/C...&nbsp;&nbsp;<a href='http://www.cppblog.com/hktk/archive/2009/09/21/96873.html'>阅读全文</a><img src ="http://www.cppblog.com/hktk/aggbug/96873.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/hktk/" target="_blank">海 阔 天 空</a> 2009-09-21 19:04 <a href="http://www.cppblog.com/hktk/archive/2009/09/21/96873.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>sizeof 操作符详解</title><link>http://www.cppblog.com/hktk/archive/2009/09/21/96872.html</link><dc:creator>海 阔 天 空</dc:creator><author>海 阔 天 空</author><pubDate>Mon, 21 Sep 2009 11:01:00 GMT</pubDate><guid>http://www.cppblog.com/hktk/archive/2009/09/21/96872.html</guid><description><![CDATA[<p><strong>1. 定义：</strong> <br><strong>sizeof是何方神圣？</strong> <br>sizeof 乃 C/C++ 中的一个操作符（operator）是也。简单说其作用就是返回一个对象或者类型所占的内存字节数。 <br>MSDN上的解释为： <br>The sizeof keyword gives the amount of storage, in bytes, associated with a variable or a type (including aggregate types).This keyword returns a value of type size_t. <br>其返回值类型为size_t，在头文件stddef.h中定义。这是一个依赖于编译系统的值，一般定义为 <br>typedef unsigned int size_t; <br>世上编译器林林总总，但作为一个规范，它们都会保证char、signed char和unsigned char的sizeof值为1，毕竟char是我们编程能用的最小数据类型。</p>
<p><br><strong>2. 语法：</strong> <br>sizeof有三种语法形式，如下： <br>1) sizeof( object ); // sizeof( 对象 ); <br>2) sizeof( type_name ); // sizeof( 类型 ); <br>3) sizeof object; // sizeof 对象; <br>所以， <br>int i; <br>sizeof( i ); // ok <br>sizeof i; // ok <br>sizeof( int ); // ok <br>sizeof int; // error <br>既然写法2可以用写法1代替，为求形式统一以及减少我们大脑的负担，第2种写法，忘掉它吧！ <br>实际上，sizeof计算对象的大小也是转换成对对象类型的计算。也就是说，同种类型的不同对象其sizeof值都是一致的。 <br>这里，对象可以进一步延伸至表达式，即sizeof可以对一个表达式求值。编译器<strong>根据表达式的最终结果类型来确定大小</strong>，一般不会对表达式进行计算。 <br>例如： <br>sizeof( 2 ); // 2的类型为int，所以等价于 sizeof( int ); <br>sizeof( 2 + 3.14 ); // 3.14的类型为double，2也会被提升成double类型，所以等价于 sizeof( double ); <br>sizeof也可以对一个函数调用求值，其结果是函数返回类型的大小，函数并不会被调用。我们来看一个完整的例子： <br>********************************************************* <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() 的返回值类型为char，所以sz = sizeof(char)，但函数foo()并不会被调用 <br>printf("sizeof( foo() ) = %d\n", sz); <br>} <br>********************************************************* <br>C99标准规定，函数、不能确定类型的表达式以及位域（bit-field）成员不能被计算sizeof值，即下面这些写法都是错误的： <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</p>
<p><br><strong>3. sizeof的常量性</strong> <br>sizeof的计算发生在编译时刻，所以它可以被当作常量表达式使用。如： <br>char ary[ sizeof( int ) * 10 ]; // ok <br>最新的C99标准规定sizeof也可以在运行时刻进行计算。如下面的程序在Dev-C++中可以正确执行： <br>int n; <br>n = 10; // n动态赋值 <br>char ary[n]; // C99也支持数组的动态定义 <br>printf("%d\n", sizeof(ary)); // ok. 输出10 <br>但在没有完全实现C99标准的编译器中就行不通了，上面的代码在VC6中就通不过编译。所以我们最好还是认为sizeof是在编译期执行的，这样不会带来错误，让程序的可移植性强些。</p>
<p><br><strong>4. 基本数据类型的sizeof</strong> <br>这里的基本数据类型指short、int、long、float、double这样的简单内置数据类型。由于它们都是和系统相关的，所以在不同的系统下取值可能不同。这务必引起我们的注意，尽量不要在这方面给自己程序的移植造成麻烦。 <br>一般的，在32位编译环境中，sizeof(int)的取值为4。</p>
<p><br><strong>5. 指针变量的sizeof</strong> <br>学过数据结构的你应该知道指针是一个很重要的概念，它记录了另一个对象的地址。既然是来存放地址的，那么它当然等于计算机内部地址总线的宽度。所以在32 位计算机中，一个指针变量的返回值必定是4（注意结果是以字节为单位）。可以预计，在将来的64位系统中指针变量的sizeof结果为8。 <br>********************************************************* <br>char* pc = "abc"; <br>int* pi; <br>string* ps; <br>char** ppc = &amp;pc; <br>void (*pf)(); // 函数指针 <br>sizeof( pc ); // 结果为4 <br>sizeof( pi ); // 结果为4 <br>sizeof( ps ); // 结果为4 <br>sizeof( ppc );// 结果为4 <br>sizeof( pf ); // 结果为4 <br>********************************************************* <br>指针变量的sizeof值与指针所指的对象没有任何关系，正是由于所有的指针变量所占内存大小相等，所以MFC消息处理函数使用两个参数WPARAM、LPARAM就能传递各种复杂的消息结构（使用指向结构体的指针）。</p>
<p><br><strong>6. 数组的sizeof</strong> <br>数组的sizeof值等于数组所占用的内存字节数，如： <br>char a1[] = "abc"; <br>int a2[3]; <br>sizeof( a1 ); // 结果为4，字符 末尾还存在一个NULL终止符 <br>sizeof( a2 ); // 结果为3*4=12（依赖于int） <br>一些朋友刚开始时把sizeof当作了求数组元素的个数，现在，你应该知道这是不对的。那么应该怎么求数组元素的个数呢？ <br>Easy，通常有下面两种写法： <br>int c1 = sizeof( a1 ) / sizeof( char ); // 总长度/单个元素的长度 <br>int c2 = sizeof( a1 ) / sizeof( a1[0]); // 总长度/第一个元素的长度 <br>写到这里，提一问，下面的c3，c4值应该是多少呢？ <br>********************************************************* <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>********************************************************* <br>也许当你试图回答c4的值时已经意识到c3答错了，是的，c3!=3。 <br>这里函数参数a3已不再是数组类型，而是蜕变成指针。相当于char* a3，为什么仔细想想就不难明白。 <br>我们调用函数foo1时，程序会在栈上分配一个大小为3的数组吗？不会！ <br>数组是&#8220;传址&#8221;的，调用者只需将实参的地址传递过去，所以a3自然为指针类型（char*），c3的值也就为4。</p>
<p><br><strong>7. 结构体的sizeof</strong> <br>这是初学者问得最多的一个问题，所以这里有必要多费点笔墨。让我们先看一个结构体： <br>struct S1 <br>{ <br>char c; <br>int i; <br>}; <br>问sizeof(s1)等于多少？ <br>聪明的你开始思考了，char占1个字节，int占4个字节，那么加起来就应该是5。 <br>是这样吗？ <br>你在你机器上试过了吗？ <br>也许你是对的，但很可能你是错的！ <br>VC6中按默认设置得到的结果为8。 <br>Why？为什么受伤的总是我？ <br>请不要沮丧，我们来好好琢磨一下sizeof的定义 —— sizeof的结果等于对象或者类型所占的内存字节数。好吧，那就让我们来看看S1的内存分配情况： <br>S1 s1 = { 'a', 0xFFFFFFFF }; <br>定义上面的变量后，加上断点，运行程序，观察s1所在的内存，你发现了什么？ <br>以我的VC6.0为例，s1的地址为0x0012FF78，其数据内容如下： <br>0012FF78: 61 CC CC CC FF FF FF FF <br>发现了什么？怎么中间夹杂了3个字节的CC？ <br>看看MSDN上的说明： <br>When applied to a structure type or variable, sizeof returns the actual size, which may include padding bytes inserted for alignment. <br>原来如此，这就是传说中的字节对齐啊！一个重要的话题出现了。 <br>为什么需要字节对齐？ <br>计算机组成原理教导我们，这样有助于加快计算机的取数速度，否则就得多花指令周期了。 <br>为此，编译器默认会对结构体进行处理（实际上其它地方的数据变量也是如此），让宽度为2的基本数据类型（short等）都位于能被2整除的地址上，让宽度为4的基本数据类型（int等）都位于能被4整除的地址上。以此类推，这样，两个数中间就可能需要加入填充字节，所以整个结构体的sizeof值就增长了。 <br>让我们交换一下S1中char与int的位置： <br>struct S2 <br>{ <br>int i; <br>char c; <br>}; <br>看看sizeof(S2)的结果为多少？怎么还是8。 <br>再看看内存，原来成员c后面仍然有3个填充字节。 <br>这又是为什么啊？别着急，下面总结规律。</p>
<p><br><strong>字节对齐的细节和编译器实现相关，但一般而言，满足三个准则：</strong> <br>1) 结构体变量的首地址能够被其最宽基本类型成员的大小所整除； <br>2) 结构体每个成员相对于结构体首地址的偏移量（offset）都是成员大小的整数倍，如有需要编译器会在成员之间加上填充字节（internal adding）； <br>3) 结构体的总大小为结构体最宽基本类型成员大小的整数倍，如有需要编译器会在最末一个成员之后加上填充字节（trailing padding）。</p>
<p><br>对于上面的准则，有几点需要说明： <br>1) 前面不是说结构体成员的地址是其大小的整数倍，怎么又说到偏移量了呢？ <br>因为有了第1点存在，所以我们就可以只考虑成员的偏移量，这样思考起来简单。想想为什么。 <br>结构体某个成员相对于结构体首地址的偏移量可以通过宏offsetof()来获得，这个宏也在stddef.h中定义，如下：<br>#define offsetof(s,m) (size_t)&amp;(((s *)0)-&gt;m) <br>例如，想要获得S2中c的偏移量，方法为 <br>size_t pos = offsetof(S2, c);// pos等于4</p>
<p><br>2) 基本类型是指前面提到的像char、short、int、float、double这样的内置数据类型。这里所说的&#8220;数据宽度&#8221;就是指其sizeof的大小。由于结构体的成员可以是复合类型，比如另外一个结构体，所以在寻找最宽基本类型成员时，应当包括复合类型成员的子成员，而不是把复合成员看成是一个整体。但在确定复合类型成员的偏移位置时则是将复合类型作为整体看待。 <br>这里叙述起来有点拗口，思考起来也有点挠头，还是让我们看看例子吧（具体数值仍以VC6为例，以后不再说明）： <br>struct S3 <br>{ <br>char c1; <br>S1 s; <br>char c2; <br>}; <br>S1的最宽简单成员的类型为int，S3在考虑最宽简单类型成员时是将S1&#8220;打散&#8221;看的，所以S3的最宽简单类型为int。这样，通过S3定义的变量，其存储空间首地址需要被4整除，整个sizeof(S3)的值也应该被4整除。 <br>c1的偏移量为0，s的偏移量呢？这时s是一个整体，它作为结构体变量也满足前面三个准则，所以其大小为8，偏移量为4，c1与s之间便需要3个填充字节，而c2与s之间就不需要了，所以c2的偏移量为12，算上c2的大小为13，13是不能被4整除的，这样末尾还得补上3个填充字节。最后得到 sizeof(S3)的值为16。</p>
<p>通过上面的叙述，我们可以得到一个公式： <br><strong>结构体的大小等于最后一个成员的偏移量加上其大小再加上末尾的填充字节数目，</strong>即： <br>sizeof( struct ) = offsetof( last item ) + sizeof( last item ) + sizeof( trailing padding )</p>
<p><br><strong>8.类的sizeof</strong> <br><br>类的sizeof值等于类中成员变量所占用的内存字节数。如： <br>**************************************************************** <br><br>class A <br>{ <br>public: <br>int b; <br>float c; <br>char d; <br>}; <br>int main(void) <br>{ <br>A object; <br>cout &lt;&lt;"sizeof(object) is "&lt; return 0 ; <br>} <br><br>*************************************************************** <br><br><br>输出结果为12（我的机器上sizeof(float)值为4，字节对其前面已经讲过）。 <br><br>不过需要注意的是，如果类中存在静态成员变量，结果又会是什么样子呢？ <br><br>*************************************************************** <br>class A <br>{ <br>public: <br>static int a; <br>int b; <br>float c; <br>char d; <br>}; <br>int main() <br>{ <br>A object; <br>cout &lt;&lt;"sizeof(object) is "&lt; return 0 ; <br>} <br><br>************************************************************** <br><br><br>16？不对。结果仍然是12. <br>因为在程序编译期间，就已经为static变量在静态存储区域分配了内存空间，并且这块内存在程序的整个运行期间都存在。 <br>而每次声明了类A的一个对象的时候，为该对象在堆上，根据对象的大小分配内存。 <br><br>如果类A中包含成员函数，那么又会是怎样的情况呢？看下面的例子 <br><br>************************************************************* <br>class A <br>{ <br>public: <br>static int a; <br>int b; <br>float c; <br>char d; <br>int add(int x,int y) <br>{ <br>return x+y; <br>} <br>}; <br>int main() <br>{ <br>A object; <br>cout &lt;&lt;"sizeof(object) is "&lt; b = object.add(3,4); <br>cout &lt;&lt;"sizeof(object) is "&lt; return 0 ; <br>} <br><br>*************************************************************** <br><br>结果仍为12。 <br><br>因为只有非静态类成员变量在新生成一个object的时候才需要自己的副本。 <br>所以每个非静态成员变量在生成新object需要内存，而function是不需要的。 <br><br>注：C++中的多态和虚继承也是非常重要的东西，不过比较复杂，编译器不同，细节也有所不同。 </p>
<img src ="http://www.cppblog.com/hktk/aggbug/96872.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/hktk/" target="_blank">海 阔 天 空</a> 2009-09-21 19:01 <a href="http://www.cppblog.com/hktk/archive/2009/09/21/96872.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>C++ 文件操作详解</title><link>http://www.cppblog.com/hktk/archive/2009/09/21/96871.html</link><dc:creator>海 阔 天 空</dc:creator><author>海 阔 天 空</author><pubDate>Mon, 21 Sep 2009 10:59:00 GMT</pubDate><guid>http://www.cppblog.com/hktk/archive/2009/09/21/96871.html</guid><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: C++ 通过以下几个类支持文件的输入输出：    ofstream: 写操作（输出）的文件类 (由ostream引申而来)     ifstream: 读操作（输入）的文件类(由istream引申而来)     fstream: 可同时读写操作的文件类 (由iostream引申而来) &nbsp;打开文件(Open a file)对这些类的一个对象所做的第一个操作通...&nbsp;&nbsp;<a href='http://www.cppblog.com/hktk/archive/2009/09/21/96871.html'>阅读全文</a><img src ="http://www.cppblog.com/hktk/aggbug/96871.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/hktk/" target="_blank">海 阔 天 空</a> 2009-09-21 18:59 <a href="http://www.cppblog.com/hktk/archive/2009/09/21/96871.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>C++ *p++,*++p,++*p</title><link>http://www.cppblog.com/hktk/archive/2009/09/21/96870.html</link><dc:creator>海 阔 天 空</dc:creator><author>海 阔 天 空</author><pubDate>Mon, 21 Sep 2009 10:56:00 GMT</pubDate><guid>http://www.cppblog.com/hktk/archive/2009/09/21/96870.html</guid><description><![CDATA[<p>#include &lt;iostream.h&gt;<br>int main(){<br>&nbsp;char s[] = "012345678", *p = s;</p>
<p>&nbsp;cout &lt;&lt; *p++ &lt;&lt; *(p++) &lt;&lt; (*p)++ &lt;&lt; *++p &lt;&lt; *(++p) &lt;&lt;++*p &lt;&lt; ++(*p) &lt;&lt; endl;<br>&nbsp;<br>&nbsp;p = s;<br>&nbsp;cout &lt;&lt; *p++ &lt;&lt; endl;<br>&nbsp;cout &lt;&lt; *(p++) &lt;&lt; endl;<br>&nbsp;cout &lt;&lt; (*p)++ &lt;&lt; endl;<br>&nbsp;cout &lt;&lt; * ++p &lt;&lt; endl;<br>&nbsp;cout &lt;&lt; *(++p) &lt;&lt;endl;<br>&nbsp;cout &lt;&lt; ++*p &lt;&lt; endl;<br>&nbsp;cout &lt;&lt; ++( *p) &lt;&lt;endl;<br>&nbsp;return 0;<br>}<br><br>VC++6 DEBUG下分析：cout的运算是从右向左进行的，但最后输出还是从左到右。<br>所以cout &lt;&lt; *p++ &lt;&lt; *(p++) &lt;&lt; (*p)++ &lt;&lt; *++p &lt;&lt; *(++p) &lt;&lt;++*p &lt;&lt; ++(*p) &lt;&lt; endl;<br>依次++(*p),++*p,*(++p),*++p,(*p)++,*(p++), *p++ ,最后再反着输出。<br>1.++*p:P指向S[0]，并把S[0]加1做为表达式的值，所以输出为1，此时S[0]=='1'<br>2.++(*p):P还指向S[0]（S[0]现在的值为1），并把S[0]加1做为表达式的值，所以输出为2，此时S[0]=='2'<br>3.*(++p):p指向S[1]，然后取S[1]的值作为表达式的值，输出'1'<br>4.*++p ：P指向S[2]，然后取S[2]的值作为表达式的值，输出'2'<br>5.(*p)++：P还是指向S[2]，取S[2]的值作为表达式的值，所以输出'2'，然后S[2]的值加1，S[2]==3<br>6.*(p++)：P还是指向S[2]（现值为3），取S[2]的值作为表达式的值，所以输出'3'，然后P指向S[3]<br>7.*p++ ：P指向S[3]，取S[3]的值作为表达式的值，所以输出'3'，然后P指向S[4]；<br>最后反着输出为3322121<br>此数组S的值为"213345678".所以后面的输出就是这些值了。</p>
<img src ="http://www.cppblog.com/hktk/aggbug/96870.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/hktk/" target="_blank">海 阔 天 空</a> 2009-09-21 18:56 <a href="http://www.cppblog.com/hktk/archive/2009/09/21/96870.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>C++中友元类和友元函数的示例讲解</title><link>http://www.cppblog.com/hktk/archive/2009/09/21/96866.html</link><dc:creator>海 阔 天 空</dc:creator><author>海 阔 天 空</author><pubDate>Mon, 21 Sep 2009 10:51:00 GMT</pubDate><guid>http://www.cppblog.com/hktk/archive/2009/09/21/96866.html</guid><description><![CDATA[<p>简单的讲 ：把类外部的类或函数声明为友元类和友元函数，使其能直接访问类中的私有成员。</p>
<p>格式： friend&nbsp; 类 或 函数 声明</p>
<p>//此语句可以放在类中的任意位置，与访问权限保留字无关。</p>
<p>需要注意几个方面：</p>
<p>1.友元关系是不可以传递的。</p>
<p>&nbsp;比方说你是我的友元而Joe是你的友元，但这并不意味着Joe是我的友元。</p>
<p>2.友元关系不能继承。</p>
<p>3.友元关系不是互通。</p>
<p>把class A设置为class B的友元并不会使得class B成为class A的友元，也许你愿意把你的秘密告诉我，但是，这并不意味着我愿意把我的秘密告诉你。<br><br><font size=4>--------------------------------------------------------------------------------<br></font>&#8220;友元关系既不继承，也不传递&#8221;是什么意思？<br>[Recently added the "not reciprocal" item thanks to Karel Roose (on 4/01). Click here to go to the next FAQ in the "chain" of recent changes.] <br>仅仅因为我承认对你的友情，允许你访问我，并不自动地允许你的孩子访问我，并不自动地允许你的朋友访问我，并不自动地允许我访问你。 </p>
<p>我不见得信任我朋友的孩子。友元的特权不被继承。友元的派生类不一定是友元。如果 Fred 类声明Base类是友元，那么Base类的派生类不会自动地被赋予对于Fred的对象的访问特权。 <br>我不见得信任我朋友的朋友。友元的特权不被传递。友元的友元不一定是友元。如果Fred类声明Wilma类是友元，并且Wilma类声明Betty类是友元，那么Betty类不会自动地被赋予对于Fred的对象的访问特权。 <br>你不见得仅仅因为我声称你是我的朋友就信任我。友元的特权不是自反的。如果Fred类声明Wilma类是友元，则Wilma对象拥有访问Fred对象的特权，但Fred对象不会自动地拥有对Wilma对象的访问特权。 <br><font size=4>--------------------------------------------------------------------------------</font><br><br></p>
<p>具体的用法详细见下面的示例：<br></p>
<div style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><span style="COLOR: #000000">#include&nbsp;</span><span style="COLOR: #000000">&lt;</span><span style="COLOR: #000000">cstdlib</span><span style="COLOR: #000000">&gt;</span><span style="COLOR: #000000"><br>#include&nbsp;</span><span style="COLOR: #000000">&lt;</span><span style="COLOR: #000000">iostream</span><span style="COLOR: #000000">&gt;</span><span style="COLOR: #000000"><br><br></span><span style="COLOR: #0000ff">using</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">namespace</span><span style="COLOR: #000000">&nbsp;std;<br><br></span><span style="COLOR: #0000ff">class</span><span style="COLOR: #000000">&nbsp;Radius<br>{<br>&nbsp;&nbsp;&nbsp;&nbsp;friend&nbsp;</span><span style="COLOR: #0000ff">class</span><span style="COLOR: #000000">&nbsp;Circle;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">声明Circle为Radius的友元类</span><span style="COLOR: #008000"><br></span><span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;friend&nbsp;</span><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000">&nbsp;Show_r(Radius&nbsp;</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">n);&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">声明Show_r为友元函数</span><span style="COLOR: #008000"><br></span><span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">public</span><span style="COLOR: #000000">:<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Radius(</span><span style="COLOR: #0000ff">int</span><span style="COLOR: #000000">&nbsp;x)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;r&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;x;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #000000">~</span><span style="COLOR: #000000">Radius()<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">private</span><span style="COLOR: #000000">:<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">int</span><span style="COLOR: #000000">&nbsp;r;<br>};<br><br></span><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000">&nbsp;Show_r(Radius&nbsp;</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">n)<br>{<br>&nbsp;&nbsp;&nbsp;&nbsp;cout</span><span style="COLOR: #000000">&lt;&lt;</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">圆的半径为:&nbsp;</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">&lt;&lt;</span><span style="COLOR: #000000">n.r</span><span style="COLOR: #000000">&lt;&lt;</span><span style="COLOR: #000000">endl;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">调用Radius对象的私有成员变量r</span><span style="COLOR: #008000"><br></span><span style="COLOR: #000000">}<br><br></span><span style="COLOR: #0000ff">class</span><span style="COLOR: #000000">&nbsp;Circle<br>{<br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">public</span><span style="COLOR: #000000">:<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Circle()<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #000000">~</span><span style="COLOR: #000000">Circle()<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">double</span><span style="COLOR: #000000">&nbsp;area(Radius&nbsp;a)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;s&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;a.r&nbsp;</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">&nbsp;a.r&nbsp;</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">3.1415926</span><span style="COLOR: #000000">;&nbsp;&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">调用Radius对象的私有成员变量r</span><span style="COLOR: #008000"><br></span><span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">return</span><span style="COLOR: #000000">&nbsp;s;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">private</span><span style="COLOR: #000000">:<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">double</span><span style="COLOR: #000000">&nbsp;s;<br>};<br><br></span><span style="COLOR: #0000ff">int</span><span style="COLOR: #000000">&nbsp;main(</span><span style="COLOR: #0000ff">int</span><span style="COLOR: #000000">&nbsp;argc,&nbsp;</span><span style="COLOR: #0000ff">char</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">argv[])<br>{<br>&nbsp;&nbsp;&nbsp;&nbsp;Radius&nbsp;objRadius(</span><span style="COLOR: #000000">9</span><span style="COLOR: #000000">);<br>&nbsp;&nbsp;&nbsp;&nbsp;Circle&nbsp;objCircle;<br>&nbsp;&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;Show_r(objRadius);<br>&nbsp;&nbsp;&nbsp;&nbsp;cout</span><span style="COLOR: #000000">&lt;&lt;</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">面积为：</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">&lt;&lt;</span><span style="COLOR: #000000">objCircle.area(objRadius)</span><span style="COLOR: #000000">&lt;&lt;</span><span style="COLOR: #000000">endl;<br>&nbsp;&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;system(</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">PAUSE</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">);<br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">return</span><span style="COLOR: #000000">&nbsp;EXIT_SUCCESS;<br>}<br></span></div>
<img src ="http://www.cppblog.com/hktk/aggbug/96866.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/hktk/" target="_blank">海 阔 天 空</a> 2009-09-21 18:51 <a href="http://www.cppblog.com/hktk/archive/2009/09/21/96866.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>C++结构体字节对齐问题2</title><link>http://www.cppblog.com/hktk/archive/2009/09/21/96865.html</link><dc:creator>海 阔 天 空</dc:creator><author>海 阔 天 空</author><pubDate>Mon, 21 Sep 2009 10:44:00 GMT</pubDate><guid>http://www.cppblog.com/hktk/archive/2009/09/21/96865.html</guid><description><![CDATA[<p><strong><span lang=EN-US style="COLOR: lime; FONT-FAMILY: 宋体; mso-bidi-font-family: 宋体">1</span></strong><strong><span style="COLOR: lime; FONT-FAMILY: 宋体; mso-bidi-font-family: 宋体">。几个结构体例子：</span></strong><span lang=EN-US><o:p></o:p></span></p>
<p><font face=宋体><span lang=EN-US style="COLOR: red">struct{<br>short a1;<br>short a2;<br>short a3;<br>}A;</span><span lang=EN-US><o:p></o:p></span></font></p>
<p><span lang=EN-US><br><font face=宋体><span style="COLOR: red">struct{<br>long a1;<br>short a2;<br>}B;</span><o:p></o:p></font></span></p>
<p><span lang=EN-US><br><font face=宋体>sizeof( A)=6, sizeof( B)=8,</font></span><font face=宋体>为什么？</font><span lang=EN-US><br></span><font face=宋体>注：<span lang=EN-US>sizeof(short)=2,sizeof(long)=4<o:p></o:p></span></font></p>
<p><font face=宋体>因为：<span lang=EN-US>&#8220;</span>成员对齐有一个重要的条件，即每个成员按自己的方式对齐。其对齐的规则是，每个成员按其类型的对齐参数<span lang=EN-US>(</span>通常是这个类型的大小<span lang=EN-US>)</span>和指定对齐参数<span lang=EN-US>(</span>这里默认是<span lang=EN-US>8</span>字节<span lang=EN-US>)</span>中较小的一个对齐。<span style="COLOR: blue">并且结构的长度必须为所用过的所有对齐参数的整数倍，不够就补空字节。<span lang=EN-US>&#8221;(</span>引用<span lang=EN-US>)</span></span><span lang=EN-US><o:p></o:p></span></font></p>
<p><span lang=EN-US style="COLOR: blue"><br></span><font face=宋体>结构体<span lang=EN-US>A</span>中有<span lang=EN-US>3</span>个<span lang=EN-US>short</span>类型变量，各自以<span lang=EN-US>2</span>字节对齐，结构体对齐参数按默认的<span lang=EN-US>8</span>字节对齐，则<span lang=EN-US>a1,a2,a3</span>都取<span lang=EN-US>2</span>字节对齐，则<span lang=EN-US>sizeof(A)</span>为<span lang=EN-US>6</span>，其也是<span lang=EN-US>2</span>的整数倍；</font><span lang=EN-US><br><font face=宋体>B</font></span><font face=宋体>中<span lang=EN-US>a1</span>为<span lang=EN-US>4</span>字节对齐，<span lang=EN-US>a2</span>为<span lang=EN-US>2</span>字节对齐，结构体默认对齐参数为<span lang=EN-US>8</span>，则<span lang=EN-US>a1</span>取<span lang=EN-US>4</span>字节对齐，<span lang=EN-US>a2</span>取<span lang=EN-US>2</span>字节对齐，结构体大小<span lang=EN-US>6</span>字节，<span lang=EN-US>6</span>不为<span lang=EN-US>4</span>的整数倍，补空字节，增到<span lang=EN-US>8</span>时，符合所有条件，则<span lang=EN-US>sizeof(B)</span>为<span lang=EN-US>8</span>；<span lang=EN-US><o:p></o:p></span></font></p>
<p><font face=宋体>可以设置成对齐的<span lang=EN-US><o:p></o:p></span></font></p>
<p><span lang=EN-US><br><font face=宋体><span style="COLOR: red">#pragma pack(1)<br>#pragma pack(push)<br>#pragma pack(1)</span><o:p></o:p></font></span></p>
<p><span lang=EN-US><br><font face=宋体><span style="COLOR: red">struct{<br>short a1;<br>short a2;<br>short a3;<br>}A;</span><o:p></o:p></font></span></p>
<p><span lang=EN-US><br><font face=宋体><span style="COLOR: red">struct{<br>long a1;<br>short a2;<br>}B;</span><o:p></o:p></font></span></p>
<p><span lang=EN-US><br><font face=宋体><span style="COLOR: red">#pragma pack(pop)</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<o:p></o:p></font></span></p>
<p><font face=宋体>结果为<span lang=EN-US>sizeof( A)=6,sizeof( B)=6<o:p></o:p></span></font></p>
<p><span lang=EN-US><font face=宋体>************************<o:p></o:p></font></span></p>
<p><font face=宋体><span lang=EN-US style="COLOR: red">#pragma pack(8)</span><span lang=EN-US><o:p></o:p></span></font></p>
<p><span lang=EN-US><br><font face=宋体><span style="COLOR: red">struct S1{<br>&nbsp;&nbsp;&nbsp; char a;<br>&nbsp;&nbsp;&nbsp; long b;<br>};</span><o:p></o:p></font></span></p>
<p><span lang=EN-US><br><font face=宋体><span style="COLOR: red">struct S2 {<br>&nbsp;&nbsp;&nbsp; char c;<br>&nbsp;&nbsp;&nbsp; struct S1 d;<br>&nbsp;&nbsp;&nbsp; long long e;<br>};</span><o:p></o:p></font></span></p>
<p><span lang=EN-US><br><font face=宋体><span style="COLOR: red">#pragma pack()</span><o:p></o:p></font></span></p>
<p><span lang=EN-US><br><font face=宋体>sizeof(S2)</font></span><font face=宋体>结果为<span lang=EN-US>24.<o:p></o:p></span></font></p>
<p><span lang=EN-US><br></span><span style="COLOR: blue"><font face=宋体>成员对齐有一个重要的条件，即每个成员分别对齐，即每个成员按自己的方式对齐。</font><span lang=EN-US><br></span></span><font face=宋体>也就是说上面虽然指定了按<span lang=EN-US>8</span>字节对齐，但并不是所有的成员都是以<span lang=EN-US>8</span>字节对齐。其对齐的规则是，每个成员按其类型的对齐参数<span lang=EN-US>(</span>通常是这个类型的大小<span lang=EN-US>)</span>和指定对齐参数<span lang=EN-US>(</span>这里是<span lang=EN-US>8</span>字节<span lang=EN-US>)</span>中较小的一个对齐。并且结构的长度必须为所用过的所有对齐参数的整数倍，不够就补空字节。<span lang=EN-US><o:p></o:p></span></font></p>
<p><span lang=EN-US><br><font face=宋体>S1</font></span><font face=宋体>中，成员<span lang=EN-US>a</span>是<span lang=EN-US>1</span>字节默认按<span lang=EN-US>1</span>字节对齐，指定对齐参数为<span lang=EN-US>8</span>，这两个值中取<span lang=EN-US>1</span>，<span lang=EN-US>a</span>按<span lang=EN-US>1</span>字节对齐；成员<span lang=EN-US>b</span>是<span lang=EN-US>4</span>个字节，默认是按<span lang=EN-US>4</span>字节对齐，这时就按<span lang=EN-US>4</span>字节对齐，所以<span lang=EN-US>sizeof(S1)</span>应该为<span lang=EN-US>8</span>；<span lang=EN-US><o:p></o:p></span></font></p>
<p><span lang=EN-US><br><font face=宋体>S2 </font></span><font face=宋体>中，<span lang=EN-US>c</span>和<span lang=EN-US>S1</span>中的<span lang=EN-US>a</span>一样<span lang=EN-US>,</span>按<span lang=EN-US>1</span>字节对齐，而<span lang=EN-US>d </span>是个结构，它是<span lang=EN-US>8</span>个字节，它按什么对齐呢<span lang=EN-US>?</span>对于结构来说，它的默认对齐方式就是它的所有成员使用的对齐参数中最大的一个，<span lang=EN-US>S1</span>的就是<span lang=EN-US>4.</span>所以<span lang=EN-US>,</span>成员<span lang=EN-US>d</span>就是 按<span lang=EN-US>4</span>字节对齐。成员<span lang=EN-US>e</span>是<span lang=EN-US>8</span>个字节，它是默认按<span lang=EN-US>8</span>字节对齐<span lang=EN-US>,</span>和指定的一样，所以它对到<span lang=EN-US>8</span>字节的边界上<span lang=EN-US>,</span>这时，已经使用了<span lang=EN-US>12</span>个字节了，所以又添加了<span lang=EN-US>4</span>个字节 的空<span lang=EN-US>,</span>从第<span lang=EN-US>16</span>个字节开始放置成员<span lang=EN-US>e</span>。这时，长度为<span lang=EN-US>24</span>，已经可以被<span lang=EN-US>8(</span>成员<span lang=EN-US>e</span>按<span lang=EN-US>8</span>字节对齐<span lang=EN-US>)</span>整除<span lang=EN-US>.</span>这样， 一共使用了<span lang=EN-US>24</span>个字节。<span lang=EN-US><o:p></o:p></span></font></p>
<p><span lang=EN-US><br><font face=宋体>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; a&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; b<br>S1</font></span><font face=宋体>的内存布局：</font><font face=宋体><span lang=EN-US>1***, 1111,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; c&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; S1.a&nbsp;&nbsp;&nbsp; S1.b&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;e<br>S2</span>的内存布局：<span lang=EN-US>1***, 1***,&nbsp;&nbsp; 1111, ****11111111<o:p></o:p></span></font></p>
<p><font face=宋体>这里有三点很重要：</font><span lang=EN-US><br><span style="COLOR: blue"><font face=宋体>1.</font></span></span><span style="COLOR: blue"><font face=宋体>每个成员分别按自己的方式对齐<span lang=EN-US>,</span>并能最小化长度</font><span lang=EN-US><br><font face=宋体>2.</font></span><font face=宋体>复杂类型<span lang=EN-US>(</span>如结构<span lang=EN-US>)</span>的默认对齐方式是它最长的成员的对齐方式<span lang=EN-US>,</span>这样在成员是复杂类型时，可以最小化长度</font><span lang=EN-US><br><font face=宋体>3.</font></span><font face=宋体>对齐后的长度必须是成员中最大的对齐参数的整数倍，这样在处理数组时可以保证每一项都边界对齐</font></span><span lang=EN-US><o:p></o:p></span></p>
<p><font face=宋体>补充一下，对于数组，比如：</font><span lang=EN-US><br><font face=宋体>char a[3]</font></span><font face=宋体>；这种，它的对齐方式和分别写<span lang=EN-US>3</span>个<span lang=EN-US>char</span>是一样的。也就是说它还是按<span lang=EN-US>1</span>个字节对齐。</font><span lang=EN-US><br></span><font face=宋体>如果写：<span lang=EN-US> typedef char Array3[3]</span>；</font><span lang=EN-US><br><font face=宋体>Array3</font></span><font face=宋体>这种类型的对齐方式还是按<span lang=EN-US>1</span>个字节对齐，而不是按它的长度。</font><span lang=EN-US><br></span><font face=宋体>不论类型是什么<span lang=EN-US>,</span>对齐的边界一定是<span lang=EN-US>1,2,4,8,16,32,64....</span>中的一个。</font><span lang=EN-US><br><br><font face=宋体>/***********************/<br></font></span><font face=宋体>字节对齐详解<span lang=EN-US><o:p></o:p></span></font></p>
<p><span lang=EN-US><br></span><font face=宋体>为什么要对齐？</font><span lang=EN-US><br></span><font face=宋体><span style="COLOR: blue">现代计算机中内存空间都是按照<span lang=EN-US>byte</span>划分的，从理论上讲似乎对任何类型的变量的访问可以从任何地址开始，但实际情况是在访问特定类型变量的时候经常在特定的内存地址访问，这就需要各种类型数据按照一定的规则在空间上排列，而不是顺序的一个接一个的排放，这就是对齐。</span><span lang=EN-US><o:p></o:p></span></font></p>
<p><span lang=EN-US><br></span><font face=宋体>对齐的作用和原因：各个硬件平台对存储空间的处理上有很大的不同。<span style="COLOR: blue">一些平台对某些特定类型的 数据只能从某些特定地址开始存取。比如有些架构的<span lang=EN-US>CPU</span>在访问一个没有进行对齐的变量的时候会发生错误，那么在这种架构下编程必须保证字节对齐。其他平台 可能没有这种情况，但是最常见的是如果不按照适合其平台要求对数据存放进行对齐，会在存取效率上带来损失。</span>比如有些平台每次读都是从偶地址开始，如果一个<span lang=EN-US>int</span>型（假设为<span lang=EN-US>32</span>位系统）如果存放在偶地址开始的地方，那么一个读周期就可以读出这<span lang=EN-US>32bit</span>，而如果存放在奇地址开始的地方，就需要<span lang=EN-US> 2</span>个读周期，并对两次读出的结果的高低字节进行拼凑才能得到该<span lang=EN-US>32bit</span>数 据。显然在读取效率上下降很多。<span lang=EN-US><o:p></o:p></span></font></p>
<p><strong><span style="COLOR: lime; FONT-FAMILY: 宋体; mso-bidi-font-family: 宋体">二。字节对齐对程序的影响：</span></strong><span lang=EN-US><o:p></o:p></span></p>
<p><strong><span lang=EN-US style="COLOR: #ff6600"><br></span></strong><font face=宋体>先让我们看几个例子吧<span lang=EN-US>(32bit,x86</span>环境<span lang=EN-US>,gcc</span>编译器<span lang=EN-US>)</span>：</font><span lang=EN-US><br></span><font face=宋体>设结构体如下定义：<span lang=EN-US><o:p></o:p></span></font></p>
<p><span lang=EN-US><br><font face=宋体><span style="COLOR: red">struct A<br>{<br>&nbsp;&nbsp;&nbsp; int a;<br>&nbsp;&nbsp;&nbsp; char b;<br>&nbsp;&nbsp;&nbsp; short c;<br>};</span><o:p></o:p></font></span></p>
<p><span lang=EN-US><br><font face=宋体><span style="COLOR: red">struct B<br>{<br>&nbsp;&nbsp;&nbsp; char b;<br>&nbsp;&nbsp;&nbsp; int a;<br>&nbsp;&nbsp;&nbsp; short c;<br>};</span><o:p></o:p></font></span></p>
<p><span lang=EN-US><br></span><font face=宋体>现在已知<span lang=EN-US>32</span>位机器上各种数据类型的长度如下：<span lang=EN-US><o:p></o:p></span></font></p>
<p><span lang=EN-US><br><span style="COLOR: blue"><font face=宋体>char:1(</font></span></span><span style="COLOR: blue"><font face=宋体>有符号无符号同</font><font face=宋体><span lang=EN-US>)&nbsp;&nbsp;&nbsp; <br>short:2(</span>有符号无符号同</font><font face=宋体><span lang=EN-US>)&nbsp;&nbsp;&nbsp; <br>int:4(</span>有符号无符号同</font><font face=宋体><span lang=EN-US>)&nbsp;&nbsp;&nbsp; <br>long:4(</span>有符号无符号同</font><span lang=EN-US><font face=宋体>)&nbsp;&nbsp;&nbsp; <br>float:4&nbsp;&nbsp;&nbsp; double:8</font></span></span><span lang=EN-US><o:p></o:p></span></p>
<p><span lang=EN-US><br></span><font face=宋体>那么上面两个结构大小如何呢？</font><span lang=EN-US><br></span><font face=宋体>结果是：</font><span lang=EN-US><br><font face=宋体>sizeof(strcut A)</font></span><font face=宋体>值为</font><font face=宋体><span lang=EN-US>8<br>sizeof(struct B)</span>的值却是<span lang=EN-US>12<o:p></o:p></span></font></p>
<p><font face=宋体>结构体<span lang=EN-US>A</span>中包含了<span lang=EN-US>4</span>字节长度的<span lang=EN-US>int</span>一个，<span lang=EN-US>1</span>字节长度的<span lang=EN-US>char</span>一个和<span lang=EN-US>2</span>字节长度的<span lang=EN-US>short</span>型数据一个，<span lang=EN-US>B</span>也一样；按理说<span lang=EN-US>A</span>，<span lang=EN-US>B</span>大小应该都是<span lang=EN-US>7</span>字节。</font><span lang=EN-US><br></span><font face=宋体>之所以出现上面的结果是因为编译器要对数据成员在空间上进行对齐。上面是按照编译器的默认设置进行对齐的结果，那么我们是不是可以改变编译器的这种默认对齐设置呢，当然可以。例如：<span lang=EN-US><o:p></o:p></span></font></p>
<p><span lang=EN-US><br><span style="COLOR: red"><font face=宋体>#pragma pack (2) /*</font></span></span><font face=宋体><span style="COLOR: red">指定按<span lang=EN-US>2</span>字节对齐<span lang=EN-US>*/</span></span><span lang=EN-US><o:p></o:p></span></font></p>
<p><span lang=EN-US><br><font face=宋体><span style="COLOR: red">struct C<br>{<br>&nbsp;&nbsp;&nbsp; char b;<br>&nbsp;&nbsp;&nbsp; int a;<br>&nbsp;&nbsp;&nbsp; short c;<br>};</span><o:p></o:p></font></span></p>
<p><span lang=EN-US><br><span style="COLOR: red"><font face=宋体>#pragma pack () /*</font></span></span><font face=宋体><span style="COLOR: red">取消指定对齐，恢复缺省对齐<span lang=EN-US>*/</span></span><span lang=EN-US><o:p></o:p></span></font></p>
<p><span lang=EN-US><br><font face=宋体>sizeof(struct C)</font></span><font face=宋体>值是<span lang=EN-US>8</span>。<span lang=EN-US><o:p></o:p></span></font></p>
<p><span lang=EN-US><br></span><font face=宋体>修改对齐值为<span lang=EN-US>1</span>：<span lang=EN-US><o:p></o:p></span></font></p>
<p><span lang=EN-US><br><span style="COLOR: red"><font face=宋体>#pragma pack (1) /*</font></span></span><font face=宋体><span style="COLOR: red">指定按<span lang=EN-US>1</span>字节对齐<span lang=EN-US>*/</span></span><span lang=EN-US><o:p></o:p></span></font></p>
<p><span lang=EN-US><br><font face=宋体><span style="COLOR: red">struct D<br>{<br>&nbsp;&nbsp;&nbsp; char b;<br>&nbsp;&nbsp;&nbsp; int a;<br>&nbsp;&nbsp;&nbsp; short c;<br>};</span><o:p></o:p></font></span></p>
<p><span lang=EN-US><br><span style="COLOR: red"><font face=宋体>#pragma pack () /*</font></span></span><font face=宋体><span style="COLOR: red">取消指定对齐，恢复缺省对齐<span lang=EN-US>*/</span></span><span lang=EN-US><o:p></o:p></span></font></p>
<p><span lang=EN-US><br><font face=宋体>sizeof(struct D)</font></span><font face=宋体>值为<span lang=EN-US>7</span>。<span lang=EN-US><o:p></o:p></span></font></p>
<p><span lang=EN-US><br></span><font face=宋体>后面我们再讲解<span lang=EN-US>#pragma pack()</span>的作用。<span lang=EN-US><o:p></o:p></span></font></p>
<p><strong><span style="COLOR: lime; FONT-FAMILY: 宋体; mso-bidi-font-family: 宋体">三。编译器是按照什么样的原则进行对齐的？</span></strong><span lang=EN-US><o:p></o:p></span></p>
<p><span lang=EN-US><br></span><font face=宋体>先让我们看四个重要的基本概念：</font><span lang=EN-US><br><span style="COLOR: blue"><font face=宋体>1.</font></span></span><span style="COLOR: blue"><font face=宋体>数据类型自身的对齐值：对于<span lang=EN-US>char</span>型数据，其自身对齐值为<span lang=EN-US>1</span>，对于<span lang=EN-US>short</span>型为<span lang=EN-US>2</span>，对于<span lang=EN-US>int,float,double</span>类型，其自身对齐值为<span lang=EN-US>4</span>，单位字节。</font><span lang=EN-US><br><font face=宋体>2.</font></span><font face=宋体>结构体或者类的自身对齐值：其成员中自身对齐值最大的那个值。</font><span lang=EN-US><br><font face=宋体>3.</font></span><font face=宋体>指定对齐值：<span lang=EN-US>#pragma pack (value)</span>时的指定对齐值<span lang=EN-US>value</span>。</font><span lang=EN-US><br><font face=宋体>4.</font></span><font face=宋体>数据成员、结构体和类的有效对齐值：自身对齐值和指定对齐值中小的那个值。</font></span><span lang=EN-US><o:p></o:p></span></p>
<p><span lang=EN-US><br></span><font face=宋体>有了这些值，我们就可以很方便的来讨论具体数据结构的成员和其自身的对齐方式。<span style="COLOR: blue">有效对齐值<span lang=EN-US>N</span>是最终用来决定数据存放地址方式的值，最重要。有效对齐<span lang=EN-US>N</span>，就是 表示<span lang=EN-US>&#8220;</span>对齐在<span lang=EN-US>N</span>上<span lang=EN-US>&#8221;</span>，也就是说该数据的<span lang=EN-US>&#8220;</span>存放起始地址<span lang=EN-US>%N=<st1:chmetcnv w:st="on" TCSC="0" NumberType="1" Negative="False" HasSpace="False" SourceValue="0" UnitName="&#8221;">0&#8221;</st1:chmetcnv></span>。</span><span style="COLOR: black">而数据结构中的数据变量都是按定义的先后顺序来排放的。第一个数据变量的起始地址就是数据结构的起始地址。结构体的成员变量要对齐排放，结构体本身也要根据自身的有效对齐值圆整<span lang=EN-US>(</span>就是结构体成员变量占用总长度需要是对结构体有效对齐值的整数倍，结合下面例子理解<span lang=EN-US>)</span>。</span>这样就不能理解上面的几个例子的值了。<span lang=EN-US><o:p></o:p></span></font></p>
<p><span lang=EN-US><br></span><font face=宋体>例子分析：</font><span lang=EN-US><br></span><font face=宋体>分析例子<span lang=EN-US>B</span>：<span lang=EN-US><o:p></o:p></span></font></p>
<p><span lang=EN-US><br><font face=宋体><span style="COLOR: red">struct B<br>{<br>&nbsp;&nbsp;&nbsp; char b;<br>&nbsp;&nbsp;&nbsp; int a;<br>&nbsp;&nbsp;&nbsp; short c;<br>};</span><o:p></o:p></font></span></p>
<p><span lang=EN-US><br></span><font face=宋体>假设<span lang=EN-US>B</span>从地址空间<span lang=EN-US>0x0000</span>开始排放。该例子中没有定义指定对齐值，在笔者环境下，该值默认为<span lang=EN-US>4</span>。第一个成员变量<span lang=EN-US>b</span>的自身对齐值是<span lang=EN-US>1</span>，比指定或者默认指定对齐值<span lang=EN-US>4</span>小，所以其有效对齐值为<span lang=EN-US>1</span>，所以其存放地址<span lang=EN-US>0x0000</span>符合<span lang=EN-US>0x0000%1=0</span>。第二个成员变量<span lang=EN-US>a</span>，其自身对齐值为<span lang=EN-US>4</span>，所以有 效对齐值也为<span lang=EN-US>4</span>，所以只能存放在起始地址为<span lang=EN-US>0x0004</span>到<span lang=EN-US>0x0007</span>这四个连续的字节空间中，符合<span lang=EN-US>0x0004%4=0</span>，且紧靠第一个变量。第三个变量<span lang=EN-US> c</span>，自身对齐值为<span lang=EN-US>2</span>，所以有效对齐值也是<span lang=EN-US>2</span>，可以存放在<span lang=EN-US>0x0008</span>到<span lang=EN-US>0x0009</span>这两个字节空间中，符合<span lang=EN-US>0x0008%2=0</span>。所以从<span lang=EN-US>0x0000</span>到<span lang=EN-US> 0x0009</span>存放的都是<span lang=EN-US>B</span>内容。再看数据结构<span lang=EN-US>B</span>的自身对齐值为其变量中最大对齐值<span lang=EN-US>(</span>这里是<span lang=EN-US>b</span>）所以就是<span lang=EN-US>4</span>，所以结构体的有效对齐值也是<span lang=EN-US>4</span>。根据结构体圆整 的要求，<span lang=EN-US>0x0009</span>到<span lang=EN-US>0x0000=10</span>字节，（<span lang=EN-US>10</span>＋<span lang=EN-US>2</span>）％<span lang=EN-US>4</span>＝<span lang=EN-US>0</span>。所以<span lang=EN-US>0x<st1:chmetcnv w:st="on" TCSC="0" NumberType="1" Negative="False" HasSpace="False" SourceValue="0" UnitName="a">0000A</st1:chmetcnv></span>到<span lang=EN-US>0x000B</span>也为结构体<span lang=EN-US>B</span>所占用。故<span lang=EN-US>B</span>从<span lang=EN-US>0x0000</span>到<span lang=EN-US> 0x000B </span>共有<span lang=EN-US>12</span>个字节，<span lang=EN-US>sizeof(struct B)=12</span>；其实如果就这一个就来说它已将满足字节对齐了， 因为它的起始地址是<span lang=EN-US>0</span>，因此肯定是对齐的。之所以在后面补充<span lang=EN-US>2</span>个字节，是因为编译器为了实现结构数组的存取效率，试想如果我们定义了一个结构<span lang=EN-US>B</span>的数组，那么第一个结构起始地址是<span lang=EN-US>0</span>没有问题，但是第二个结构呢？按照数组的定义，数组中所有元素都是紧挨着的，如果我们不把结构的大小补充为<span lang=EN-US>4</span>的整数倍，那么下一 个结构的起始地址将是<span lang=EN-US>0x<st1:chmetcnv w:st="on" TCSC="0" NumberType="1" Negative="False" HasSpace="False" SourceValue="0" UnitName="a">0000A</st1:chmetcnv></span>，这显然不能满足结构的地址对齐了，因此我们要把结构补充成有效对齐大小的整数倍。其实诸如：对于<span lang=EN-US>char</span>型数据，其 自身对齐值为<span lang=EN-US>1</span>，对于<span lang=EN-US>short</span>型为<span lang=EN-US>2</span>，对于<span lang=EN-US>int</span>，<span lang=EN-US>float</span>，<span lang=EN-US>double</span>类型，其自身对齐值为<span lang=EN-US>4</span>，这些已有类型的自身对齐值也是基于数组考虑的，只是因为这些类型的长度已知了，所以他们的自身对齐值也就已知了。</font><span lang=EN-US><br></span><font face=宋体>同理，分析上面例子<span lang=EN-US>C</span>：<span lang=EN-US><o:p></o:p></span></font></p>
<p><span lang=EN-US><br><span style="COLOR: red"><font face=宋体>#pragma pack (2) /*</font></span></span><font face=宋体><span style="COLOR: red">指定按<span lang=EN-US>2</span>字节对齐<span lang=EN-US>*/</span></span><span lang=EN-US><o:p></o:p></span></font></p>
<p><span lang=EN-US style="COLOR: red"><br><font face=宋体>struct C<br>{<br>&nbsp;&nbsp;&nbsp; char b;<br>&nbsp;&nbsp;&nbsp; int a;<br>&nbsp;&nbsp;&nbsp; short c;<br>};</font></span><span lang=EN-US><o:p></o:p></span></p>
<p><span lang=EN-US><br><span style="COLOR: red"><font face=宋体>#pragma pack () /*</font></span></span><font face=宋体><span style="COLOR: red">取消指定对齐，恢复缺省对齐<span lang=EN-US>*/</span></span><span lang=EN-US><o:p></o:p></span></font></p>
<p><span lang=EN-US><br></span><font face=宋体>第一个变量<span lang=EN-US>b</span>的自身对齐值为<span lang=EN-US>1</span>，指定对齐值为<span lang=EN-US>2</span>，所以，其有效对齐值为<span lang=EN-US>1</span>，假设<span lang=EN-US>C</span>从<span lang=EN-US>0x0000</span>开始，那么<span lang=EN-US>b</span>存放在<span lang=EN-US>0x0000</span>，符合<span lang=EN-US> 0x0000%1= 0</span>；第二个变量，自身对齐值为<span lang=EN-US>4</span>，指定对齐值为<span lang=EN-US>2</span>，所以有效对齐值为<span lang=EN-US>2</span>，所以顺序存放在<span lang=EN-US>0x0002</span>、<span lang=EN-US>0x0003</span>、<span lang=EN-US>0x0004</span>、<span lang=EN-US>0x0005</span>四个连续字节中，符合<span lang=EN-US>0x0002%2=0</span>。第三个变量<span lang=EN-US>c</span>的自身对齐值为<span lang=EN-US>2</span>，所以有效对齐值为<span lang=EN-US>2</span>，顺序存放在<span lang=EN-US>0x0006</span>、<span lang=EN-US>0x0007</span>中，符合<span lang=EN-US> 0x0006%2=0</span>。所以从<span lang=EN-US>0x0000</span>到<span lang=EN-US>0x00007</span>共八字节存放的是<span lang=EN-US>C</span>的变量。又<span lang=EN-US>C</span>的自身对齐值为<span lang=EN-US>4</span>，所以<span lang=EN-US>C</span>的有效对齐值为<span lang=EN-US>2</span>。又<span lang=EN-US>8%2=0</span>，<span lang=EN-US>C </span>只占用<span lang=EN-US>0x0000</span>到<span lang=EN-US>0x0007</span>的八个字节。所以<span lang=EN-US>sizeof(struct C)=8</span>。<span lang=EN-US><o:p></o:p></span></font></p>
<p><strong><span style="COLOR: lime; FONT-FAMILY: 宋体; mso-bidi-font-family: 宋体">四。如何修改编译器的默认对齐值？<span lang=EN-US><o:p></o:p></span></span></strong></p>
<p><span lang=EN-US><br><font face=宋体>1.</font></span><font face=宋体>在<span lang=EN-US>VC IDE</span>中，可以这样修改：<span lang=EN-US style="COLOR: blue">[Project]|[Settings]</span>，<span lang=EN-US>c/c++</span>选项卡<span lang=EN-US style="COLOR: blue">Category</span>的<span lang=EN-US style="COLOR: blue">Code Generation</span>选项的<span lang=EN-US style="COLOR: blue">Struct Member Alignment</span>中修改，默认是<span lang=EN-US>8</span>字节。</font><span lang=EN-US><br><font face=宋体>2.</font></span><font face=宋体>在编码时，可以这样动态修改<span style="COLOR: blue">：<span lang=EN-US>#pragma pack</span>（<span lang=EN-US>value</span>）</span> 。注意：是<span lang=EN-US>pragma</span>而不是<span lang=EN-US>progma.<o:p></o:p></span></font></p>
<p><strong><span style="COLOR: lime; FONT-FAMILY: 宋体; mso-bidi-font-family: 宋体">五。针对字节对齐，我们在编程中如何考虑？</span></strong><span lang=EN-US><o:p></o:p></span></p>
<p><font face=宋体>如果在编程的时候要考虑节约空间的话，那么我们只需要假定结构的首地址是<span lang=EN-US>0</span>，然后各个变量按照上面的原则进行排列即可，基本的原则就是把结构中的变量按照类型大小，从小到大声明，尽量减少中间的填补空间。还有一种就是为了以空间换取时间的效率，我们显示的进行填补空间进行对齐，比如：有一种使用空间 换时间做法是显式的插入<span lang=EN-US>reserved</span>成员：<span lang=EN-US><o:p></o:p></span></font></p>
<p><span lang=EN-US style="COLOR: red"><font face=宋体>struct A{<br>&nbsp;&nbsp;&nbsp;&nbsp; char a;<br>&nbsp;&nbsp;&nbsp;&nbsp; char reserved[3];//</font></span><span style="COLOR: red"><font face=宋体>使用空间换时间</font><span lang=EN-US><br><font face=宋体>&nbsp;&nbsp;&nbsp;&nbsp; int b;<br>}</font></span></span><span lang=EN-US><o:p></o:p></span></p>
<p><font face=宋体><span lang=EN-US>reserved</span>成员对我们的程序没有什么意义，它只是起到填补空间以达到字节对齐的目的，当然即使不加这个成员通常编译器也会给我们自动填补对齐，我们自己加上它只是起到显式的提醒作用。<span lang=EN-US><o:p></o:p></span></font></p>
<p><strong><span style="COLOR: lime; FONT-FAMILY: 宋体; mso-bidi-font-family: 宋体">六。字节对齐可能带来的隐患：</span></strong><span lang=EN-US><o:p></o:p></span></p>
<p><span lang=EN-US><br></span><font face=宋体>代码中关于对齐的隐患，很多是隐式的。比如在强制类型转换的时候。例如：<span lang=EN-US><o:p></o:p></span></font></p>
<p><span lang=EN-US><br><font face=宋体><span style="COLOR: red">unsigned int i = 0x12345678;<br>unsigned char *p=NULL;<br>unsigned short *p1=NULL;</span><o:p></o:p></font></span></p>
<p><font face=宋体><span lang=EN-US style="COLOR: red">p=&amp;i;<br>*p=0x00;<br>p1=(unsigned short *)(p+1);<br>*p1=0x0000;</span><span lang=EN-US><o:p></o:p></span></font></p>
<p><span lang=EN-US><br></span><font face=宋体>最后两句代码，从奇数边界去访问<span lang=EN-US>unsignedshort</span>型变量，显然不符合对齐的规定。</font><span lang=EN-US><br></span><font face=宋体>在<span lang=EN-US>x86</span>上，类似的操作只会影响效率，但是在<span lang=EN-US>MIPS</span>或者<span lang=EN-US>sparc</span>上，可能就是一个<span lang=EN-US>error</span>，因为它们要求必须字节对齐。<span lang=EN-US><o:p></o:p></span></font></p>
<p><strong><span style="COLOR: lime; FONT-FAMILY: 宋体; mso-bidi-font-family: 宋体">七。如何查找与字节对齐方面的问题：</span></strong><span lang=EN-US><o:p></o:p></span></p>
<p><span lang=EN-US><br></span><font face=宋体>如果出现对齐或者赋值问题首先查看</font><span lang=EN-US><br><font face=宋体>1. </font></span><font face=宋体>编译器的<span lang=EN-US>big little</span>端设置</font><span lang=EN-US><br><font face=宋体>2. </font></span><font face=宋体>看这种体系本身是否支持非对齐访问</font><span lang=EN-US><br><font face=宋体>3. </font></span><font face=宋体>如果支持看设置了对齐与否，如果没有则看访问时需要加某些特殊的修饰来标志其特殊访问操作。<span lang=EN-US><o:p></o:p></span></font></p>
<p><font face=宋体><span lang=EN-US>ARM</span>下的对齐处理<span lang=EN-US><o:p></o:p></span></font></p>
<p><span lang=EN-US><br><font face=宋体><span style="COLOR: blue">from DUI0067D_ADS1_2_CompLib</span><o:p></o:p></font></span></p>
<p><font face=宋体><span lang=EN-US style="COLOR: blue">3.13 type qulifiers</span><span lang=EN-US><o:p></o:p></span></font></p>
<p><font face=宋体>有部分摘自<span lang=EN-US>ARM</span>编译器文档对齐部分<span lang=EN-US><o:p></o:p></span></font></p>
<p><font face=宋体>对齐的使用：</font><span lang=EN-US><br><font face=宋体>1.__align(num)<br>&nbsp;&nbsp; </font></span><font face=宋体>这个用于修改最高级别对象的字节边界。在汇编中使用<span lang=EN-US>LDRD</span>或者<span lang=EN-US>STRD</span>时<span lang=EN-US>&nbsp;&nbsp; </span>就要用到此命令<span lang=EN-US>__align(8)</span>进行修饰限制。来保证数据对象是相应对齐。<span lang=EN-US>&nbsp;&nbsp; </span>这个修饰对象的命令最大是<span lang=EN-US>8</span>个字节限制，可以让<span lang=EN-US>2</span>字节的对象进行<span lang=EN-US>4</span>字节<span lang=EN-US>&nbsp;&nbsp; </span>对齐，但是不能让<span lang=EN-US>4</span>字节的对象<span lang=EN-US>2</span>字节对齐。<span lang=EN-US>&nbsp;&nbsp; __align</span>是存储类修改，他只修饰最高级类型对象不能用于结构或者函数对象。</font><span lang=EN-US><br><font face=宋体>&nbsp;&nbsp; <br>2.__packed <br>__packed</font></span><font face=宋体>是进行一字节对齐<span lang=EN-US>1.</span>不能对<span lang=EN-US>packed</span>的对象进行对齐<span lang=EN-US>2.</span>所有对象的读写访问都进行非对齐访问</font><span lang=EN-US><br><font face=宋体>3.float</font></span><font face=宋体>及包含<span lang=EN-US>float</span>的结构联合及未用<span lang=EN-US>__packed</span>的对象将不能字节对齐</font><span lang=EN-US><br><font face=宋体>4.__packed</font></span><font face=宋体>对局部整形变量无影响</font><span lang=EN-US><br><font face=宋体>5.</font></span><font face=宋体>强制由<span lang=EN-US>unpacked</span>对象向<span lang=EN-US>packed</span>对象转化是未定义<span lang=EN-US>,</span>整形指针可以合法定义为<span lang=EN-US>packed</span>。<span lang=EN-US> __packed int* p</span>；<span lang=EN-US>//__packed int </span>则没有意义</font><span lang=EN-US><br><font face=宋体>6.</font></span><font face=宋体>对齐或非对齐读写访问带来问题<span lang=EN-US><o:p></o:p></span></font></p>
<p><span lang=EN-US><br><span style="COLOR: red"><font face=宋体>__packed struct STRUCT_TEST<br>{<br>char a;<br>int b;<br>char c;<br>} ;&nbsp;&nbsp;&nbsp; //</font></span></span><span style="COLOR: red"><font face=宋体>定义如下结构此时<span lang=EN-US>b</span>的起始地址一定是不对齐的</font><span lang=EN-US><br><font face=宋体>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //</font></span><font face=宋体>在栈中访问<span lang=EN-US>b</span>可能有问题<span lang=EN-US>,</span>因为栈上数据肯定是对齐访问</font><font face=宋体><span lang=EN-US>[from CL]<br>//</span>将下面变量定义成全局静态不在栈上 </font></span><span lang=EN-US><o:p></o:p></span></p>
<p><span lang=EN-US><br><span style="COLOR: red"><font face=宋体>static char* p;<br>static struct STRUCT_TEST a;<br>void Main()<br>{<br>__packed int* q; //</font></span></span><font face=宋体><span style="COLOR: red">此时定义成<span lang=EN-US>__packed</span>来修饰当前<span lang=EN-US>q</span>指向为非对齐的数据地址下面的访问则可以</span><span lang=EN-US><o:p></o:p></span></font></p>
<p><span lang=EN-US style="COLOR: red"><font face=宋体>p = (char*)&amp;a;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br>q = (int*)(p+1);&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br><br>*q = 0x87654321; <br>/*&nbsp;&nbsp; <br></font></span><span style="COLOR: red"><font face=宋体>得到赋值的汇编指令很清楚</font><span lang=EN-US><br><font face=宋体>ldr&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; r5,0x20001590 ; = #0x12345678<br>[0xe<st1:chmetcnv w:st="on" TCSC="0" NumberType="1" Negative="False" HasSpace="False" SourceValue="1" UnitName="a">1a</st1:chmetcnv>00005]&nbsp;&nbsp; mov&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; r0,r5<br>[0xeb0000b0]&nbsp;&nbsp; bl&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; __rt_uwrite4 //</font></span><font face=宋体>在此处调用一个写<span lang=EN-US>4byte</span>的操作函数</font><font face=宋体><span lang=EN-US> <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br>[0xe<st1:chmetcnv w:st="on" TCSC="0" NumberType="1" Negative="False" HasSpace="False" SourceValue="5" UnitName="C">5c</st1:chmetcnv>10000]&nbsp;&nbsp; strb&nbsp;&nbsp;&nbsp;&nbsp; r0,[r1,#0]&nbsp;&nbsp; //</span>函数进行<span lang=EN-US>4</span>次<span lang=EN-US>strb</span>操作然后返回保证了数据正确的访问</font><span lang=EN-US><br><font face=宋体>[0xe<st1:chmetcnv w:st="on" TCSC="0" NumberType="1" Negative="False" HasSpace="False" SourceValue="1" UnitName="a">1a</st1:chmetcnv>02420]&nbsp;&nbsp; mov&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; r2,r0,lsr #8<br>[0xe<st1:chmetcnv w:st="on" TCSC="0" NumberType="1" Negative="False" HasSpace="False" SourceValue="5" UnitName="C">5c</st1:chmetcnv>12001]&nbsp;&nbsp; strb&nbsp;&nbsp;&nbsp;&nbsp; r2,[r1,#1]<br>[0xe<st1:chmetcnv w:st="on" TCSC="0" NumberType="1" Negative="False" HasSpace="False" SourceValue="1" UnitName="a">1a</st1:chmetcnv>02820]&nbsp;&nbsp; mov&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; r2,r0,lsr #16<br>[0xe<st1:chmetcnv w:st="on" TCSC="0" NumberType="1" Negative="False" HasSpace="False" SourceValue="5" UnitName="C">5c</st1:chmetcnv>12002]&nbsp;&nbsp; strb&nbsp;&nbsp;&nbsp;&nbsp; r2,[r1,#2]<br>[0xe<st1:chmetcnv w:st="on" TCSC="0" NumberType="1" Negative="False" HasSpace="False" SourceValue="1" UnitName="a">1a</st1:chmetcnv><st1:chmetcnv w:st="on" TCSC="0" NumberType="1" Negative="False" HasSpace="False" SourceValue="2" UnitName="C">02c</st1:chmetcnv>20]&nbsp;&nbsp; mov&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; r2,r0,lsr #24<br>[0xe<st1:chmetcnv w:st="on" TCSC="0" NumberType="1" Negative="False" HasSpace="False" SourceValue="5" UnitName="C">5c</st1:chmetcnv>12003]&nbsp;&nbsp; strb&nbsp;&nbsp;&nbsp;&nbsp; r2,[r1,#3]<br>[0xe<st1:chmetcnv w:st="on" TCSC="0" NumberType="1" Negative="False" HasSpace="False" SourceValue="1" UnitName="a">1a</st1:chmetcnv><st1:chmetcnv w:st="on" TCSC="0" NumberType="1" Negative="False" HasSpace="False" SourceValue="0" UnitName="F">0f</st1:chmetcnv>00e]&nbsp;&nbsp; mov&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; pc,r14<br>*/</font></span></span><span lang=EN-US><o:p></o:p></span></p>
<p><span lang=EN-US style="COLOR: red"><font face=宋体>/*<br></font></span><span style="COLOR: red"><font face=宋体>如果<span lang=EN-US>q</span>没有加<span lang=EN-US>__packed</span>修饰则汇编出来指令是这样直接会导致奇地址处访问失败</font><span lang=EN-US><br><font face=宋体>[0xe<st1:chmetcnv w:st="on" TCSC="0" NumberType="1" Negative="False" HasSpace="False" SourceValue="59" UnitName="F">59f</st1:chmetcnv>2018]&nbsp;&nbsp; ldr&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; r2,0x20001594 ; = #0x87654321<br>[0xe5812000]&nbsp;&nbsp; str&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; r2,[r1,#0]<br>*/</font></span></span><span lang=EN-US><o:p></o:p></span></p>
<p><span lang=EN-US style="COLOR: red"><font face=宋体>//</font></span><span style="COLOR: red"><font face=宋体>这样可以很清楚的看到非对齐访问是如何产生错误的</font><span lang=EN-US><br><font face=宋体>//</font></span><font face=宋体>以及如何消除非对齐访问带来问题</font><span lang=EN-US><br><font face=宋体>//</font></span><font face=宋体>也可以看到非对齐访问和对齐访问的指令差异导致效率问题</font><span lang=EN-US><br><font face=宋体>}</font></span></span><span lang=EN-US><o:p></o:p></span></p>
<p><font face=宋体><span lang=EN-US>sizeof</span>进行结构体大小的判断<span lang=EN-US><o:p></o:p></span></font></p>
<p><span lang=EN-US style="COLOR: red"><font face=宋体>typedef struct<br>{<br>&nbsp;&nbsp;&nbsp; int a;<br>&nbsp;&nbsp;&nbsp; char b;<br>}A_t;<br>typedef struct<br>{<br>&nbsp;&nbsp;&nbsp; int a;<br>&nbsp;&nbsp;&nbsp; char b;<br>&nbsp;&nbsp;&nbsp; char c;<br>}B_t;<br>typedef struct<br>{<br>&nbsp;&nbsp;&nbsp; char a;<br>&nbsp;&nbsp;&nbsp; int b;<br>&nbsp;&nbsp;&nbsp; char c;<br>}C_t;<br>void main()<br>{<br>&nbsp;&nbsp;&nbsp; char*a=0;<br>&nbsp;&nbsp;&nbsp; cout&lt;&lt;sizeof(a)&lt;&lt;endl;//4<br>&nbsp;&nbsp;&nbsp; cout&lt;&lt;sizeof(*a)&lt;&lt;endl;//1--</font></span><span style="COLOR: red"><font face=宋体>这个能理解</font><span lang=EN-US><br><font face=宋体>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; cout&lt;&lt;sizeof(A_t)&lt;&lt;endl;//8<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; cout&lt;&lt;sizeof(B_t)&lt;&lt;endl;//8<br>&nbsp;&nbsp;&nbsp; cout&lt;&lt;sizeof(C_t)&lt;&lt;endl;//12<br>}</font></span></span><span lang=EN-US><o:p></o:p></span></p>
<p><span lang=EN-US><br></span><font face=宋体>为什么是这样的结果啊？<span lang=EN-US><o:p></o:p></span></font></p>
<p><span lang=EN-US><br><font face=宋体>2. </font></span><font face=宋体>语法：</font><font face=宋体><span lang=EN-US> <br>sizeof</span>有三种语法形式，如下：</font><span lang=EN-US><font face=宋体> <br><span style="COLOR: blue">1) sizeof( object ); // sizeof( </span></font></span><span style="COLOR: blue"><font face=宋体>对象</font><font face=宋体><span lang=EN-US> ); <br>2) sizeof( type_name ); // sizeof( </span>类型</font><font face=宋体><span lang=EN-US> ); <br>3) sizeof object; // sizeof </span>对象<span lang=EN-US>;</span></font></span><span lang=EN-US><o:p></o:p></span></p>
<p><span lang=EN-US><br><font face=宋体>5. </font></span><font face=宋体>指针变量的</font><font face=宋体><span lang=EN-US>sizeof <br></span><span style="COLOR: blue">既然是来存放地址的，那么它当然等于计算机内部地址总线的宽度。所以在<span lang=EN-US>32</span>位计算机中，一个指针变量的返回值必定是<span lang=EN-US>4</span>（以字节为单位），可以预计，在将来的<span lang=EN-US>64</span>位系统中指针变量的<span lang=EN-US>sizeof</span>结果为<span lang=EN-US>8</span>。</span><span lang=EN-US><o:p></o:p></span></font></p>
<p><span lang=EN-US><br><span style="COLOR: red"><font face=宋体>char* pc = "abc"; <br>int* pi; <br>string* ps; <br>char** ppc = &amp;pc; <br>void (*pf)();// </font></span></span><font face=宋体><span style="COLOR: red">函数指针</span><span style="COLOR: blue"> </span><span lang=EN-US><o:p></o:p></span></font></p>
<p><span lang=EN-US style="COLOR: blue"><br><font face=宋体>sizeof( pc ); // </font></span><span style="COLOR: blue"><font face=宋体>结果为</font><font face=宋体><span lang=EN-US>4 <br>sizeof( pi ); // </span>结果为</font><font face=宋体><span lang=EN-US>4 <br>sizeof( ps ); // </span>结果为</font><font face=宋体><span lang=EN-US>4 <br>sizeof( ppc ); // </span>结果为</font><font face=宋体><span lang=EN-US>4 <br>sizeof( pf );// </span>结果为<span lang=EN-US>4 </span></font></span><span lang=EN-US><o:p></o:p></span></p>
<p><span lang=EN-US><br></span><font face=宋体>指针变量的<span lang=EN-US>sizeof</span>值与指针所指的对象没有任何关系，正是由于所有的指针变量所占内存大小相等，所以<span lang=EN-US>MFC</span>消息处理函数使用两个参数<span lang=EN-US>WPARAM</span>、<span lang=EN-US>LPARAM</span>就能传递各种复杂的消息结构（使用指向结构体的指针）。<span lang=EN-US><o:p></o:p></span></font></p>
<p><span lang=EN-US><br><font face=宋体>6. </font></span><font face=宋体>数组的</font><font face=宋体><span lang=EN-US>sizeof <br></span><span style="COLOR: blue">数组的<span lang=EN-US>sizeof</span>值等于数组所占用的内存字节数</span><span lang=EN-US><o:p></o:p></span></font></p>
<p><font face=宋体>如：</font><font face=宋体><span lang=EN-US> <br>char a1[] = "abc"; <br>int a2[3]; <br>sizeof( a1 ); // </span>结果为<span lang=EN-US>4</span>，字符串末尾还存在一个<span lang=EN-US>NULL</span>终止符</font><font face=宋体><span lang=EN-US> <br>sizeof( a2 ); // </span>结果为<span lang=EN-US>3*4=12</span>（依赖于<span lang=EN-US>int</span>）</font><font face=宋体><span lang=EN-US> <br></span>一些朋友刚开始时把<span lang=EN-US>sizeof</span>当作了求数组元素的个数，现在，你应该知道这是不对的，那么应该怎么求数组元素的个数呢？<span lang=EN-US>Easy</span>，通常有下面两种写法：<span lang=EN-US><o:p></o:p></span></font></p>
<p><span lang=EN-US><br><span style="COLOR: red"><font face=宋体>int c1 = sizeof( a1 ) / sizeof( char ); // </font></span></span><span style="COLOR: red"><font face=宋体>总长度<span lang=EN-US>/</span>单个元素的长度</font><font face=宋体><span lang=EN-US> <br>int c2 = sizeof( a1 ) / sizeof( a1[0] ); // </span>总长度<span lang=EN-US>/</span>第一个元素的长度</font></span><span lang=EN-US><o:p></o:p></span></p>
<p><span lang=EN-US><br></span><font face=宋体>写到这里，提一问，下面的<span lang=EN-US>c3</span>，<span lang=EN-US>c4</span>值应该是多少呢？<span lang=EN-US><o:p></o:p></span></font></p>
<p><span lang=EN-US><br><font face=宋体><span style="COLOR: red">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>} </span><o:p></o:p></font></span></p>
<p><span lang=EN-US><br></span><font face=宋体>也许当你试图回答<span lang=EN-US>c4</span>的值时已经意识到<span lang=EN-US>c3</span>答错了，是的，<span lang=EN-US>c3!=3</span>。这里函数参数<span lang=EN-US>a3</span>已不再是数组类型，而是蜕变成指针，相当于<span lang=EN-US> char* a3</span>，为什么？仔细想想就不难明白，我们调用函数<span lang=EN-US>foo1</span>时，程序会在栈上分配一个大小为<span lang=EN-US>3</span>的数组吗？不会！数组是<span lang=EN-US>&#8220;</span>传址<span lang=EN-US>&#8221;</span>的，调用者只需将实参的地址传递过去，所以<span lang=EN-US>a3</span>自然为指针类型（<span lang=EN-US>char*</span>），<span lang=EN-US>c3</span>的值也就为<span lang=EN-US>4</span>。<span lang=EN-US><o:p></o:p></span></font></p>
<p><font face=宋体><span lang=EN-US>7. </span>结构体的</font><font face=宋体><span lang=EN-US>sizeof <br></span>这是初学者问得最多的一个问题，所以这里有必要多费点笔墨。让我们先看一个结构体：<span lang=EN-US><o:p></o:p></span></font></p>
<p><span lang=EN-US><br><font face=宋体><span style="COLOR: red">struct S1 <br>{ <br>char c; <br>int i; <br>}; </span><o:p></o:p></font></span></p>
<p><span lang=EN-US><br></span><font face=宋体>问<span lang=EN-US>sizeof(s1)</span>等于多少？聪明的你开始思考了，<span lang=EN-US>char</span>占<span lang=EN-US>1</span>个字节，<span lang=EN-US>int</span>占<span lang=EN-US>4</span>个字节，那么加起来就应该是<span lang=EN-US>5</span>。是这样吗？你在你机器上试过了吗？也许你是对的，但很可能你是错的！<span lang=EN-US>VC6</span>中按默认设置得到的结果为<span lang=EN-US>8</span>。</font><font face=宋体><span lang=EN-US> <br>Why</span>？为什么受伤的总是我？请不要沮丧，我们来好好琢磨一下<span lang=EN-US>sizeof</span>的定义<span lang=EN-US>——sizeof</span>的结果等于对象或者类型所占的内存字节数，好吧，那就让我们来看看<span lang=EN-US>S1</span>的内存分配情况：<span lang=EN-US><o:p></o:p></span></font></p>
<p><span lang=EN-US><br><font face=宋体>S1 s1 = { a , 0xFFFFFFFF };<o:p></o:p></font></span></p>
<p><span lang=EN-US><br></span><font face=宋体>定义上面的变量后，加上断点，运行程序，观察<span lang=EN-US>s1</span>所在的内存，你发现了什么？</font><font face=宋体><span lang=EN-US> <br></span>以我的<span lang=EN-US>VC6.0</span>为例，<span lang=EN-US>s1</span>的地址为<span lang=EN-US>0x0012FF78</span>，其数据内容如下：<span lang=EN-US><o:p></o:p></span></font></p>
<p><span lang=EN-US><br><font face=宋体>0012FF78: 61 CC CC CC FF FF FF FF<o:p></o:p></font></span></p>
<p><span lang=EN-US><br></span><font face=宋体>发现了什么？怎么中间夹杂了<span lang=EN-US>3</span>个字节的<span lang=EN-US>CC</span>？看看<span lang=EN-US>MSDN</span>上的说明：<span lang=EN-US><o:p></o:p></span></font></p>
<p><span lang=EN-US><br><font face=宋体>When applied to a structure type or variable, sizeof returns the actual size,<o:p></o:p></font></span></p>
<p><span lang=EN-US><font face=宋体>which may include padding bytes inserted for alignment.<o:p></o:p></font></span></p>
<p><span lang=EN-US><br></span><font face=宋体>原来如此，这就是传说中的字节对齐啊！一个重要的话题出现了。<span style="COLOR: blue">为什么需要字节对齐？计算机组成原理教导我们这样有助于加快计算机的取数速度，否则就得多花指令周期了。</span>为此，编译器默认会对结构体进行处理（实际上其它地方的数据变量也是如此），让宽度为<span lang=EN-US>2</span>的基本数据类型（<span lang=EN-US>short</span>等）都位于能被<span lang=EN-US>2</span>整除的地址上，让宽度为<span lang=EN-US> 4</span>的基本数据类型（<span lang=EN-US>int</span>等）都位于能被<span lang=EN-US>4</span>整除的地址上，以此类推。这样，两个数中间就可能需要加入填充字节，所以整个结构体的<span lang=EN-US>sizeof</span>值就增长了。 让我们交换一下<span lang=EN-US>S1</span>中<span lang=EN-US>char</span>与<span lang=EN-US>int</span>的位置：<span lang=EN-US><o:p></o:p></span></font></p>
<p><span lang=EN-US><br><font face=宋体><span style="COLOR: red">struct S2 <br>{ <br>int i; <br>char c; <br>}; </span><o:p></o:p></font></span></p>
<p><span lang=EN-US><br></span><font face=宋体>看看<span lang=EN-US>sizeof(S2)</span>的结果为多少，怎么还是<span lang=EN-US>8</span>？再看看内存，原来成员<span lang=EN-US>c</span>后面仍然有<span lang=EN-US>3</span>个填充字节，这又是为什么啊？别着急，下面总结规律。<span lang=EN-US><o:p></o:p></span></font></p>
<p><font face=宋体>字节对齐的细节和编译器实现相关，但一般而言，满足三个准则：</font><span lang=EN-US><font face=宋体> <br><span style="COLOR: blue">1) </span></font></span><span style="COLOR: blue"><font face=宋体>结构体变量的首地址能够被其最宽基本类型成员的大小所整除；</font><font face=宋体><span lang=EN-US> <br>2) </span>结构体每个成员相对于结构体首地址的偏移量（<span lang=EN-US>offset</span>）都是成员大小的整数倍，如有需要编译器会在成员之间加上填充字节（<span lang=EN-US>internal adding</span>）；</font><font face=宋体><span lang=EN-US> <br>3) </span>结构体的总大小为结构体最宽基本类型成员大小的整数倍，如有需要编译器会在最末一个成员之后加上填充字节（<span lang=EN-US>trailing padding</span>）。</font></span><span lang=EN-US><o:p></o:p></span></p>
<p><span lang=EN-US><br></span><font face=宋体>对于上面的准则，有几点需要说明：</font><font face=宋体><span lang=EN-US> <br>1) </span>前面不是说结构体成员的地址是其大小的整数倍，怎么又说到偏移量了呢？因为有了第<span lang=EN-US>1</span>点存在，所以我们就可以只考虑成员的偏移量，这样思考起来简单。想想为什么。结构体某个成员相对于结构体首地址的偏移量可以通过宏<span lang=EN-US>offsetof()</span>来获得，这个宏也在<span lang=EN-US>stddef.h</span>中定义，如下：<span lang=EN-US><o:p></o:p></span></font></p>
<p><span lang=EN-US><br><font face=宋体><span style="COLOR: red">#define offsetof(s,m) (size_t)&amp;(((s *)0)-&gt;m)</span><o:p></o:p></font></span></p>
<p><span lang=EN-US><br></span><font face=宋体>例如，想要获得<span lang=EN-US>S2</span>中<span lang=EN-US>c</span>的偏移量，方法为</font><font face=宋体><span lang=EN-US> <br>size_t pos = offsetof(S2, c);// pos</span>等于<span lang=EN-US>4<o:p></o:p></span></font></p>
<p><span lang=EN-US><br><font face=宋体>2) </font></span><font face=宋体>基本类型是指前面提到的像<span lang=EN-US>char</span>、<span lang=EN-US>short</span>、<span lang=EN-US>int</span>、<span lang=EN-US>float</span>、<span lang=EN-US>double</span>这样的内置数据类型，这里所说的<span lang=EN-US>&#8220;</span>数据宽度<span lang=EN-US>&#8221;</span>就是指其<span lang=EN-US>sizeof</span>的大小。由于结构体的成员可以是复合类型，比如另外一个结构体，所以在寻找最宽基本类型成员时，应当包括复合类型成员的子成员，而不是把复合成员看成是一个整 体。但在确定复合类型成员的偏移位置时则是将复合类型作为整体看待。这里叙述起来有点拗口，思考起来也有点挠头，还是让我们看看例子吧（具体数值仍以<span lang=EN-US> VC6</span>为例，以后不再说明）：<span lang=EN-US><o:p></o:p></span></font></p>
<p><span lang=EN-US><br><font face=宋体><span style="COLOR: red">struct S3 <br>{ <br>char c1; <br>S1 s; <br>char c2 <br>}; </span><o:p></o:p></font></span></p>
<p><span lang=EN-US><br><font face=宋体>S1</font></span><font face=宋体>的最宽简单成员的类型为<span lang=EN-US>int</span>，<span lang=EN-US>S3</span>在考虑最宽简单类型成员时是将<span lang=EN-US>S1&#8220;</span>打散<span lang=EN-US>&#8221;</span>看的，所以<span lang=EN-US>S3</span>的最宽简单类型为<span lang=EN-US>int</span>，这样，通过<span lang=EN-US>S3</span>定义的变量，其存储空间首地址需要被<span lang=EN-US>4</span>整除，整个<span lang=EN-US>sizeof(S3)</span>的值也应该被<span lang=EN-US>4</span>整除。</font><span lang=EN-US><br><font face=宋体>c1 </font></span><font face=宋体>的偏移量为<span lang=EN-US>0</span>，<span lang=EN-US>s</span>的偏移量呢？这时<span lang=EN-US>s</span>是一个整体，它作为结构体变量也满足前面三个准则，所以其大小为<span lang=EN-US>8</span>，偏移量为<span lang=EN-US>4</span>，<span lang=EN-US>c1</span>与<span lang=EN-US>s</span>之间便需要<span lang=EN-US>3</span>个填充字节，而<span lang=EN-US> c2</span>与<span lang=EN-US>s</span>之间就不需要了，所以<span lang=EN-US>c2</span>的偏移量为<span lang=EN-US>12</span>，算上<span lang=EN-US>c2</span>的大小为<span lang=EN-US>13</span>，<span lang=EN-US>13</span>是不能被<span lang=EN-US>4</span>整除的，这样末尾还得补上<span lang=EN-US>3</span>个填充字节。最后得到<span lang=EN-US>sizeof (S3)</span>的值为<span lang=EN-US>16</span>。</font><font face=宋体><span lang=EN-US> <br></span>通过上面的叙述，我们可以得到一个公式：<span lang=EN-US><o:p></o:p></span></font></p>
<p><span lang=EN-US><br></span><span style="COLOR: blue"><font face=宋体>结构体的大小等于最后一个成员的偏移量加上其大小再加上末尾的填充字节数目，即：</font></span><span lang=EN-US><br><font face=宋体><span style="COLOR: blue">sizeof( struct ) = offsetof( last item ) + sizeof( last item ) + sizeof( trailing padding )</span><o:p></o:p></font></span></p>
<p><font face=宋体>到这里，朋友们应该对结构体的<span lang=EN-US>sizeof</span>有了一个全新的认识，但不要高兴得太早，有一个影响<span lang=EN-US>sizeof</span>的重要参量还未被提及，那便是编译器的<span lang=EN-US> pack</span>指令。它是用来调整结构体对齐方式的，不同编译器名称和用法略有不同，<span lang=EN-US>VC6</span>中通过<span lang=EN-US>#pragma pack</span>实现，也可以直接修改<span lang=EN-US>/Zp</span>编译开关。<span lang=EN-US>#pragma pack</span>的基本用法为：<span lang=EN-US style="COLOR: blue">#pragma pack( n )</span><span style="COLOR: blue">，<span lang=EN-US>n</span>为字节对齐数，其取值为<span lang=EN-US>1</span>、<span lang=EN-US>2</span>、<span lang=EN-US>4</span>、<span lang=EN-US>8</span>、<span lang=EN-US>16</span></span>，默认是<span lang=EN-US>8</span>，如果这个值比结构体成员的<span lang=EN-US>sizeof</span>值小，那么该成员的偏移量应该以此值为准，即是说，结构体成员的偏移量应该取二者的最小值，公式如下：</font><font face=宋体><span lang=EN-US> <br>offsetof( item ) = min( n, sizeof( item ) ) <br></span>再看示例：<span lang=EN-US><o:p></o:p></span></font></p>
<p><span lang=EN-US><br><span style="COLOR: red"><font face=宋体>#pragma pack(push) // </font></span></span><span style="COLOR: red"><font face=宋体>将当前<span lang=EN-US>pack</span>设置压栈保存</font><font face=宋体><span lang=EN-US> <br>#pragma pack(2)// </span>必须在结构体定义之前使用</font><font face=宋体><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 lang=EN-US>pack</span>设置</font></span><span lang=EN-US><o:p></o:p></span></p>
<p><span lang=EN-US><br></span><font face=宋体>计算<span lang=EN-US>sizeof(S1)</span>时，<span lang=EN-US>min(2, sizeof(i))</span>的值为<span lang=EN-US>2</span>，所以<span lang=EN-US>i</span>的偏移量为<span lang=EN-US>2</span>，加上<span lang=EN-US>sizeof(i)</span>等于<span lang=EN-US>6</span>，能够被<span lang=EN-US>2</span>整除，所以整个<span lang=EN-US>S1</span>的大小为<span lang=EN-US>6</span>。 同样，对于<span lang=EN-US>sizeof(S3)</span>，<span lang=EN-US>s</span>的偏移量为<span lang=EN-US>2</span>，<span lang=EN-US>c2</span>的偏移量为<span lang=EN-US>8</span>，加上<span lang=EN-US>sizeof(c2)</span>等于<span lang=EN-US>9</span>，不能被<span lang=EN-US>2</span>整除，添加一个填充字节，所以<span lang=EN-US> sizeof(S3)</span>等于<span lang=EN-US>10</span>。现在，朋友们可以轻松的出一口气了，还有一点要注意，<span lang=EN-US>&#8220;</span>空结构体<span lang=EN-US>&#8221;</span>（不含数据成员）的大小不为<span lang=EN-US>0</span>，而是<span lang=EN-US>1</span>。试想一个<span lang=EN-US>&#8220;</span>不占 空间<span lang=EN-US>&#8221;</span>的变量如何被取地址、两个不同的<span lang=EN-US>&#8220;</span>空结构体<span lang=EN-US>&#8221;</span>变量又如何得以区分呢？于是，<span lang=EN-US>&#8220;</span>空结构体<span lang=EN-US>&#8221;</span>变量也得被存储，这样编译器也就只能为其分配一个字节的空间 用于占位了。<span lang=EN-US><o:p></o:p></span></font></p>
<p><font face=宋体>如下：</font><span lang=EN-US><font face=宋体> <br></font><span style="COLOR: red"><font face=宋体>struct S5 { }; <br>sizeof( S5 ); // </font></span></span><font face=宋体><span style="COLOR: red">结果为<span lang=EN-US>1</span></span><span lang=EN-US><o:p></o:p></span></font></p>
<p><span lang=EN-US><br><font face=宋体>8. </font></span><font face=宋体>含位域结构体的</font><font face=宋体><span lang=EN-US>sizeof <br></span>前面已经说过，位域成员不能单独被取<span lang=EN-US>sizeof</span>值，我们这里要讨论的是含有位域的结构体的<span lang=EN-US> sizeof</span>，只是考虑到其特殊性而将其专门列了出来。<span lang=EN-US>C99</span>规定<span lang=EN-US>int</span>、<span lang=EN-US>unsigned int</span>和<span lang=EN-US>bool</span>可以作为位域类型，但编译器几乎都对此作了扩展，允许其它类型类型的存在。<span lang=EN-US><o:p></o:p></span></font></p>
<p><span lang=EN-US><br></span><font face=宋体>使用位域的主要目的是压缩存储，其大致规则为：</font><span lang=EN-US><font face=宋体> <br><span style="COLOR: blue">1) </span></font></span><span style="COLOR: blue"><font face=宋体>如果相邻位域字段的类型相同，且其位宽之和小于类型的<span lang=EN-US>sizeof</span>大小，则后面的字段将紧邻前一个字段存储，直到不能容纳为止；</font><font face=宋体><span lang=EN-US> <br>2) </span>如果相邻位域字段的类型相同，但其位宽之和大于类型的<span lang=EN-US>sizeof</span>大小，则后面的字段将从新的存储单元开始，其偏移量为其类型大小的整数倍；</font><font face=宋体><span lang=EN-US> <br>3) </span>如果相邻的位域字段的类型不同，则各编译器的具体实现有差异，<span lang=EN-US>VC6</span>采取不压缩方式，<span lang=EN-US>Dev-C++</span>采取压缩方式；</font><font face=宋体><span lang=EN-US> <br>4) </span>如果位域字段之间穿插着非位域字段，则不进行压缩；</font><font face=宋体><span lang=EN-US> <br>5) </span>整个结构体的总大小为最宽基本类型成员大小的整数倍。</font></span><span lang=EN-US><o:p></o:p></span></p>
<p><font face=宋体>还是让我们来看看例子。</font><font face=宋体><span lang=EN-US> <br></span>示例<span lang=EN-US>1</span>：<span lang=EN-US><o:p></o:p></span></font></p>
<p><span lang=EN-US><br><font face=宋体><span style="COLOR: red">struct BF1 <br>{ <br>char f1 : 3; <br>char f2 : 4; <br>char f3 : 5; <br>}; </span><o:p></o:p></font></span></p>
<p><span lang=EN-US><br></span><font face=宋体>其内存布局为：</font><font face=宋体><span lang=EN-US> <br>|_f1__|__f2__|_|____f3___|____| <br>|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_| <br>0 3&nbsp;&nbsp; 7 8&nbsp;&nbsp; 1316 <br></span>位域类型为<span lang=EN-US>char</span>，第<span lang=EN-US>1</span>个字节仅能容纳下<span lang=EN-US>f1</span>和<span lang=EN-US>f2</span>，所以<span lang=EN-US>f2</span>被压缩到第<span lang=EN-US>1</span>个字节中，而<span lang=EN-US>f3</span>只能从下一个字节开始。因此<span lang=EN-US>sizeof(BF1)</span>的结果为<span lang=EN-US>2</span>。</font><font face=宋体><span lang=EN-US> <br></span>示例<span lang=EN-US>2</span>：<span lang=EN-US><o:p></o:p></span></font></p>
<p><span lang=EN-US><br><font face=宋体><span style="COLOR: red">struct BF2 <br>{ <br>char f1 : 3; <br>short f2 : 4; <br>char f3 : 5; <br>};</span><o:p></o:p></font></span></p>
<p><span lang=EN-US><br></span><font face=宋体>由于相邻位域类型不同，在<span lang=EN-US>VC6</span>中其<span lang=EN-US>sizeof</span>为<span lang=EN-US>6</span>，在<span lang=EN-US>Dev-C++</span>中为<span lang=EN-US>2</span>。</font><font face=宋体><span lang=EN-US> <br></span>示例<span lang=EN-US>3</span>：<span lang=EN-US><o:p></o:p></span></font></p>
<p><span lang=EN-US><br><font face=宋体><span style="COLOR: red">struct BF3 <br>{ <br>char f1 : 3; <br>char f2; <br>char f3 : 5; <br>};</span><o:p></o:p></font></span></p>
<p><span lang=EN-US><br></span><font face=宋体>非位域字段穿插在其中，不会产生压缩，在<span lang=EN-US>VC6</span>和<span lang=EN-US>Dev-C++</span>中得到的大小均为<span lang=EN-US>3</span>。<span lang=EN-US><o:p></o:p></span></font></p>
<p><span lang=EN-US><br><font face=宋体>9. </font></span><font face=宋体>联合体的</font><span lang=EN-US><font face=宋体>sizeof <br></font></span><span style="COLOR: blue"><font face=宋体>结构体在内存组织上是顺序式的，联合体则是重叠式，各成员共享一段内存，所以整个联合体的<span lang=EN-US>sizeof</span>也就是每个成员<span lang=EN-US>sizeof</span>的最大值。结构体的成员也可以是复合类型，这里，复合类型成员是被作为整体考虑的。</font><span lang=EN-US><br></span></span><font face=宋体>所以，下面例子中，<span lang=EN-US>U</span>的<span lang=EN-US>sizeof</span>值等于<span lang=EN-US>sizeof(s)</span>。<span lang=EN-US><o:p></o:p></span></font></p>
<p><span lang=EN-US><br><font face=宋体><span style="COLOR: red">union U <br>{ <br>int i; <br>char c; <br>S1 s; <br>};</span><o:p></o:p></font></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US><o:p>&nbsp;</o:p></span></p>
<img src ="http://www.cppblog.com/hktk/aggbug/96865.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/hktk/" target="_blank">海 阔 天 空</a> 2009-09-21 18:44 <a href="http://www.cppblog.com/hktk/archive/2009/09/21/96865.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>C++结构体字节对齐问题</title><link>http://www.cppblog.com/hktk/archive/2009/09/21/96864.html</link><dc:creator>海 阔 天 空</dc:creator><author>海 阔 天 空</author><pubDate>Mon, 21 Sep 2009 10:34:00 GMT</pubDate><guid>http://www.cppblog.com/hktk/archive/2009/09/21/96864.html</guid><description><![CDATA[<p>#include&lt;stdio.h&gt;<br>struct a<br>{<br>char no[10];<br>int p;<br>long int pp;<br>unsigned int ppp;<br>char x;<br>float y;<br>double h;<br>}xy;<br>void main()<br>{<br>printf("struct a: %d \n",sizeof(struct a)); // 不是等于 35 吗？？？ 为什么等于 40 啦？<br>}</p>
<p>结构体的sizeof<br>这是初学者问得最多的一个问题，所以这里有必要多费点笔墨。让我们先看一个结构体：<br>struct S1<br>{<br>char c;<br>int i;<br>};<br>问sizeof(s1)等于多少聪明的你开始思考了，char占1个字节，int占4个字节，那么加起来就应该是5。是这样吗你在你机器上试过了吗也许你是对的，但很可能你是错的！VC6中按默认设置得到的结果为8。</p>
<p>Why为什么受伤的总是我<br>请不要沮丧，我们来好好琢磨一下sizeof的定义——sizeof的结果等于对象或者类型所占的内存字节数，好吧，那就让我们来看看S1的内存分配情况：<br>S1 s1 = { 'a', 0xFFFFFFFF };<br>定义上面的变量后，加上断点，运行程序，观察s1所在的内存，你发现了什么<br>以我的VC6.0为例，s1的地址为0x0012FF78，其数据内容如下：<br>0012FF78: 61 CC CC CC FF FF FF FF</p>
<p>发现了什么怎么中间夹杂了3个字节的CC看看MSDN上的说明：<br>When applied to a structure type or variable, sizeof returns the actual size, which may include padding bytes inserted for alignment.<br>原来如此，这就是传说中的字节对齐啊！一个重要的话题出现了。<br>为什么需要字节对齐计算机组成原理教导我们这样有助于加快计算机的取数速度，否则就得多花指令周期了。为此，编译器默认会对结构体进行处理（实际上其它地方的数据变量也是如此），让宽度为2的基本数据类型（short等）都位于能被2整除的地址上，让宽度为4的基本数据类型（int等）都位于能被4整除的地址上，以此类推。这样，两个数中间就可能需要加入填充字节，所以整个结构体的sizeof值就增长了。<br>让我们交换一下S1中char与int的位置：<br>struct S2<br>{<br>int i;<br>char c;<br>};<br>看看sizeof(S2)的结果为多少，怎么还是8再看看内存，原来成员c后面仍然有3个填充字节，这又是为什么啊别着急，下面总结规律。</p>
<p>字节对齐的细节和编译器实现相关，但一般而言，满足三个准则：<br><strong>1) 结构体变量的首地址能够被其最宽基本类型成员的大小所整除；<br>2) 结构体每个成员相对于结构体首地址的偏移量（offset）都是成员大小的整数倍，如有需要编译器会在成员之间加上填充字节（internal adding）；<br>3) 结构体的总大小为结构体最宽基本类型成员大小的整数倍，如有需要编译器会在最末一个成员之后加上填充字节（trailing padding）。</strong></p>
<p>对于上面的准则，有几点需要说明：<br>1) 前面不是说结构体成员的地址是其大小的整数倍，怎么又说到偏移量了呢因为有了第1点存在，所以我们就可以只考虑成员的偏移量，这样思考起来简单。想想为什么。</p>
<p>结构体某个成员相对于结构体首地址的偏移量可以通过宏offsetof()来获得，这个宏也在stddef.h中定义，如下：<br>#define offsetof(s,m) (size_t)&amp;(((s *)0)-&gt;m)<br>例如，想要获得S2中c的偏移量，方法为<br>size_t pos = offsetof(S2, c);// pos等于4</p>
<p>2) 基本类型是指前面提到的像char、short、int、float、double这样的内置数据类型，这里所说的&#8220;数据宽度&#8221;就是指其sizeof的大小。由于结构体的成员可以是复合类型，比如另外一个结构体，所以在寻找最宽基本类型成员时，应当包括复合类型成员的子成员，而不是把复合成员看成是一个整体。但在确定复合类型成员的偏移位置时则是将复合类型作为整体看待。<br>这里叙述起来有点拗口，思考起来也有点挠头，还是让我们看看例子吧（具体数值仍以VC6为例，以后不再说明）：<br>struct S3<br>{<br>char c1;<br>S1 s;<br>char c2;<br>};<br>S1的最宽简单成员的类型为int，S3在考虑最宽简单类型成员时是将S1&#8220;打散&#8221;看的，所以S3的最宽简单类型为int，这样，通过S3定义的变量，其存储空间首地址需要被4整除，整个sizeof(S3)的值也应该被4整除。<br>c1 的偏移量为0，s的偏移量呢这时s是一个整体，它作为结构体变量也满足前面三个准则，所以其大小为8，偏移量为4，c1与s之间便需要3个填充字节，而 c2与s之间就不需要了，所以c2的偏移量为12，算上c2的大小为13，13是不能被4整除的，这样末尾还得补上3个填充字节。最后得到sizeof (S3)的值为16。</p>
<p>通过上面的叙述，我们可以得到一个公式：<br>结构体的大小等于最后一个成员的偏移量加上其大小再加上末尾的填充字节数目，即：</p>
<p>sizeof( struct ) = offsetof( last item ) + sizeof( last item ) + sizeof( trailing padding )</p>
<p>到这里，朋友们应该对结构体的sizeof有了一个全新的认识，但不要高兴得太早，有一个影响sizeof的重要参量还未被提及，那便是编译器的pack指令。它是用来调整结构体对齐方式的，不同编译器名称和用法略有不同，VC6中通过#pragma pack实现，也可以直接修改/Zp编译开关。#pragma pack的基本用法为：#pragma pack( n )，n为字节对齐数，其取值为1、2、4、8、16，默认是8，如果这个值比结构体成员的sizeof值小，那么<br>该成员的偏移量应该以此值为准，即是说，结构体成员的偏移量应该取二者的最小值，<br>公式如下：<br>offsetof( item ) = min( n, sizeof( item ) )<br>再看示例：<br>#pragma pack(push) // 将当前pack设置压栈保存<br>#pragma pack(2) // 必须在结构体定义之前使用<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) // 恢复先前的pack设置<br>计算sizeof(S1)时，min(2, sizeof(i))的值为2，所以i的偏移量为2，加上sizeof(i)等于6，能够被2整除，所以整个S1的大小为6。<br>同样，对于sizeof(S3)，s的偏移量为2，c2的偏移量为8，加上sizeof(c2)等于9，不能被2整除，添加一个填充字节，所以sizeof(S3)等于10。</p>
<p>现在，朋友们可以轻松的出一口气了，:)<br>还有一点要注意，&#8220;空结构体&#8221;（不含数据成员）的大小不为0，而是1。试想一个&#8220;不占空间&#8221;的变量如何被取地址、两个不同的&#8220;空结构体&#8221;变量又如何得以区分呢于是，&#8220;空结构体&#8221;变量也得被存储，这样编译器也就只能为其分配一个字节的空间用于占位了。如下：<br>struct S5 { };<br>sizeof( S5 ); // 结果为1</p>
<img src ="http://www.cppblog.com/hktk/aggbug/96864.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/hktk/" target="_blank">海 阔 天 空</a> 2009-09-21 18:34 <a href="http://www.cppblog.com/hktk/archive/2009/09/21/96864.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>C++动态二维数组</title><link>http://www.cppblog.com/hktk/archive/2009/09/16/96375.html</link><dc:creator>海 阔 天 空</dc:creator><author>海 阔 天 空</author><pubDate>Tue, 15 Sep 2009 17:39:00 GMT</pubDate><guid>http://www.cppblog.com/hktk/archive/2009/09/16/96375.html</guid><description><![CDATA[&nbsp;
<p><span>C</span>语言中：&nbsp;<wbr></p>
<p><span>int x,y;<br>&nbsp;<wbr>x=3;<br>&nbsp;<wbr>y=4;</span></p>
<p><span>&nbsp;<wbr>int **pt;&nbsp;<wbr>&nbsp;<br><wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<br><wbr>&nbsp;<wbr>pt=(int**)malloc(sizeof(int*)*x);&nbsp;<wbr>&nbsp;<br><wbr>&nbsp;<br><wbr>&nbsp;<wbr>for(int i=0;i&lt;x;i++)<br>&nbsp;<wbr>{&nbsp;<wbr>&nbsp;<br><wbr>&nbsp;<wbr>&nbsp;<wbr>pt[i]=(int*)malloc(sizeof(int)*y);&nbsp;<wbr>&nbsp;<br><wbr>&nbsp;<wbr>}&nbsp;<wbr>&nbsp;<br><wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<br><wbr>&nbsp;<wbr>pt[2][3]=1;</span>&nbsp;<wbr></p>
<p><span>char arr**;</span></p>
<p><span>char *GoodsName[1000]; //</span>指针数组，每个指针指向一行<span><br>for(i=0;i&lt;1000,i++)<br>GoodsName[i]=(char *)malloc(100 * sizeof(char));</span></p>
<p><span>arr = GoodsName;</span></p>
<p>&nbsp;<wbr></p>
<p><span>==================================================================================</span></p>
<p>&nbsp;<wbr></p>
<p><span>C++</span>中用<span>new</span>动态创建二维数组的格式一般是这样：</p>
<p><span>TYPE (*p)[N] = new TYPE [][N];</span></p>
<p>其中，<span>TYPE</span>是某种类型，<span>N</span>是二维数组的列数。采用这种格式，列数必须指出，而行数无需指定。在这里，<span>p</span>的类型是<span>TYPE*[N]</span>，即是指向一个有<span>N</span>列元素数组的指针。</p>
<p>还有一种方法，可以不指定数组的列数：</p>
<p><span>int **p;<br>p = new int*[10];&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> //</span>注意，<span>int*[10]</span>表示一个有<span>10</span>个元素的指针数组<span><br>for (int i = 0; i&nbsp;&lt; 10; ++i)<br>{<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> p[i] = new int[5];<br>}</span></p>
<p>这里是将<span>p</span>作为一个指向指针的指针，它指向一个包含<span>10</span>个元素的指针数组，并且每个元素指向一个有<span>5</span>个元素的数组，这样就构建了一个<span>10</span>行<span>5</span>列的数组。</p>
<p>当数组使用完毕，释放空间的代码是：</p>
<p><span>for(int i = 0; i&nbsp;&lt; 10; i++)<br>{<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> delete[] p[i];<br>}<br>delete[] p;</span></p>
<p>&nbsp;<wbr></p>
<p>还有一种方法：（数组的第二维必须为常量）</p>
<p><span>&nbsp;<wbr>int&nbsp;<wbr>&nbsp;<wbr>len&nbsp;<wbr>=&nbsp;<wbr>20; &nbsp;<br><wbr>&nbsp;<wbr> &nbsp;<wbr> &nbsp;<wbr> &nbsp;<wbr> &nbsp;<wbr> int</span>（<span>*p)[1024]&nbsp;<wbr>= new&nbsp;<wbr>int[len][1024];&nbsp;<wbr>&nbsp;</span><wbr></p>
<p>&nbsp;<wbr></p>
<p>处理二维数组，可以用降维或是二维法。<span><br></span>降维法是用一位数组来接受二维数组，将二维元素的首地址<span>&amp;a[0][0]</span>作为参数，传递给函数，函数用<span>int *</span>接受。<span><br></span>二维法就直接用二维数组来接受，但是需要指定列数。</p>
<p><span>============================================================================</span></p>
<p>方法一、</p>
<p>例如：<span><br><br>int a[5][10];<br><br></span>函数这样定义：<span><br><br>void func(int (*p)[10])<br>{<br>.............. //</span>处理语句，就可以这样使用：<span>p[i][j]<br>}<br><br></span>这样调用：<span>func(a);</span></p>
<p>方法二、</p>
<p>二维数组相当一个指向指针的指针<span><br></span>所有， 只要这样就可以了<span><br>void fun(int **p)<br>{<br><br>}</span></p>
<p>方法三、</p>
<p>可以对二维数组进行降维处理，看作一维数组，传递数组首地址，并且将数组二维长度传递过去，在函数中要访问<span>a[i][j]</span>，即可以通过<span>*(p+i*col+j),</span>其中的<span>col</span>即二维长度。<br></p>
<img src ="http://www.cppblog.com/hktk/aggbug/96375.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/hktk/" target="_blank">海 阔 天 空</a> 2009-09-16 01:39 <a href="http://www.cppblog.com/hktk/archive/2009/09/16/96375.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>qsort使用详解</title><link>http://www.cppblog.com/hktk/archive/2009/09/16/96373.html</link><dc:creator>海 阔 天 空</dc:creator><author>海 阔 天 空</author><pubDate>Tue, 15 Sep 2009 16:39:00 GMT</pubDate><guid>http://www.cppblog.com/hktk/archive/2009/09/16/96373.html</guid><description><![CDATA[<span id=reply_content_102844294>
<pre>void qsort( void *base, size_t num, size_t width, int (__cdecl *compare )(const void *elem1, const void *elem2 ) );
qsort（quicksort）主要根据你给的比较条件给一个快速排序，主要是通过指针移动实现排序功能。排序之后的结果仍然放在原来数组中。
参数意义如下:
base:需要排序的目标数组开始地址
num:目标数组元素个数
width:目标数组中没一个元素长度
copare:函数指针，指向比较函数
给一个MSDN的例子:
#include &lt;stdlib.h&gt;
#include &lt;string.h&gt;
#include &lt;stdio.h&gt;
int compare( const void *arg1, const void *arg2 );
void main( int argc, char **argv )
{
int i, sz;
char* dest[] = {"every", "good", "boy", "deserves", "favor"};
sz = 5;
qsort( (void *)dest, (size_t)sz, sizeof( char * ), compare );
for( i = 0; i &lt; sz; ++i )
printf( "%s ", dest[i] );
printf( "\n" );
}
int compare( const void *arg1, const void *arg2 )
{
return _stricmp( * ( char** ) arg1, * ( char** ) arg2 );
} </pre>
<pre>&lt;===========================================================================&gt;<span id=reply_content_102845185>
七种qsort排序方法
&lt;本文中排序都是采用的从小到大排序&gt;
一、对int类型数组排序
int num[100];
Sample:
int cmp ( const void *a , const void *b )
{
return *(int *)a - *(int *)b;
}
qsort(num,100,sizeof(num[0]),cmp);
二、对char类型数组排序（同int类型）
char word[100];
Sample:
int cmp( const void *a , const void *b )
{
return *(char *)a - *(int *)b;
}
qsort(word,100,sizeof(word[0]),cmp);
三、对double类型数组排序（特别要注意）
double in[100];
int cmp( const void *a , const void *b )
{
return *(double *)a &gt; *(double *)b ? 1 : -1;
}
qsort(in,100,sizeof(in[0]),cmp)；
四、对结构体一级排序
struct In
{
double data;
int other;
}s[100]
//按照data的值从小到大将结构体排序,关于结构体内的排序关键数据data的类型可以很多种，参考上面的例子写
int cmp( const void *a ,const void *b)
{
return (*(In *)a).data &gt; (*(In *)b).data ? 1 : -1;
}
qsort(s,100,sizeof(s[0]),cmp);
五、对结构体二级排序
struct In
{
int x;
int y;
}s[100];
//按照x从小到大排序，当x相等时按照y从大到小排序
int cmp( const void *a , const void *b )
{
struct In *c = (In *)a;
struct In *d = (In *)b;
if(c-&gt;x != d-&gt;x) return c-&gt;x - d-&gt;x;
else return d-&gt;y - c-&gt;y;
}
qsort(s,100,sizeof(s[0]),cmp);
六、对字符串进行排序
struct In
{
int data;
char str[100];
}s[100];
//按照结构体中字符串str的字典顺序排序
int cmp ( const void *a , const void *b )
{
return strcmp( (*(In *)a)-&gt;str , (*(In *)b)-&gt;str );
}
qsort(s,100,sizeof(s[0]),cmp);
七、计算几何中求凸包的cmp
int cmp(const void *a,const void *b) //重点cmp函数，把除了1点外的所有点，旋转角度排序
{
struct point *c=(point *)a;
struct point *d=(point *)b;
if( calc(*c,*d,p[1]) &lt; 0) return 1;
else if( !calc(*c,*d,p[1]) &amp;&amp; dis(c-&gt;x,c-&gt;y,p[1].x,p[1].y) &lt; dis(d-&gt;x,d-&gt;y,p[1].x,p[1].y)) //如果在一条直线上，则把远的放在前面
return 1;
else return -1;
}
PS:
其中的qsort函数包含在&lt;stdlib.h&gt;的头文件里，strcmp包含在&lt;string.h&gt;的头文件里 <br></span></pre>
</span>
<img src ="http://www.cppblog.com/hktk/aggbug/96373.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/hktk/" target="_blank">海 阔 天 空</a> 2009-09-16 00:39 <a href="http://www.cppblog.com/hktk/archive/2009/09/16/96373.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>C++ 文件操作(ftream ofstream ifstream)</title><link>http://www.cppblog.com/hktk/archive/2009/09/15/96259.html</link><dc:creator>海 阔 天 空</dc:creator><author>海 阔 天 空</author><pubDate>Tue, 15 Sep 2009 10:50:00 GMT</pubDate><guid>http://www.cppblog.com/hktk/archive/2009/09/15/96259.html</guid><description><![CDATA[<p dragover="true"><span>读</span>&nbsp;<span>《</span><span>C++ Primer Plus</span><span>》第</span><span>17</span><span>章</span><span>4-7</span><span>节</span></p>
<p dragover="true"><span>在线文档：</span><span><a href="http://www.cplusplus.com/reference"><span>http://www.cplusplus.com/reference</span></a></span></p>
<p dragover="true">&nbsp;</p>
<p dragover="true"><span>一</span><span><span>&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>头文件关系：</span></p>
<p dragover="true"><span>C++</span><span>在头文件</span><span>fstream(fstream.h)</span><span>中定义了</span><span>ifstream</span><span>类和</span><span>oftream</span><span>类，以及用于</span><span>I/O</span><span>同步的</span><span>fstream</span><span>。这些类都是从头文件</span><span>iostream</span><span>中的类派生而来，</span><span>iostream</span><span>的基类是</span><span>istream,ostream</span><span>。对于大多数实现来说，包含</span><span>fstream</span><span>则自动包含</span><span>iostream</span><span>文件。</span><span>ofstream</span><span>的基类是</span><span>ostream,iftream</span><span>类的基类是</span><span>istream</span><span>。文件流类从</span><span>ios_base</span><span>类中继承了一个流状态成员以及报告流状态的方法。</span></p>
<p><span>二</span><span><span>&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>创建读写流对象的方法：</span></p>
<p><span><span>1）&nbsp;</span></span><span>构造函数</span></p>
<p><span>1.<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>ifstream fin;<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>fin.open(&#8220;jar.dat&#8221;);<span>&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>或者</span><span><span> </span>ifstram fin(&#8220;jar.dat&#8221;);</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>ofstream</span><span>同理</span><span><span> </span></span><span>使用方法可同</span><span>cin,cout.</span><span>即</span><span>cin&gt;&gt;name;cout&lt;&lt;name;</span></p>
<p><span>2.<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>文件模式的使用方法：</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>ifstraem fin(&#8220;temp.txt&#8221;,mode);</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>ofstream fout;</span></p>
<p><span>fout.open(&#8220;temp.txt&#8221;,mode);</span></p>
<p><span>2</span><span>）</span><span><span>&nbsp;&nbsp; </span>open</span><span>函数</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>在</span><span>fstream</span><span>类中，有一个成员函数</span><span>open()</span><span>，就是用来打开文件的，其原型是：</span></p>
<p><span>　　</span><span>void yWXz(const char* filename,int mode,int access); <br></span><span>参数说明：</span><span> </span><span>　　</span><span><br></span><span>　　</span><span>filename</span><span>：　　要打开的文件名</span><span> <br></span><span>　　</span><span>mode</span><span>：　　　　要打开文件的方式</span><span> <br></span><span>　　</span><span>access</span><span>：　　　打开文件的属性</span><span> <br></span><span>　　打开文件的方式在类</span><span>ios(</span><span>是所有流式</span><span>I/O</span><span>类的基类</span><span>)</span><span>中定义，常用的值如下：</span><span> </span><span>　　　　</span></p>
<table cellSpacing=0 cellPadding=0 border=1>
    <tbody>
        <tr>
            <td vAlign=top width=111>
            <p><span>常量</span></p>
            </td>
            <td vAlign=top width=142>
            <p><span>含义</span></p>
            </td>
            <td vAlign=top width=142>
            <p><span>常量</span></p>
            </td>
            <td vAlign=top width=142>
            <p><span>含义</span></p>
            </td>
        </tr>
        <tr>
            <td vAlign=top width=111>
            <p><span>ios_base::in</span></p>
            </td>
            <td vAlign=top width=142>
            <p><span>打开文件，以便读取</span></p>
            </td>
            <td vAlign=top width=142>
            <p><span>ios_base::app</span></p>
            </td>
            <td vAlign=top width=142>
            <p><span>追加到文件尾</span></p>
            </td>
        </tr>
        <tr>
            <td vAlign=top width=111>
            <p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>out</span></p>
            </td>
            <td vAlign=top width=142>
            <p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>写入</span></p>
            </td>
            <td vAlign=top width=142>
            <p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>trun</span></p>
            </td>
            <td vAlign=top width=142>
            <p><span>文件存在，则截短文件</span></p>
            </td>
        </tr>
        <tr>
            <td vAlign=top width=111>
            <p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>ate</span></p>
            </td>
            <td vAlign=top width=142>
            <p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>，并移到文件尾</span></p>
            </td>
            <td vAlign=top width=142>
            <p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>binary</span></p>
            </td>
            <td vAlign=top width=142>
            <p><span>二进制文件</span></p>
            </td>
        </tr>
    </tbody>
</table>
<p><span>ios::nocreate</span><span>：</span><span> </span><span>不建立文件，所以文件不存在时打开失败　</span><span> </span></p>
<p><span>ios::noreplace</span><span>：不覆盖文件，所以打开文件时如果文件存在失败</span><span> </span></p>
<p><span>ios::trunc</span><span>：　　如果文件存在，把文件长度设为</span><span>0</span></p>
<p>&nbsp;</p>
<p><span>打开文件的属性取值是：</span><span> </span><span>　　</span></p>
<p><span>　<span>　</span></span><span>0</span><span>：普通文件，打开访问</span><span> </span></p>
<p><span>　　</span><span>1</span><span>：只读文件</span><span> </span></p>
<p><span>　　</span><span>2</span><span>：隐含文件</span><span> </span></p>
<p><span>　　</span><span>4</span><span>：系统文件</span><span> </span></p>
<p><span>　　可以用&#8220;或&#8221;或者&#8220;</span><span>+</span><span>&#8221;把以上属性连接起来</span><span> </span><span>，如</span><span>3</span><span>或</span><span>1|2</span><span>就是以只读和隐含属性打开文件。</span><span> </span><span>　　</span></p>
<p><span>　　例如：以二进制输入方式打开文件</span><span>c:\config.sys </span><span>　　</span></p>
<p><span>　　　　</span><span>fstream file1; </span></p>
<p><span>　　　　</span><span>file1.open("c:\\config.sys",ios::binary|ios::in,0); </span><span>　　</span></p>
<p><span>　　　　如果</span><span>open</span><span>函数只有文件名一个参数，则是以读</span><span>/</span><span>写普通文件打开，即：</span><span> </span><span>　　</span></p>
<p><span>　　　　</span><span>file1.open("c:\\config.sys");&lt;=&gt;file1.open("c:\\config.sys",ios::in|ios::out,0); </span><span>　　</span></p>
<p><span>　　　　另外，</span><span>fstream</span><span>还有和</span><span>open()</span><span>一样的构造函数，对于上例，在定义的时侯就可以打开文件了：</span><span> </span><span>　　</span></p>
<p><span>　　　<span>　</span></span><span>fstream file1("c:\\config.sys"); </span><span>　　</span></p>
<p><span>　　　　特别提出的是，</span><span>fstream</span><span>有两个子类：</span><span>ifstream(input file stream)</span><span>和</span><span>ofstream(outpu file stream)</span><span>，</span><span>ifstream</span><span>默认以输入方式打开文件，而</span><span>ofstream</span><span>默认以输出方式打开文件。</span><span> </span><span>　　</span></p>
<p><span>　　<span>　　</span></span><span>ifstream file2("c:\\pdos.def");//</span><span>以输入方式打开文件</span><span> </span></p>
<p><span>　　　　</span><span>ofstream file3("c:\\x.123");//</span><span>以输出方式打开文件</span></p>
<p><span>三</span><span><span>&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>检查文件是否打开的方法：</span></p>
<p><span>a)<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>if(fin.fail());</span></p>
<p><span>b)<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>if(!fin.good());</span></p>
<p><span>c)<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>if(!fin)<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>//</span><span>根据</span><span>fin.good()</span><span>来设置自己的</span><span>bool</span><span>值</span></p>
<p><span>d)<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>is_open()<span> </span>//</span><span>能够检测到一不合适的文本模式打开文件时失败的错误和上述</span><span>good()</span><span>测到的各种错误</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>fin.good();//</span><span>正常读取文件时返回</span><span>true</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>fin.fail();/</span><span>在文件到达结尾或者出现其他输入错误如内存不足时返回</span><span>true</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>fin.eof();//</span><span>当文件到达结尾的返回</span><span>true</span><span>。</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>fin.bad();//</span><span>出现无法识别的故障（如硬盘故障）时返回</span><span>true</span><span>。</span></p>
<p><span>四</span><span><span>&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>使用命令行参数的方法：</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>&nbsp;int main(int argc,char* argv[]){&#8230;}<span>&nbsp;&nbsp;&nbsp; </span></span><span>第一个参数为命令和参数个数的和，第二个参数为参数数组</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>&nbsp;fin.clear()<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>将文件与</span><span>ifstream</span><span>对象关联起来时，自动重置状态。不一定要求，但是无害。</span></p>
<p><span>五</span><span><span>&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>文件模式常量：</span></p>
<p><span>六</span><span><span>&nbsp;&nbsp;&nbsp;&nbsp; </span>C++</span><span>和</span><span>C</span><span>的文件打开模式</span></p>
<table cellSpacing=0 cellPadding=0 border=1>
    <tbody>
        <tr>
            <td vAlign=top width=144>
            <p><span>C++</span><span>模式</span></p>
            </td>
            <td vAlign=top width=84>
            <p><span>C</span><span>模式</span></p>
            </td>
            <td vAlign=top width=309>
            <p><span>含义</span></p>
            </td>
        </tr>
        <tr>
            <td vAlign=top width=144>
            <p><span>ios_base::in</span></p>
            </td>
            <td vAlign=top width=84>
            <p><span>&#8220;r&#8221;</span></p>
            </td>
            <td vAlign=top width=309>
            <p><span>打开一读取</span></p>
            </td>
        </tr>
        <tr>
            <td vAlign=top width=144>
            <p><span>ios_base::out</span></p>
            </td>
            <td vAlign=top width=84>
            <p><span>&#8220;w&#8221;</span></p>
            </td>
            <td vAlign=top width=309>
            <p><span>等价于</span><span>ios_base::out|ios_base::trunk(</span><span>有时可以省略</span><span>)</span></p>
            </td>
        </tr>
        <tr>
            <td vAlign=top width=144>
            <p><span>out|trunc</span></p>
            </td>
            <td vAlign=top width=84>
            <p><span>&#8220;w&#8221;</span></p>
            </td>
            <td vAlign=top width=309>
            <p><span>打开一些如文件，如果存在，则截取文件</span></p>
            </td>
        </tr>
        <tr>
            <td vAlign=top width=144>
            <p><span>out|app</span></p>
            </td>
            <td vAlign=top width=84>
            <p><span>&#8220;a&#8221;</span></p>
            </td>
            <td vAlign=top width=309>
            <p><span>打开以写入，只追加</span></p>
            </td>
        </tr>
        <tr>
            <td vAlign=top width=144>
            <p><span>in|out</span></p>
            </td>
            <td vAlign=top width=84>
            <p><span>&#8220;r+&#8221;</span></p>
            </td>
            <td vAlign=top width=309>
            <p><span>打开以写入，在文件读取的位置写入</span></p>
            </td>
        </tr>
        <tr>
            <td vAlign=top width=144>
            <p><span>in|out|trunc</span></p>
            </td>
            <td vAlign=top width=84>
            <p><span>&#8220;w+&#8221;</span></p>
            </td>
            <td vAlign=top width=309>
            <p><span>打开以读写，如果存在，则首先截取文件</span></p>
            </td>
        </tr>
        <tr>
            <td vAlign=top width=144>
            <p><span>out|binary</span></p>
            </td>
            <td vAlign=top width=84>
            <p><span>cmodeb</span></p>
            </td>
            <td vAlign=top width=309>
            <p><span>以</span><span>C++mode</span><span>和二进制模式打开，例如</span><span>ios_base::in|ios_base::binary</span><span>成为</span><span>&#8220;rb&#8221;</span></p>
            </td>
        </tr>
        <tr>
            <td vAlign=top width=144>
            <p><span>out|ate</span></p>
            </td>
            <td vAlign=top width=84>
            <p><span>&#8220;cmode&#8221;</span></p>
            </td>
            <td vAlign=top width=309>
            <p><span>以指定的模式打开，并已到文件尾。</span><span>C</span><span>则需要使用独立的函数调用。例如，</span><span>ios_base::in|ios_base::ate</span><span>则被转换为</span><span>&#8220;r&#8221;</span><span>和</span><span>C</span><span>函数调用</span><span>fseek(file,0,SEEK_END);</span></p>
            </td>
        </tr>
    </tbody>
</table>
<p><span>对于</span><span>app</span><span>模式，只能追加，但是不能修改原来已经存在的数据，要是其能修改，则需要与</span><span>out</span><span>模式组合，即</span><span>app|out</span><span>。对于已经处理到末尾的文件，</span><span>eof</span><span>位将被被设置为</span><span>1</span><span>，因此，如果想对文件再作修改，则需要调用函数</span><span>fout.clear()</span><span>终止流状态，否则禁止对文件作进一步读写操作。</span></p>
<p><span>七</span><span><span>&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>文件读写：</span></p>
<p><span>1</span><span>）</span><span><span>&nbsp;&nbsp; </span></span><span>操纵符</span><span> </span><span>功能</span><span> </span><span>输入</span><span>/</span><span>输出</span><span> </span></p>
<table cellSpacing=0 cellPadding=0 border=1>
    <tbody>
        <tr>
            <td vAlign=top width=420 colSpan=2>
            <p><span>dec </span><span>格式化为十进制数值数据</span><span> </span><span>输入和输出</span><span> </span></p>
            </td>
        </tr>
        <tr>
            <td vAlign=top width=420 colSpan=2>
            <p><span>endl </span><span>输出一个换行符并刷新此流</span><span> </span><span>输出</span><span> </span></p>
            </td>
        </tr>
        <tr>
            <td vAlign=top width=420 colSpan=2>
            <p><span>ends </span><span>输出一个空字符</span><span> </span><span>输出</span><span> </span></p>
            </td>
        </tr>
        <tr>
            <td vAlign=top width=420 colSpan=2>
            <p><span>hex </span><span>格式化为十六进制数值数据</span><span> </span><span>输入和输</span></p>
            </td>
        </tr>
        <tr>
            <td vAlign=top width=420 colSpan=2>
            <p><span>出</span><span> oct </span><span>格式化为八进制数值数据</span><span> </span><span>输入和输出</span><span> </span></p>
            </td>
        </tr>
        <tr>
            <td vAlign=top width=408>
            <p><span>setpxecision(int p) </span><span>设置浮点数的精度位数</span><span> </span><span>输出</span></p>
            </td>
            <td width=12>
            <p>&nbsp;</p>
            </td>
        </tr>
    </tbody>
</table>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>2</span><span>）</span><span><span>&nbsp;&nbsp; </span></span><span>基本方法</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>①</span><span>put() </span></p>
<p><span>　　　<span>　</span></span><span>put()</span><span>函数向流写入一个字符，其原型是</span><span>ofstream &amp;put(char ch)</span><span>，使用也比较简单，如</span><span>file1.put('c');</span><span>就是向流写一个字符</span><span>'c'</span><span>。</span><span> </span><span>　　</span></p>
<p><span>　　</span><span><span> </span></span><span>②</span><span>get() </span></p>
<p><span>　　　<span>　</span></span><span>get()</span><span>函数比较灵活，有</span><span>3</span><span>种常用的重载形式：</span><span> </span><span>　　</span></p>
<p><span>　　　　一种就是和</span><span>put()</span><span>对应的形式：</span><span>ifstream &amp;get(char &amp;ch);</span><span>功能是从流中读取一个字符，结果保存在引用</span><span>ch</span><span>中，如果到文件尾，返回空字符。如</span><span>file2.get(x);</span><span>表示从文件中读取一个字符，并把读取的字符保存在</span><span>x</span><span>中。</span><span> </span><span>　　</span></p>
<p><span>　　　　<span>另一种重载形式的原型是：</span></span><span> int get();</span><span>这种形式是从流中返回一个字符，如果到达文件尾，返回</span><span>EOF</span><span>，如</span><span>x=file2.get();</span><span>和上例功能是一样的。</span><span> </span><span>　　</span></p>
<p><span>　　　<span>　还有一种形式的原型是：</span></span><span>ifstream &amp;get(char *buf,int num,char delim='\n')</span><span>；这种形式把字符读入由</span><span> buf </span><span>指向的数组，直到读入了</span><span> num </span><span>个字符或遇到了由</span><span> delim </span><span>指定的字符，如果没使用</span><span> delim </span><span>这个参数，将使用缺省值换行符</span><span>'\n'</span><span>。例如：</span><span> </span><span>　　</span></p>
<p><span>　　　<span>　</span></span><span>file2.get(str1,127,'A');//</span><span>从文件中读取字符到字符串</span><span>str1</span><span>，当遇到字符</span><span>'A'</span><span>或读取了</span><span>127</span><span>个字符时终止。</span><span> </span><span>　　</span></p>
<p><span>　　③读写数据块（</span><span>read, write</span><span>）</span></p>
<p><span>　　　　要读写二进制数据块，使用成员函数</span><span>read()</span><span>和</span><span>write()</span><span>成员函数，它们原型如下：</span><span> </span></p>
<p><span>　　　　</span><span>read(unsigned char *buf,int num); </span></p>
<p><span>　　　　</span><span>write(const unsigned char *buf,int num); </span></p>
<p><span>　　　　　　</span><span>read()</span><span>从文件中读取</span><span> num </span><span>个字符到</span><span> buf </span><span>指向的缓存中，如果在还未读入</span><span> num </span><span>个字符时就到了文件尾，可以用成员函数</span><span> int gcount();</span><span>来取得实际读取的字符数；而</span><span> write() </span><span>从</span><span>buf </span><span>指向的缓存写</span><span> num </span><span>个字符到文件中，值得注意的是缓存的类型是</span><span> unsigned char *</span><span>，有时可能需要类型转换。</span><span> </span><span>　　</span></p>
<p><span>　　例：</span><span> </span></p>
<p><span>　　　　　　</span><span>out.write(str1,strlen(str1));//</span><span>把字符串</span><span>str1</span><span>全部写到</span><span>yyy.yyy</span><span>中</span><span> </span></p>
<p><span>　　　　　　</span><span>in.read((unsigned char*)n,sizeof(n));//</span><span>从</span><span>xxx.xxx</span><span>中读取指定个整数，注意类型转换</span><span> </span></p>
<p><span>八</span><span><span>&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>随即读取文件的函数</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>和</span><span>C</span><span>的文件操作方式不同的是，</span><span>C++ I/O</span><span>系统管理两个与一个文件相联系的指针。一个是读指针，它说明输入操作在文件中的位置；另一个是写指针，它下次写操作的位置。每次执行输入或输出时，相应的指针自动变化。所以</span><span>，</span><strong><span>C++</span></strong><strong><span>的文件定位分为读位置和写位置的定位，对应的成员函数是</span></strong><strong><span> seekg()</span></strong><strong><span>和</span></strong><strong><span> seekp()</span></strong><strong><span>，</span></strong><strong><span>seekg()</span></strong><strong><span>是设置读位置，</span></strong><strong><span>seekp</span></strong><strong><span>是设置写位置</span></strong><span>。它们最通用的形式如下：</span><span> </span><span>　　</span></p>
<p><span>　</span><span><span>&nbsp;&nbsp;&nbsp;&nbsp; </span><span>istream &amp;seekg(streamoff offset,seek_dir origin); </span></span></p>
<p><span>　　</span><span> <span>ostream &amp;seekp(streamoff offset,seek_dir origin); </span></span></p>
<p><span>　　　　</span><span>streamoff</span><span>定义于</span><span> iostream.h </span><span>中，定义有偏移量</span><span> offset </span><span>所能取得的最大值，</span><span>seek_dir </span><span>表示移动的基准位置，是一个有以下值的枚举：</span><span> </span><span>　　</span></p>
<p><span>　　</span><span>ios::beg</span><span>：　　文件开头</span><span> </span></p>
<p><span>　　</span><span>ios::cur</span><span>：　　文件当前位置</span><span> </span></p>
<p><span>　　</span><span>ios::end</span><span>：　　文件结尾</span><span> </span></p>
<p><span>　　　　这两个函数一般用于二进制文件，因为文本文件会因为系统对字符的解释而可能与预想的值不同。</span><span> </span></p>
<p><span>　　例：</span><span> </span></p>
<p><span>　　　　　　</span><span> file1.seekg(1234,ios::cur);//</span><span>把文件的读指针从当前位置向后移</span><span>1234</span><span>个字节</span><span> </span></p>
<p><span>　　　　　　</span><span> file2.seekp(1234,ios::beg);//</span><span>把文件的写指针从文件开头向后移</span><span>1234</span><span>个字</span></p>
<p><span>代码展示</span></p>
<p><span>1</span><span>）</span><span><span>&nbsp;&nbsp; </span></span><span>覆盖操作：（</span><span>tellp</span><span>，</span><span>seekp</span><span>）</span></p>
<pre><span>outfile.write (<span><span>"This is an apple"</span></span>,16);</span></pre>
<pre><span>pos=outfile.tellp();</span></pre>
<pre><span>outfile.seekp (pos-7);</span></pre>
<pre><span>outfile.write (<span><span>" sam"</span></span>,4);</span></pre>
<p><span>输出：</span><tt><span>This is a sample</span></tt></p>
<p><tt><span>2</span></tt><tt><span><span>）</span></span></tt><tt><span><span>&nbsp;&nbsp; </span></span></tt><tt><span><span>读取整个文件到缓冲（</span></span></tt><tt><span>seekg</span></tt><tt><span><span>，</span></span></tt><tt><span>tellg</span></tt><span><tt><span>）</span></tt><tt></tt></span></p>
<p align=left><span>&nbsp;int length;</span></p>
<p align=left><span>&nbsp;char * buffer;</span></p>
<p align=left><span>&nbsp;ifstream is;</span></p>
<p align=left><span>&nbsp;is.open ("test.txt", ios::binary );<br><br></span></p>
<p align=left><span>&nbsp;&nbsp;// get length of file:</span></p>
<p align=left><strong><span>&nbsp;is.seekg (0, ios::end);</span></strong></p>
<p align=left><span>&nbsp;<strong>&nbsp;length = is.tellg();</strong></span></p>
<p align=left><span>&nbsp;is.seekg (0, ios::beg);<br><br></span></p>
<p align=left><span>&nbsp;// allocate memory:</span></p>
<p align=left><span>&nbsp;buffer = new char [length];</span></p>
<p align=left><span>&nbsp;&nbsp;is.read (buffer,length);</span></p>
<p align=left><span>&nbsp;is.close();</span></p>
<p align=left>&nbsp;</p>
<p align=left><span>&nbsp;cout.write (buffer,length);</span></p>
<p align=left>&nbsp;</p>
<p align=left><span>&nbsp;delete[] buffer;</span></p>
<p><span>九</span><span><span>&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>文件操作结构体</span></p>
<p><span><span>&nbsp;</span></span><span>struct &nbsp; DATA {</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>int &nbsp; n; &nbsp;</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>char &nbsp; a; &nbsp; </span></p>
<p><span>&nbsp; }; </span></p>
<p><span>&nbsp; ostream&amp; &nbsp; operator &nbsp; &lt;&lt;(ostream&amp; &nbsp; os, &nbsp; DATA&amp; &nbsp; data) { &nbsp; <br><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>&nbsp; os&lt;&lt;data.a; &nbsp; <br><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>&nbsp; os&lt;&lt;data.n; &nbsp; <br><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>&nbsp; return &nbsp; os; &nbsp; <br>&nbsp; } &nbsp; <br>&nbsp; istream&amp; &nbsp; operator &nbsp; &gt;&gt;(istream&amp; &nbsp; os, &nbsp; DATA&amp; &nbsp; data) { &nbsp; <br><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>&nbsp; os&gt;&gt;data.a; &nbsp;</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>&nbsp; os&gt;&gt;data.n; &nbsp; </span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>&nbsp; return &nbsp; os; &nbsp; </span></p>
<p><span>&nbsp; }</span></p>
<p><span>十</span><span><span>&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>使用临时文件</span><span>(cstdio(stdio.h))</span></p>
<p><span>tmpname(pasName);</span><span>生成生成与当前目录不重名的字符串存于</span><span>pasName</span><span>中，</span><span>pasName</span><span>的长度最多为</span><span>L_tmpnam,</span><span>最多生成</span><span>TMP_NAM</span><span>个不同的文件名。</span></p>
<img src ="http://www.cppblog.com/hktk/aggbug/96259.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/hktk/" target="_blank">海 阔 天 空</a> 2009-09-15 18:50 <a href="http://www.cppblog.com/hktk/archive/2009/09/15/96259.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>memset使用详解</title><link>http://www.cppblog.com/hktk/archive/2009/09/14/96139.html</link><dc:creator>海 阔 天 空</dc:creator><author>海 阔 天 空</author><pubDate>Mon, 14 Sep 2009 09:14:00 GMT</pubDate><guid>http://www.cppblog.com/hktk/archive/2009/09/14/96139.html</guid><description><![CDATA[<p>&nbsp; </p>
<p align=left><span>　　功 能<span>: </span>将<span>s</span>所指向的某一块内存中的每个字节的内容全部设置为<span>ch</span>指定的<span>ASCII</span>值<span>,</span></span></p>
<p align=left><span>　　块的大小由第三个参数指定<span>,</span>这个函数通常为新申请的内存做初始化工作</span></p>
<p align=left><span>　　用 法<span>: void *memset(void *s, char ch, unsigned n);</span></span></p>
<p align=left><span>　　程序例<span>:</span></span></p>
<p align=left><span>　　<span>#include &lt;string.h&gt;</span></span></p>
<p align=left><span>　　<span>#include &lt;stdio.h&gt;</span></span></p>
<p align=left><span>　　<span>#include &lt;mem.h&gt;</span></span></p>
<p align=left><span>　　<span>int main(void)</span></span></p>
<p align=left><span>　　<span>{</span></span></p>
<p align=left><span>　　<span>char buffer[] = "Hello world\n";</span></span></p>
<p align=left><span>　　<span>printf("Buffer before memset: %s\n", buffer);</span></span></p>
<p align=left><span>　　<span>memset(buffer, '*', strlen(buffer) );</span></span></p>
<p align=left><span>　　<span>printf("Buffer after memset: %s\n", buffer);</span></span></p>
<p align=left><span>　　<span>return 0;</span></span></p>
<p align=left><span>　　<span>} </span></span></p>
<p align=left><span>　　输出结果：</span></p>
<p align=left><span>　　<span>Buffer before memset: Hello world</span></span></p>
<p align=left><span>　　<span>Buffer after memset: ***********</span></span></p>
<p align=left><span>　　编译平台：</span></p>
<p align=left><span>　　<span>Microsoft Visual C++ 6.0</span></span></p>
<p align=left><span>　　也不一定就是把内容全部设置为<span>ch</span>指定的<span>ASCII</span>值，而且该处的<span>ch</span>可为<span>int</span>或者其他类型，并不一定要是<span>char</span>类型。例如下面这样：</span></p>
<p align=left><span>　　<span>int array[5] = {1,4,3,5,2};</span></span></p>
<p align=left><span>　　<span>for(int i = 0; i &lt; 5; i++)</span></span></p>
<p align=left><span>　　<span>cout&lt;&lt;array<em>&lt;&lt;" ";</em></span></span></p>
<p align=left><em><span>　　<span>cout&lt;&lt;endl;</span></span></em></p>
<p align=left><em><span>　　<span>memset(array,0,5*sizeof(int));</span></span></em></p>
<p align=left><em><span>　　<span>for(int k = 0; k &lt; 5; k++)</span></span></em></p>
<p align=left><em><span>　　<span>cout&lt;&lt;array<strong>[k]</strong>&lt;&lt;" ";</span></span></em></p>
<p align=left><em><span>　　<span>cout&lt;&lt;endl;</span></span></em></p>
<p align=left><em><span>　　输出的结果就是：<span>1 4 3 5 2</span></span></em></p>
<p align=left><em><span>　　<span>0 0 0 0 0</span></span></em></p>
<p align=left><em><span>　　后面的表大小的参数是以字节为单位，所以，对于<span>int</span>或其他的就并不是都乘默认的<span>1</span>（字符型）了。而且不同的机器上<span>int</span>的大小也可能不同，所以最好用<span>sizeof</span>（）。 </span></em></p>
<p align=left><em><span>　　要注意的是，<span>memset</span>是对字节进行操作，所以上述程序如果改为</span></em></p>
<p align=left><em><span>　　<span>int array[5] = {1,4,3,5,2};</span></span></em></p>
<p align=left><em><span>　　<span>for(int i = 0; i &lt; 5; i++)</span></span></em></p>
<p align=left><em><span>　　<span>cout&lt;&lt;array&lt;&lt;" ";</span></span></em></p>
<p align=left><em><span>　　<span>cout&lt;&lt;endl;</span></span></em></p>
<p align=left><em><span>　　<span>memset(array,1,5*sizeof(int));// </span>注意 这里与上面的程序不同</span></em></p>
<p align=left><em><span>　　<span>for(int k = 0; k &lt; 5; k++) </span></span></em></p>
<p align=left><em><span>　　<span>cout&lt;&lt;array<strong>[k]</strong>&lt;&lt;" ";</span></span></em></p>
<p align=left><em><span>　　<span>cout&lt;&lt;endl;</span></span></em></p>
<p align=left><em><span>　　输出的结果就是：<span>1 4 3 5 2</span></span></em></p>
<p align=left><em><span>　　<span>16843009 16843009 16843009 16843009 16843009</span></span></em></p>
<p align=left><em><span>　　为什么呢？</span></em></p>
<p align=left><em><span>　　因为<span>memset</span>是以字节为单位就是对<span>array</span>指向的内存的<span>5</span>个字节进行赋 值，每个都用<span>ASCII</span>为<span>1</span>的字符去填充，转为二进制后，<span>1</span>就是<span>00000001,</span>占一个字节。一个<span>INT</span>元素是<span>4</span>字节，合一起就是<span> 1000000010000000100000001</span>，就等于<span>16843009</span>，就完成了对一个<span>INT</span>元素的赋值了。</span></em></p>
<p align=left><em><span>　　所以用<span>memset</span>对非字符型数组赋初值是不可取的！</span></em></p>
<p align=left><em><span>　　楼上说的很对，只是程序执行结果是<span>0 0 0 0</span>；程序不同的地方不在那里。程序如下：</span></em></p>
<p align=left><span>　　<span>int array[5] = {1,4,3,5,2};</span></span></p>
<p align=left><span>　　<span>for(int i = 0; i &lt; 5; i++)</span></span></p>
<p align=left><span>　　<span>cout&lt;&lt;array&lt;&lt;" ";</span></span></p>
<p align=left><span>　　<span>cout&lt;&lt;endl;</span></span></p>
<p align=left><span>　　<span>memset(array,1,5*sizeof(int)); //</span>这里才是不同的地方</span></p>
<p align=left><span>　　<span>for(int k = 0; k &lt; 5; k++) //</span>不同不在这里，<span>k=1</span>只是少循环了一次而已</span></p>
<p align=left><span>　　<span>cout&lt;&lt;array[k]&lt;&lt;" ";</span></span></p>
<p align=left><span>　　<span>cout&lt;&lt;endl;</span></span></p>
<p align=left><span>　　例如有一个结构体<span>Some x</span>，可以这样清零：</span></p>
<p align=left><span>　　<span>memset( &amp;x, 0, sizeof(Some) );</span></span></p>
<p align=left><span>　　如果是一个结构体的数组<span>Some x[10]</span>，可以这样：</span></p>
<p align=left><span>　　<span>menset( x, 0, sizeof(Some)*10 );</span></span></p>
<p align=left><strong><span><br>memset</span></strong><strong><span>函数详细说明</span></strong></p>
<p align=left><span>　　<span>1</span>。<span>void *memset(void *s,int c,size_t n)</span></span></p>
<p align=left><span>　　总的作用：将已开辟内存空间<span> s </span>的首<span> n </span>个字节的值设为值<span> c</span>。</span></p>
<p align=left><span>　　<span>2</span>。例子</span></p>
<p align=left><span>　　<span>main(){</span></span></p>
<p align=left><span>　　<span>char *s="Golden Global View";</span></span></p>
<p align=left><span>　　<span>clrscr();</span></span></p>
<p align=left><span>　　<span>memset(s,'G',6);//</span>貌似这里有点问题<span>//</span></span></p>
<p align=left><span>　　<span>printf("%s",s);</span></span></p>
<p align=left><span>　　<span>getchar();</span></span></p>
<p align=left><span>　　<span>return 0;</span></span></p>
<p align=left><span>　　<span>}</span>　</span></p>
<p align=left><span>　　【这个问题相当大，程序根本就运行不下去了，你这里的<span>S</span>志向的是一段只读的内存，而你<span>memset</span>又试图修改它，所以运行时要出错，修改办法<span>char *s</span>修改为<span>char s[]</span>】</span></p>
<p align=left><span>　　<span>3</span>。<span>memset() </span>函数常用于内存空间初始化。如：</span></p>
<p align=left><span>　　<span>char str[100];</span></span></p>
<p align=left><span>　　<span>memset(str,0,100);</span></span></p>
<p align=left><span>　　<span>4</span>。<span>memset()</span>的深刻内涵：用来对一段内存空间全部设置为某个字符，一般用在对定义的字符串进行初始化为<span>&#8216;memset(a, '\0', sizeof(a));</span></span></p>
<p align=left><span>　　<span>memcpy</span>用来做内存拷贝，你可以拿它拷贝任何数据类型的对象，可以指定拷贝的数据长度；例：<span>char a[100],b[50]; memcpy(b, a, sizeof(b));</span>注意如用<span>sizeof(a)</span>，会造成<span>b</span>的内存地址溢出。</span></p>
<p align=left><span>　　<span>strcpy</span>就只能拷贝字符串了，它遇到<span>'\0'</span>就结束拷贝；例：<span>char a[100],b[50];strcpy(a,b);</span>如用<span>strcpy(b,a)</span>，要注意<span>a</span>中的字符串长度（第一个<span>&#8216;\0&#8217;</span>之前）是否超过<span>50</span>位，如超过，则会造成<span>b</span>的内存地址溢出。</span></p>
<p align=left><span>　　<span>5.</span>补充：某人的一点心得</span></p>
<p align=left><span>　　<span>memset</span>可以方便的清空一个结构类型的变量或数组。</span></p>
<p align=left><span>　　如：</span></p>
<p align=left><span>　　<span>struct sample_struct</span></span></p>
<p align=left><span>　　<span>{</span></span></p>
<p align=left><span>　　<span>char csName[16];</span></span></p>
<p align=left><span>　　<span>int iSeq;</span></span></p>
<p align=left><span>　　<span>int iType;</span></span></p>
<p align=left><span>　　<span>};</span></span></p>
<p align=left><span>　　对于变量</span></p>
<p align=left><span>　　<span>struct sample_strcut stTest;</span></span></p>
<p align=left><span>　　一般情况下，清空<span>stTest</span>的方法：</span></p>
<p align=left><span>　　<span>stTest.csName[0]='\0';</span></span></p>
<p align=left><span>　　<span>stTest.iSeq=0;</span></span></p>
<p align=left><span>　　<span>stTest.iType=0;</span></span></p>
<p align=left><span>　　用<span>memset</span>就非常方便：</span></p>
<p align=left><span>　　<span>memset(&amp;stTest,0,sizeof(struct sample_struct));</span></span></p>
<p align=left><span>　　如果是数组：</span></p>
<p align=left><span>　　<span>struct sample_struct TEST[10];</span></span></p>
<p align=left><span>　　则</span></p>
<p align=left><span>　　<span>memset(TEST,0,sizeof(struct sample_struct)*10);</span></span></p>
<p align=left><span>　　<span>6</span>。<span>strcpy</span></span></p>
<p align=left><span>　　原型：<span>extern char *strcpy(char *dest,char *src);</span></span></p>
<p align=left><span>　　用法：＃<span>i nclude</span></span></p>
<p align=left><span>　　功能：把<span>src</span>所指由<span>NULL</span>结束的字符串复制到<span>dest</span>所指的数组中。</span></p>
<p align=left><span>　　说明：<span>src</span>和<span>dest</span>所指内存区域不可以重叠且<span>dest</span>必须有足够的空间来容纳<span>src</span>的字符串。</span></p>
<p align=left><span>　　返回指向<span>dest</span>的指针。</span></p>
<p align=left><span>　　<span>memcpy</span></span></p>
<p align=left><span>　　原型：<span>extern void *memcpy(void *dest, void *src, unsigned int count);</span></span></p>
<p align=left><span>　　用法：＃<span>i nclude</span></span></p>
<p align=left><span>　　功能：由<span>src</span>所指内存区域复制<span>count</span>个字节到<span>dest</span>所指内存区域。</span></p>
<p align=left><span>　　说明：<span>src</span>和<span>dest</span>所指内存区域不能重叠，函数返回指向<span>dest</span>的指针。</span></p>
<p align=left><span>　　<span>memset</span></span></p>
<p align=left><span>　　原型：<span>extern void *memset(void *buffer, int c, int count);</span></span></p>
<p align=left><span>　　用法：＃<span>i nclude</span></span></p>
<p align=left><span>　　功能：把<span>buffer</span>所指内存区域的前<span>count</span>个字节设置成字符<span>c</span>。</span></p>
<p><span>　　说明：返回指向<span>buffer</span>的指针。<br></span></p>
<br>原文链接：<a href="http://baike.baidu.com/view/982208.htm" target=_blank>http://baike.baidu.com/view/982208.htm</a><br>&lt;====================================================================================================&gt;<br><br>1。void *memset(void *s,int c,size_t n)<br>总的作用：将已开辟内存空间 s 的首 n 个字节的值设为值 c。<br><br>2。例子<br><br>main(){<br>char *s="Golden Global View";<br><br>clrscr();<br><br>memset(s,'G',6);//貌似这里有点问题//<br>printf("%s",s);<br><br>getchar();<br>return 0;<br>}　<br>3。memset() 函数常用于内存空间初始化。如：<br>char str[100];<br>memset(str,0,100);<a name=entrymore></a><br><br>4。memset()的深刻内涵：用来对一段内存空间全部设置为某个字符，一般用在对定义的字符串进行初始化为&#8216;memset(a, '\0', sizeof(a));<br><br>memcpy用来做内存拷贝，你可以拿它拷贝任何数据类型的对象，可以指定拷贝的数据长度；例：char a[100],b[50]; memcpy(b, a, sizeof(b));注意如用sizeof(a)，会造成b的内存地址溢出。<br><br>strcpy就只能拷贝字符串了，它遇到'\0'就结束拷贝；例：char a[100],b[50];strcpy(a,b);如用strcpy(b,a)，要注意a中的字符串长度（第一个&#8216;\0&#8217;之前）是否超过50位，如超过，则会造成b的内存地址溢出。<br><br>5.补充：某人的一点心得<br>memset可以方便的清空一个结构类型的变量或数组。<br><br>如：<br>struct sample_struct<br>{<br>char csName[16];<br>int iSeq;<br>int iType;<br>};<br><br>对于变量<br>struct sample_strcut stTest;<br><br>一般情况下，清空stTest的方法：<br>stTest.csName[0]='\0';<br>stTest.iSeq=0;<br>stTest.iType=0;<br><br>用memset就非常方便：<br>memset(&amp;stTest,0,sizeof(struct sample_struct));<br><br>如果是数组：<br>struct sample_struct TEST[10];<br>则<br>memset(TEST,0,sizeof(struct sample_struct)*10);<br><br>6。strcpy<br>原型：extern char *strcpy(char *dest,char *src);<br>用法：＃i nclude<br>功能：把src所指由NULL结束的字符串复制到dest所指的数组中。<br>说明：src和dest所指内存区域不可以重叠且dest必须有足够的空间来容纳src的字符串。<br>返回指向dest的指针。<br><br><br>memcpy<br>原型：extern void *memcpy(void *dest, void *src, unsigned int count);<br>用法：＃i nclude<br>功能：由src所指内存区域复制count个字节到dest所指内存区域。<br>说明：src和dest所指内存区域不能重叠，函数返回指向dest的指针。<br><br><br><br>memset<br>原型：extern void *memset(void *buffer, int c, int count);<br>用法：＃i nclude<br>功能：把buffer所指内存区域的前count个字节设置成字符c。<br>说明：返回指向buffer的指针。<br><br>原文链接：<a href="http://www.azure.net.ru/read.php/137.htm" target=_blank>http://www.azure.net.ru/read.php/137.htm</a>
<img src ="http://www.cppblog.com/hktk/aggbug/96139.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/hktk/" target="_blank">海 阔 天 空</a> 2009-09-14 17:14 <a href="http://www.cppblog.com/hktk/archive/2009/09/14/96139.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>C++宽字符处理函数函数与普通函数对照表</title><link>http://www.cppblog.com/hktk/archive/2008/05/20/50587.html</link><dc:creator>海 阔 天 空</dc:creator><author>海 阔 天 空</author><pubDate>Tue, 20 May 2008 15:04:00 GMT</pubDate><guid>http://www.cppblog.com/hktk/archive/2008/05/20/50587.html</guid><description><![CDATA[字符分类：&nbsp;&nbsp;&nbsp;&nbsp; 宽字符函数普通C函数描述<br>iswalnum()&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; isalnum()&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 测试字符是否为数字或字母<br>iswalpha()&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; isalpha()&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 测试字符是否是字母<br>iswcntrl()&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; iscntrl()&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 测试字符是否是控制符<br>iswdigit()&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; isdigit()&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 测试字符是否为数字<br>iswgraph()&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; isgraph()&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 测试字符是否是可见字符<br>iswlower()&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; islower()&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 测试字符是否是小写字符<br>iswprint()&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; isprint()&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 测试字符是否是可打印字符<br>iswpunct()&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ispunct()&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 测试字符是否是标点符号<br>iswspace()&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; isspace()&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 测试字符是否是空白符号<br>iswupper()&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; isupper()&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 测试字符是否是大写字符<br>iswxdigit()&nbsp;&nbsp;&nbsp;&nbsp; isxdigit()&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 测试字符是否是十六进制的数字<br><br>大小写转换：&nbsp;&nbsp; &nbsp;<br>宽字符函数&nbsp;&nbsp;&nbsp;&nbsp; 普通C函数描述<br>towlower()&nbsp;&nbsp;&nbsp;&nbsp; tolower() 把字符转换为小写<br>towupper()&nbsp;&nbsp;&nbsp;&nbsp; toupper() 把字符转换为大写<br><br>字符比较：&nbsp;&nbsp;&nbsp;&nbsp; 宽字符函数普通C函数描述<br>wcscoll()&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; strcoll() 比较字符串<br><br>日期和时间转换：<br>宽字符函数描述<br>strftime()&nbsp;&nbsp;&nbsp;&nbsp; 根据指定的字符串格式和locale设置格式化日期和时间<br>wcsftime()&nbsp;&nbsp;&nbsp;&nbsp; 根据指定的字符串格式和locale设置格式化日期和时间， 并返回宽字符串<br>strptime()&nbsp;&nbsp;&nbsp;&nbsp; 根据指定格式把字符串转换为时间值， 是strftime的反过程<br><br>打印和扫描字符串：<br>宽字符函数描述<br>fprintf()&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /fwprintf()&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 使用vararg参量的格式化输出<br>fscanf()&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /fwscanf()&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 格式化读入<br>printf()&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;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 使用vararg参量的格式化输出到标准输出<br>scanf()&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 从标准输入的格式化读入<br>sprintf()&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /swprintf()&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 根据vararg参量表格式化成字符串<br>sscanf()&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;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 以字符串作格式化读入<br>vfprintf()&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /vfwprintf()&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 使用stdarg参量表格式化输出到文件<br>vprintf()&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;&nbsp;&nbsp;&nbsp;&nbsp; 使用stdarg参量表格式化输出到标准输出<br>vsprintf()&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /vswprintf()&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 格式化stdarg参量表并写到字符串<br><br>数字转换：<br>宽字符函数&nbsp;&nbsp;&nbsp;&nbsp; 普通C函数描述<br>wcstod()&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; strtod()&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 把宽字符的初始部分转换为双精度浮点数<br>wcstol()&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; strtol()&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 把宽字符的初始部分转换为长整数<br>wcstoul()&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; strtoul()&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 把宽字符的初始部分转换为无符号长整数<br><br>多字节字符和宽字符转换及操作：<br>宽字符函数描述<br>mblen()&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 根据locale的设置确定字符的字节数<br>mbstowcs()&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 把多字节字符串转换为宽字符串<br>mbtowc()&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /btowc()&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 把多字节字符转换为宽字符<br>wcstombs()&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 把宽字符串转换为多字节字符串<br>wctomb()&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /wctob()&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 把宽字符转换为多字节字符<br><br>输入和输出：<br>宽字符函数&nbsp;&nbsp;&nbsp;&nbsp; 普通C函数描述<br>fgetwc()&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; fgetc() 从流中读入一个字符并转换为宽字符<br>fgetws()&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; fgets() 从流中读入一个字符串并转换为宽字符串<br>fputwc()&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; fputc() 把宽字符转换为多字节字符并且输出到标准输出<br>fputws()&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; fputs() 把宽字符串转换为多字节字符并且输出到标准输出串<br>getwc() getc()&nbsp; 从标准输入中读取字符， 并且转换为宽字符<br>getwchar()&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; getchar()&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 从标准输入中读取字符， 并且转换为宽字符<br>None&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; gets()&nbsp; 使用fgetws()<br>putwc() putc()&nbsp; 把宽字符转换成多字节字符并且写到标准输出<br>putwchar()&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; putchar()&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 把宽字符转换成多字节字符并且写到标准输出<br>None&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; puts()&nbsp; 使用fputws()<br>ungetwc()&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ungetc()&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 把一个宽字符放回到输入流中<br><br>字符串操作：<br>宽字符函数&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 普通C函数描述<br>wcscat()&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; strcat()&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 把一个字符串接到另一个字符串的尾部<br>wcsncat()&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; strncat()&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 类似于wcscat()， 而且指定粘接字符串的粘接长度.<br>wcschr()&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; strchr()&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 查找子字符串的第一个位置<br>wcsrchr()&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; strrchr()&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 从尾部开始查找子字符串出现的第一个位置<br>wcspbrk()&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; strpbrk()&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 从一字符字符串中查找另一字符串中任何一个字符第一次出现的位置<br>wcswcs()&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /wcsstr(&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; strchr()&nbsp;&nbsp;&nbsp;&nbsp; 在一字符串中查找另一字符串第一次出现的位置<br>wcscspn()&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; strcspn()&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 返回不包含第二个字符串的的初始数目<br>wcsspn()&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; strspn()&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 返回包含第二个字符串的初始数目<br>wcscpy()&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; strcpy()&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 拷贝字符串<br>wcsncpy()&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; strncpy()&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 类似于wcscpy()， 同时指定拷贝的数目<br>wcscmp()&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; strcmp()&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 比较两个宽字符串<br>wcsncmp()&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; strncmp()&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 类似于wcscmp()， 还要指定比较字符字符串的数目<br>wcslen()&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; strlen()&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 获得宽字符串的数目<br>wcstok()&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; strtok()&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 根据标示符把宽字符串分解成一系列字符串<br>wcswidth()&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; None&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 获得宽字符串的宽度<br>wcwidth()&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; None&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 获得宽字符的宽度<br><br>另外还有对应于memory操作的 wmemcpy()， wmemchr()， wmemcmp()， wmemmove()， wmemset()． <img src ="http://www.cppblog.com/hktk/aggbug/50587.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/hktk/" target="_blank">海 阔 天 空</a> 2008-05-20 23:04 <a href="http://www.cppblog.com/hktk/archive/2008/05/20/50587.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>C++ 判断目录是否存在</title><link>http://www.cppblog.com/hktk/archive/2008/05/20/50465.html</link><dc:creator>海 阔 天 空</dc:creator><author>海 阔 天 空</author><pubDate>Mon, 19 May 2008 17:20:00 GMT</pubDate><guid>http://www.cppblog.com/hktk/archive/2008/05/20/50465.html</guid><description><![CDATA[<div style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top><span style="COLOR: #0000ff">bool</span><span style="COLOR: #000000">&nbsp;DirectoryExist(&nbsp;</span><span style="COLOR: #0000ff">const</span><span style="COLOR: #000000">&nbsp;std::</span><span style="COLOR: #0000ff">string</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">strPath&nbsp;)<br><img id=Codehighlighter1_50_111_Open_Image onclick="this.style.display='none'; Codehighlighter1_50_111_Open_Text.style.display='none'; Codehighlighter1_50_111_Closed_Image.style.display='inline'; Codehighlighter1_50_111_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockStart.gif" align=top><img id=Codehighlighter1_50_111_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_50_111_Closed_Text.style.display='none'; Codehighlighter1_50_111_Open_Image.style.display='inline'; Codehighlighter1_50_111_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedBlock.gif" align=top></span><span id=Codehighlighter1_50_111_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><img src="http://www.cppblog.com/Images/dot.gif"></span><span id=Codehighlighter1_50_111_Open_Text><span style="COLOR: #000000">{<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">return</span><span style="COLOR: #000000">&nbsp;::PathIsDirectory(strPath.c_str())&nbsp;</span><span style="COLOR: #000000">?</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">true</span><span style="COLOR: #000000">&nbsp;:&nbsp;</span><span style="COLOR: #0000ff">false</span><span style="COLOR: #000000">;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockEnd.gif" align=top>}</span></span><span style="COLOR: #000000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top></span></div>
<img src ="http://www.cppblog.com/hktk/aggbug/50465.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/hktk/" target="_blank">海 阔 天 空</a> 2008-05-20 01:20 <a href="http://www.cppblog.com/hktk/archive/2008/05/20/50465.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>