﻿<?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++博客-jack-wang-随笔分类-c++ 程序设计基础</title><link>https://www.cppblog.com/jack-wang/category/10923.html</link><description>小王</description><language>zh-cn</language><lastBuildDate>Mon, 11 Mar 2024 08:41:06 GMT</lastBuildDate><pubDate>Mon, 11 Mar 2024 08:41:06 GMT</pubDate><ttl>60</ttl><item><title>编译报错：/lib/../lib64/crt1.o：在函数‘_start’中(.text+0x20)对‘main’未定义的引用 collect2: error: ld returned 1exit status</title><link>http://www.cppblog.com/jack-wang/archive/2024/03/05/230298.html</link><dc:creator>小王</dc:creator><author>小王</author><pubDate>Tue, 05 Mar 2024 04:18:00 GMT</pubDate><guid>http://www.cppblog.com/jack-wang/archive/2024/03/05/230298.html</guid><wfw:comment>http://www.cppblog.com/jack-wang/comments/230298.html</wfw:comment><comments>http://www.cppblog.com/jack-wang/archive/2024/03/05/230298.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/jack-wang/comments/commentRss/230298.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/jack-wang/services/trackbacks/230298.html</trackback:ping><description><![CDATA[不用怀疑，把main()函数干没了，下次仔细检查一下吧，尼玛！<img src ="http://www.cppblog.com/jack-wang/aggbug/230298.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/jack-wang/" target="_blank">小王</a> 2024-03-05 12:18 <a href="http://www.cppblog.com/jack-wang/archive/2024/03/05/230298.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>小数点后保留N位</title><link>http://www.cppblog.com/jack-wang/archive/2022/10/20/229446.html</link><dc:creator>小王</dc:creator><author>小王</author><pubDate>Thu, 20 Oct 2022 07:34:00 GMT</pubDate><guid>http://www.cppblog.com/jack-wang/archive/2022/10/20/229446.html</guid><wfw:comment>http://www.cppblog.com/jack-wang/comments/229446.html</wfw:comment><comments>http://www.cppblog.com/jack-wang/archive/2022/10/20/229446.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/jack-wang/comments/commentRss/229446.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/jack-wang/services/trackbacks/229446.html</trackback:ping><description><![CDATA[<div>小数点后保留2位数：<br /><br />float x = 100.1234;</div><div>char buf[8] = { 0 };</div><div>::sprintf(buf, "%0.2lf", x);</div><img src ="http://www.cppblog.com/jack-wang/aggbug/229446.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/jack-wang/" target="_blank">小王</a> 2022-10-20 15:34 <a href="http://www.cppblog.com/jack-wang/archive/2022/10/20/229446.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>CMake报错："STREQUAL" "x86_64" Unknown arguments specified</title><link>http://www.cppblog.com/jack-wang/archive/2021/12/22/217888.html</link><dc:creator>小王</dc:creator><author>小王</author><pubDate>Wed, 22 Dec 2021 08:24:00 GMT</pubDate><guid>http://www.cppblog.com/jack-wang/archive/2021/12/22/217888.html</guid><wfw:comment>http://www.cppblog.com/jack-wang/comments/217888.html</wfw:comment><comments>http://www.cppblog.com/jack-wang/archive/2021/12/22/217888.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/jack-wang/comments/commentRss/217888.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/jack-wang/services/trackbacks/217888.html</trackback:ping><description><![CDATA[使用CMake时报错：<br /><p>"STREQUAL" "x86_64" Unknown arguments specified<br />解决：<br /><br /><span style="font-family: &quot;Source Code Pro&quot;, &quot;DejaVu Sans Mono&quot;, &quot;Ubuntu Mono&quot;, &quot;Anonymous Pro&quot;, &quot;Droid Sans Mono&quot;, Menlo, Monaco, Consolas, Inconsolata, Courier, monospace, &quot;PingFang SC&quot;, &quot;Microsoft YaHei&quot;, sans-serif; font-variant-ligatures: no-common-ligatures; white-space: pre; background-color: #fafafa;">if</span><span punctuation"="" style="box-sizing: border-box; outline: 0px; margin: 0px; padding: 0px; overflow-wrap: normal; font-family: &quot;Source Code Pro&quot;, &quot;DejaVu Sans Mono&quot;, &quot;Ubuntu Mono&quot;, &quot;Anonymous Pro&quot;, &quot;Droid Sans Mono&quot;, Menlo, Monaco, Consolas, Inconsolata, Courier, monospace, &quot;PingFang SC&quot;, &quot;Microsoft YaHei&quot;, sans-serif; color: #999999; word-break: break-all; white-space: pre; font-variant-numeric: normal !important; font-variant-east-asian: normal !important; font-stretch: normal !important; line-height: normal !important;">(</span><span variable"="" style="box-sizing: border-box; outline: 0px; margin: 0px; padding: 0px; overflow-wrap: normal; font-family: &quot;Source Code Pro&quot;, &quot;DejaVu Sans Mono&quot;, &quot;Ubuntu Mono&quot;, &quot;Anonymous Pro&quot;, &quot;Droid Sans Mono&quot;, Menlo, Monaco, Consolas, Inconsolata, Courier, monospace, &quot;PingFang SC&quot;, &quot;Microsoft YaHei&quot;, sans-serif; color: #ee9900; word-break: break-all; white-space: pre; font-variant-numeric: normal !important; font-variant-east-asian: normal !important; font-stretch: normal !important; line-height: normal !important;">$ENV</span><span punctuation"="" style="box-sizing: border-box; outline: 0px; margin: 0px; padding: 0px; overflow-wrap: normal; font-family: &quot;Source Code Pro&quot;, &quot;DejaVu Sans Mono&quot;, &quot;Ubuntu Mono&quot;, &quot;Anonymous Pro&quot;, &quot;Droid Sans Mono&quot;, Menlo, Monaco, Consolas, Inconsolata, Courier, monospace, &quot;PingFang SC&quot;, &quot;Microsoft YaHei&quot;, sans-serif; color: #999999; word-break: break-all; white-space: pre; font-variant-numeric: normal !important; font-variant-east-asian: normal !important; font-stretch: normal !important; line-height: normal !important;">(</span><span style="font-family: &quot;Source Code Pro&quot;, &quot;DejaVu Sans Mono&quot;, &quot;Ubuntu Mono&quot;, &quot;Anonymous Pro&quot;, &quot;Droid Sans Mono&quot;, Menlo, Monaco, Consolas, Inconsolata, Courier, monospace, &quot;PingFang SC&quot;, &quot;Microsoft YaHei&quot;, sans-serif; font-variant-ligatures: no-common-ligatures; white-space: pre; background-color: #fafafa;">COMPILING_TYPE</span><span punctuation"="" style="box-sizing: border-box; outline: 0px; margin: 0px; padding: 0px; overflow-wrap: normal; font-family: &quot;Source Code Pro&quot;, &quot;DejaVu Sans Mono&quot;, &quot;Ubuntu Mono&quot;, &quot;Anonymous Pro&quot;, &quot;Droid Sans Mono&quot;, Menlo, Monaco, Consolas, Inconsolata, Courier, monospace, &quot;PingFang SC&quot;, &quot;Microsoft YaHei&quot;, sans-serif; color: #999999; word-break: break-all; white-space: pre; font-variant-numeric: normal !important; font-variant-east-asian: normal !important; font-stretch: normal !important; line-height: normal !important;">)</span><span style="font-family: &quot;Source Code Pro&quot;, &quot;DejaVu Sans Mono&quot;, &quot;Ubuntu Mono&quot;, &quot;Anonymous Pro&quot;, &quot;Droid Sans Mono&quot;, Menlo, Monaco, Consolas, Inconsolata, Courier, monospace, &quot;PingFang SC&quot;, &quot;Microsoft YaHei&quot;, sans-serif; font-variant-ligatures: no-common-ligatures; white-space: pre; background-color: #fafafa;"> STREQUAL </span><span string"="" style="box-sizing: border-box; outline: 0px; margin: 0px; padding: 0px; overflow-wrap: normal; font-family: &quot;Source Code Pro&quot;, &quot;DejaVu Sans Mono&quot;, &quot;Ubuntu Mono&quot;, &quot;Anonymous Pro&quot;, &quot;Droid Sans Mono&quot;, Menlo, Monaco, Consolas, Inconsolata, Courier, monospace, &quot;PingFang SC&quot;, &quot;Microsoft YaHei&quot;, sans-serif; color: #50a14f; word-break: break-all; white-space: pre; font-variant-numeric: normal !important; font-variant-east-asian: normal !important; font-stretch: normal !important; line-height: normal !important;">"x86_64"</span><span punctuation"="" style="box-sizing: border-box; outline: 0px; margin: 0px; padding: 0px; overflow-wrap: normal; font-family: &quot;Source Code Pro&quot;, &quot;DejaVu Sans Mono&quot;, &quot;Ubuntu Mono&quot;, &quot;Anonymous Pro&quot;, &quot;Droid Sans Mono&quot;, Menlo, Monaco, Consolas, Inconsolata, Courier, monospace, &quot;PingFang SC&quot;, &quot;Microsoft YaHei&quot;, sans-serif; color: #999999; word-break: break-all; white-space: pre; font-variant-numeric: normal !important; font-variant-east-asian: normal !important; font-stretch: normal !important; line-height: normal !important;">)<br /><span style="color: #000000; font-family: verdana, &quot;courier new&quot;; white-space: normal;">改为：</span><br /><span style="color: #000000; font-variant-ligatures: no-common-ligatures; background-color: #fafafa;">if</span><span punctuation"="" style="box-sizing: border-box; outline: 0px; margin: 0px; padding: 0px; overflow-wrap: normal; word-break: break-all; font-variant-numeric: normal !important; font-variant-east-asian: normal !important; font-stretch: normal !important; line-height: normal !important;">((</span><span variable"="" style="box-sizing: border-box; outline: 0px; margin: 0px; padding: 0px; overflow-wrap: normal; color: #ee9900; word-break: break-all; font-variant-numeric: normal !important; font-variant-east-asian: normal !important; font-stretch: normal !important; line-height: normal !important;">$ENV</span><span punctuation"="" style="box-sizing: border-box; outline: 0px; margin: 0px; padding: 0px; overflow-wrap: normal; word-break: break-all; font-variant-numeric: normal !important; font-variant-east-asian: normal !important; font-stretch: normal !important; line-height: normal !important;">(</span><span style="color: #000000; font-variant-ligatures: no-common-ligatures; background-color: #fafafa;">COMPILING_TYPE</span><span punctuation"="" style="box-sizing: border-box; outline: 0px; margin: 0px; padding: 0px; overflow-wrap: normal; word-break: break-all; font-variant-numeric: normal !important; font-variant-east-asian: normal !important; font-stretch: normal !important; line-height: normal !important;">))</span><span style="color: #000000; font-variant-ligatures: no-common-ligatures; background-color: #fafafa;"> STREQUAL </span><span string"="" style="box-sizing: border-box; outline: 0px; margin: 0px; padding: 0px; overflow-wrap: normal; color: #50a14f; word-break: break-all; font-variant-numeric: normal !important; font-variant-east-asian: normal !important; font-stretch: normal !important; line-height: normal !important;">"x86_64"</span><span punctuation"="" style="box-sizing: border-box; outline: 0px; margin: 0px; padding: 0px; overflow-wrap: normal; word-break: break-all; font-variant-numeric: normal !important; font-variant-east-asian: normal !important; font-stretch: normal !important; line-height: normal !important;">)<br /><br /><br /><span style="color: #000000; font-family: verdana, &quot;courier new&quot;; white-space: normal;">O了！</span></span></span></p><img src ="http://www.cppblog.com/jack-wang/aggbug/217888.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/jack-wang/" target="_blank">小王</a> 2021-12-22 16:24 <a href="http://www.cppblog.com/jack-wang/archive/2021/12/22/217888.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Centos中升级gcc（编译cmake报错：‘make_unique’不是‘std’的成员）</title><link>http://www.cppblog.com/jack-wang/archive/2020/01/10/217078.html</link><dc:creator>小王</dc:creator><author>小王</author><pubDate>Fri, 10 Jan 2020 07:11:00 GMT</pubDate><guid>http://www.cppblog.com/jack-wang/archive/2020/01/10/217078.html</guid><wfw:comment>http://www.cppblog.com/jack-wang/comments/217078.html</wfw:comment><comments>http://www.cppblog.com/jack-wang/archive/2020/01/10/217078.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/jack-wang/comments/commentRss/217078.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/jack-wang/services/trackbacks/217078.html</trackback:ping><description><![CDATA[make_unique是包含在C++14中的，gcc版本过低，安装新版本gcc，比如8.x
<br />
1、安装centos-release-scl
<br />sudo yum install centos-release-scl
<br />
2、安装devtoolset
<br />sudo yum install devtoolset-9-gcc*
<br />（如果想安装7.*版本的，就改成devtoolset-7-gcc*）

<br />3、激活对应的devtoolset，所以你可以一次安装多个版本的devtoolset，<br />需要的时候用下面以下命令切换到对应的版本
<br />scl enable devtoolset-9 bash
<br />
4、查看gcc版本
gcc -v
显示为9.x

O了！<img src ="http://www.cppblog.com/jack-wang/aggbug/217078.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/jack-wang/" target="_blank">小王</a> 2020-01-10 15:11 <a href="http://www.cppblog.com/jack-wang/archive/2020/01/10/217078.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>几个基本的库函数，备忘</title><link>http://www.cppblog.com/jack-wang/archive/2010/09/14/126543.html</link><dc:creator>小王</dc:creator><author>小王</author><pubDate>Mon, 13 Sep 2010 16:24:00 GMT</pubDate><guid>http://www.cppblog.com/jack-wang/archive/2010/09/14/126543.html</guid><wfw:comment>http://www.cppblog.com/jack-wang/comments/126543.html</wfw:comment><comments>http://www.cppblog.com/jack-wang/archive/2010/09/14/126543.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/jack-wang/comments/commentRss/126543.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/jack-wang/services/trackbacks/126543.html</trackback:ping><description><![CDATA[double pow(double x double k);<br>返回x的k次方，可以另k=1/n,则函数返回的是x的n次方根 
<img src ="http://www.cppblog.com/jack-wang/aggbug/126543.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/jack-wang/" target="_blank">小王</a> 2010-09-14 00:24 <a href="http://www.cppblog.com/jack-wang/archive/2010/09/14/126543.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>代码自动生成-宏带来的奇技淫巧 </title><link>http://www.cppblog.com/jack-wang/archive/2010/06/22/118512.html</link><dc:creator>小王</dc:creator><author>小王</author><pubDate>Tue, 22 Jun 2010 15:54:00 GMT</pubDate><guid>http://www.cppblog.com/jack-wang/archive/2010/06/22/118512.html</guid><wfw:comment>http://www.cppblog.com/jack-wang/comments/118512.html</wfw:comment><comments>http://www.cppblog.com/jack-wang/archive/2010/06/22/118512.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.cppblog.com/jack-wang/comments/commentRss/118512.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/jack-wang/services/trackbacks/118512.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: &nbsp;&nbsp;<a href='http://www.cppblog.com/jack-wang/archive/2010/06/22/118512.html'>阅读全文</a><img src ="http://www.cppblog.com/jack-wang/aggbug/118512.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/jack-wang/" target="_blank">小王</a> 2010-06-22 23:54 <a href="http://www.cppblog.com/jack-wang/archive/2010/06/22/118512.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>VS2005/2008 warning C4251 needs to have dll-interface</title><link>http://www.cppblog.com/jack-wang/archive/2010/04/07/111826.html</link><dc:creator>小王</dc:creator><author>小王</author><pubDate>Wed, 07 Apr 2010 00:57:00 GMT</pubDate><guid>http://www.cppblog.com/jack-wang/archive/2010/04/07/111826.html</guid><wfw:comment>http://www.cppblog.com/jack-wang/comments/111826.html</wfw:comment><comments>http://www.cppblog.com/jack-wang/archive/2010/04/07/111826.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.cppblog.com/jack-wang/comments/commentRss/111826.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/jack-wang/services/trackbacks/111826.html</trackback:ping><description><![CDATA[<p style="FONT-SIZE: 10pt">&nbsp;&nbsp; 程序中消除warning有两种方法：消极一点不去理他，反正不是error:-)；积极一点，则想办法去掉。去掉又用两种方法：一种使用#pragma warning(disable: xxxx)，眼不见，心不烦；另外就是找出解决问题的办法了。<br>&nbsp; 今天做dll库时，在struct中用到了stl：<br>&nbsp; class CLASS_TEST<br>&nbsp; {<br>&nbsp;&nbsp;&nbsp; ...<br>&nbsp; private:<br>&nbsp;&nbsp;&nbsp; std::vector&lt;MY_STRUCT&gt; m_structs;<br>&nbsp; }<br>&nbsp; 但是编译时，vs2005给出了warning C4251: 'CLASS_TEST::m_structs' : class 'std::vector&lt;_Ty&gt;' needs to have dll-interface to be used by clients of class &#8216;CLASS_TEST&#8217;的警告信息。费了很大的劲才解决掉，记录下来。</p>
<p style="FONT-SIZE: 10pt">&nbsp; 在头文件中，定义宏<br>#ifdef MYDLL_EXPORTS<br>#define MYDLL_API __declspec(dllexport)<br>#else<br>#define MYDLL_API __declspec(dllimport)<br>#endif</p>
<p style="FONT-SIZE: 10pt">&nbsp; 现在，在变量m_structs前，添加：<br>template class MYDLL_API std::allocator&lt;myStruct&gt;;<br>template class MYDLL_API std::vector&lt;myStruct, std::allocator&lt;myStruct&gt; &gt;;<br>&nbsp; 这样，即可以了。</p>
<p style="FONT-SIZE: 10pt">&nbsp;</p>
<p style="FONT-SIZE: 10pt"><a href="http://blog.csdn.net/alwaysrun/archive/2008/12/22/3581987.aspx"></a>&nbsp;</p>
<img src ="http://www.cppblog.com/jack-wang/aggbug/111826.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/jack-wang/" target="_blank">小王</a> 2010-04-07 08:57 <a href="http://www.cppblog.com/jack-wang/archive/2010/04/07/111826.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>字节对齐详解</title><link>http://www.cppblog.com/jack-wang/archive/2010/03/17/109901.html</link><dc:creator>小王</dc:creator><author>小王</author><pubDate>Wed, 17 Mar 2010 07:55:00 GMT</pubDate><guid>http://www.cppblog.com/jack-wang/archive/2010/03/17/109901.html</guid><wfw:comment>http://www.cppblog.com/jack-wang/comments/109901.html</wfw:comment><comments>http://www.cppblog.com/jack-wang/archive/2010/03/17/109901.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/jack-wang/comments/commentRss/109901.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/jack-wang/services/trackbacks/109901.html</trackback:ping><description><![CDATA[<p style="FONT-SIZE: 10pt"><a href="http://blog.csdn.net/jszj/archive/2009/02/20/3915328.aspx">http://blog.csdn.net/jszj/archive/2009/02/20/3915328.aspx</a><br><br>一.什么是字节对齐,为什么要对齐?<br>&nbsp;&nbsp;&nbsp; 现代计算机中内存空间都是按照byte划分的，从理论上讲似乎对任何类型的变量的访问可以从任何地址开始，但实际情况是在访问特定类型变量的时候经常在特 定的内存地址访问，这就需要各种类型数据按照一定的规则在空间上排列，而不是顺序的一个接一个的排放，这就是对齐。<br>&nbsp;&nbsp;&nbsp; 对齐的作用和原因：各个硬件平台对存储空间的处理上有很大的不同。一些平台对某些特定类型的数据只能从某些特定地址开始存取。比如有些架构的CPU在访问 一个没有进行对齐的变量的时候会发生错误,那么在这种架构下编程必须保证字节对齐.其他平台可能没有这种情况，但是最常见的是如果不按照适合其平台要求对 数据存放进行对齐，会在存取效率上带来损失。比如有些平台每次读都是从偶地址开始，如果一个int型（假设为32位系统）如果存放在偶地址开始的地方，那 么一个读周期就可以读出这32bit，而如果存放在奇地址开始的地方，就需要2个读周期，并对两次读出的结果的高低字节进行拼凑才能得到该32bit数 据。显然在读取效率上下降很多。</p>
<p style="FONT-SIZE: 10pt">二.字节对齐对程序的影响:<br>&nbsp;&nbsp;&nbsp; 先让我们看几个例子吧(32bit,x86环境,gcc编译器):<br>设结构体如下定义：<br>struct A<br>{<br>&nbsp;&nbsp;&nbsp; int a;<br>&nbsp;&nbsp;&nbsp; char b;<br>&nbsp;&nbsp;&nbsp; short c;<br>};<br>struct B<br>{<br>&nbsp;&nbsp;&nbsp; char b;<br>&nbsp;&nbsp;&nbsp; int a;<br>&nbsp;&nbsp;&nbsp; short c;<br>};<br>现在已知32位机器上各种数据类型的长度如下:<br>char:1(有符号无符号同)&nbsp;&nbsp;&nbsp; <br>short:2(有符号无符号同)&nbsp;&nbsp;&nbsp; <br>int:4(有符号无符号同)&nbsp;&nbsp;&nbsp; <br>long:4(有符号无符号同)&nbsp;&nbsp;&nbsp; <br>float:4&nbsp;&nbsp;&nbsp; double:8<br>那么上面两个结构大小如何呢?<br>结果是:<br>sizeof(strcut A)值为8<br>sizeof(struct B)的值却是12</p>
<p style="FONT-SIZE: 10pt">结构体A中包含了4字节长度的int一个，1字节长度的char一个和2字节长度的short型数据一个,B也一样;按理说A,B大小应该都是7字节。<br>之所以出现上面的结果是因为编译器要对数据成员在空间上进行对齐。上面是按照编译器的默认设置进行对齐的结果,那么我们是不是可以改变编译器的这种默认对齐设置呢,当然可以.例如:<br>#pragma pack (2) /*指定按2字节对齐*/<br>struct C<br>{<br>&nbsp;&nbsp;&nbsp; char b;<br>&nbsp;&nbsp;&nbsp; int a;<br>&nbsp;&nbsp;&nbsp; short c;<br>};<br>#pragma pack () /*取消指定对齐，恢复缺省对齐*/<br>sizeof(struct C)值是8。<br>修改对齐值为1：<br>#pragma pack (1) /*指定按1字节对齐*/<br>struct D<br>{<br>&nbsp;&nbsp;&nbsp; char b;<br>&nbsp;&nbsp;&nbsp; int a;<br>&nbsp;&nbsp;&nbsp; short c;<br>};<br>#pragma pack () /*取消指定对齐，恢复缺省对齐*/<br>sizeof(struct D)值为7。<br>后面我们再讲解#pragma pack()的作用.</p>
<p style="FONT-SIZE: 10pt">三.编译器是按照什么样的原则进行对齐的?<br>&nbsp;&nbsp;&nbsp; 先让我们看四个重要的基本概念：<br>1.数据类型自身的对齐值：<br>&nbsp; 对于char型数据，其自身对齐值为1，对于short型为2，对于int,float,double类型，其自身对齐值为4，单位字节。<br>2.结构体或者类的自身对齐值：其成员中自身对齐值最大的那个值。<br>3.指定对齐值：#pragma pack (value)时的指定对齐值value。<br>4.数据成员、结构体和类的有效对齐值：自身对齐值和指定对齐值中小的那个值。<br>有 了这些值，我们就可以很方便的来讨论具体数据结构的成员和其自身的对齐方式。有效对齐值N是最终用来决定数据存放地址方式的值，最重要。有效对齐N，就是 表示&#8220;对齐在N上&#8221;，也就是说该数据的"存放起始地址%N=0".而数据结构中的数据变量都是按定义的先后顺序来排放的。第一个数据变量的起始地址就是数 据结构的起始地址。结构体的成员变量要对齐排放，结构体本身也要根据自身的有效对齐值圆整(就是结构体成员变量占用总长度需要是对结构体有效对齐值的整数 倍，结合下面例子理解)。这样就不能理解上面的几个例子的值了。<br>例子分析：<br>分析例子B；<br>struct B<br>{<br>&nbsp;&nbsp;&nbsp; char b;<br>&nbsp;&nbsp;&nbsp; int a;<br>&nbsp;&nbsp;&nbsp; short c;<br>};<br>假 设B从地址空间0x0000开始排放。该例子中没有定义指定对齐值，在笔者环境下，该值默认为4。第一个成员变量b的自身对齐值是1，比指定或者默认指定 对齐值4小，所以其有效对齐值为1，所以其存放地址0x0000符合0x0000%1=0.第二个成员变量a，其自身对齐值为4，所以有效对齐值也为4， 所以只能存放在起始地址为0x0004到0x0007这四个连续的字节空间中，复核0x0004%4=0,且紧靠第一个变量。第三个变量c,自身对齐值为 2，所以有效对齐值也是2，可以存放在0x0008到0x0009这两个字节空间中，符合0x0008%2=0。所以从0x0000到0x0009存放的 都是B内容。再看数据结构B的自身对齐值为其变量中最大对齐值(这里是b）所以就是4，所以结构体的有效对齐值也是4。根据结构体圆整的要求， 0x0009到0x0000=10字节，（10＋2）％4＝0。所以0x0000A到0x000B也为结构体B所占用。故B从0x0000到0x000B 共有12个字节,sizeof(struct B)=12;其实如果就这一个就来说它已将满足字节对齐了, 因为它的起始地址是0,因此肯定是对齐的,之所以在后面补充2个字节,是因为编译器为了实现结构数组的存取效率,试想如果我们定义了一个结构B的数组,那 么第一个结构起始地址是0没有问题,但是第二个结构呢?按照数组的定义,数组中所有元素都是紧挨着的,如果我们不把结构的大小补充为4的整数倍,那么下一 个结构的起始地址将是0x0000A,这显然不能满足结构的地址对齐了,因此我们要把结构补充成有效对齐大小的整数倍.其实诸如:对于char型数据，其 自身对齐值为1，对于short型为2，对于int,float,double类型，其自身对齐值为4，这些已有类型的自身对齐值也是基于数组考虑的,只 是因为这些类型的长度已知了,所以他们的自身对齐值也就已知了.<br>同理,分析上面例子C：<br>#pragma pack (2) /*指定按2字节对齐*/<br>struct C<br>{<br>&nbsp;&nbsp;&nbsp; char b;<br>&nbsp;&nbsp;&nbsp; int a;<br>&nbsp;&nbsp;&nbsp; short c;<br>};<br>#pragma pack () /*取消指定对齐，恢复缺省对齐*/<br>第 一个变量b的自身对齐值为1，指定对齐值为2，所以，其有效对齐值为1，假设C从0x0000开始，那么b存放在0x0000，符合0x0000%1= 0;第二个变量，自身对齐值为4，指定对齐值为2，所以有效对齐值为2，所以顺序存放在0x0002、0x0003、0x0004、0x0005四个连续 字节中，符合0x0002%2=0。第三个变量c的自身对齐值为2，所以有效对齐值为2，顺序存放<br>在0x0006、0x0007中，符合 0x0006%2=0。所以从0x0000到0x00007共八字节存放的是C的变量。又C的自身对齐值为4，所以C的有效对齐值为2。又8%2=0,C 只占用0x0000到0x0007的八个字节。所以sizeof(struct C)=8.</p>
<p style="FONT-SIZE: 10pt">四.如何修改编译器的默认对齐值?<br>1.在VC IDE中，可以这样修改：[Project]|[Settings],c/c++选项卡Category的Code Generation选项的Struct Member Alignment中修改，默认是8字节。<br>2.在编码时，可以这样动态修改：#pragma pack .注意:是pragma而不是progma.</p>
<p style="FONT-SIZE: 10pt">五.针对字节对齐,我们在编程中如何考虑?</p>
<p style="FONT-SIZE: 10pt">&nbsp;&nbsp;&nbsp; 如果在编程的时候要考虑节约空间的话,那么我们只需要假定结构的首地址是0,然后各个变量按照上面的原则进行排列即可,基本的原则就是把结构中的变量按照 类型大小从小到大声明,尽量减少中间的填补空间.还有一种就是为了以空间换取时间的效率,我们显示的进行填补空间进行对齐,比如:有一种使用空间换时间做 法是显式的插入reserved成员：<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; struct A{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; char a;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; char reserved[3];//使用空间换时间<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int b;<br>}</p>
<p style="FONT-SIZE: 10pt">reserved成员对我们的程序没有什么意义,它只是起到填补空间以达到字节对齐的目的,当然即使不加这个成员通常编译器也会给我们自动填补对齐,我们自己加上它只是起到显式的提醒作用.</p>
<p style="FONT-SIZE: 10pt">六.字节对齐可能带来的隐患:<br>&nbsp;&nbsp;&nbsp; 代码中关于对齐的隐患，很多是隐式的。比如在强制类型转换的时候。例如：<br>unsigned int i = 0x12345678;<br>unsigned char *p=NULL;<br>unsigned short *p1=NULL;</p>
<p style="FONT-SIZE: 10pt">p=&amp;i;<br>*p=0x00;<br>p1=(unsigned short *)(p+1);<br>*p1=0x0000;<br>最后两句代码，从奇数边界去访问unsignedshort型变量，显然不符合对齐的规定。<br>在x86上，类似的操作只会影响效率，但是在MIPS或者sparc上，可能就是一个error,因为它们要求必须字节对齐.</p>
<p style="FONT-SIZE: 10pt">七.如何查找与字节对齐方面的问题:<br>如果出现对齐或者赋值问题首先查看<br>1. 编译器的big little端设置<br>2. 看这种体系本身是否支持非对齐访问<br>3. 如果支持看设置了对齐与否,如果没有则看访问时需要加某些特殊的修饰来标志其特殊访问操作。</p>
<p style="FONT-SIZE: 10pt">八.相关文章:转自<a href="http://blog.csdn.net/goodluckyxl/archive/2005/10/17/506827.aspx">http://blog.csdn.net/goodluckyxl/archive/2005/10/17/506827.aspx</a></p>
<p style="FONT-SIZE: 10pt">&nbsp;ARM下的对齐处理 <br>from DUI0067D_ADS1_2_CompLib </p>
<p style="FONT-SIZE: 10pt">3.13 type&nbsp; qulifiers </p>
<p style="FONT-SIZE: 10pt">有部分摘自ARM编译器文档对齐部分</p>
<p style="FONT-SIZE: 10pt">对齐的使用:<br>1.__align(num)<br>&nbsp;&nbsp; 这个用于修改最高级别对象的字节边界。在汇编中使用LDRD或者STRD时<br>&nbsp;&nbsp; 就要用到此命令__align(8)进行修饰限制。来保证数据对象是相应对齐。<br>&nbsp;&nbsp; 这个修饰对象的命令最大是8个字节限制,可以让2字节的对象进行4字节<br>&nbsp;&nbsp; 对齐,但是不能让4字节的对象2字节对齐。<br>&nbsp;&nbsp; __align是存储类修改,他只修饰最高级类型对象不能用于结构或者函数对象。<br>&nbsp;&nbsp; <br>2.__packed <br>&nbsp; __packed是进行一字节对齐<br>&nbsp; 1.不能对packed的对象进行对齐<br>&nbsp; 2.所有对象的读写访问都进行非对齐访问<br>&nbsp; 3.float及包含float的结构联合及未用__packed的对象将不能字节对齐<br>&nbsp; 4.__packed对局部整形变量无影响<br>&nbsp; 5.强制由unpacked对象向packed对象转化是未定义,整形指针可以合法定<br>&nbsp; 义为packed。<br>&nbsp;&nbsp;&nbsp;&nbsp; __packed int* p;&nbsp; //__packed int 则没有意义<br>&nbsp; 6.对齐或非对齐读写访问带来问题<br>&nbsp; __packed struct STRUCT_TEST<br>&nbsp;{<br>&nbsp; char a;<br>&nbsp; int b;<br>&nbsp; char c;<br>&nbsp;}&nbsp; ;&nbsp;&nbsp;&nbsp; //定义如下结构此时b的起始地址一定是不对齐的<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //在栈中访问b可能有问题,因为栈上数据肯定是对齐访问[from CL]<br>//将下面变量定义成全局静态不在栈上 <br>static char* p;<br>static struct STRUCT_TEST a;<br>void Main()<br>{<br>&nbsp;__packed int* q;&nbsp; //此时定义成__packed来修饰当前q指向为非对齐的数据地址下面的访问则可以</p>
<p style="FONT-SIZE: 10pt">&nbsp;p = (char*)&amp;a;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br>&nbsp;q = (int*)(p+1);&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br>&nbsp;<br>&nbsp;*q = 0x87654321; <br>/*&nbsp;&nbsp; <br>得到赋值的汇编指令很清楚<br>ldr&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; r5,0x20001590 ; = #0x12345678<br>[0xe1a00005]&nbsp;&nbsp; mov&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; r0,r5<br>[0xeb0000b0]&nbsp;&nbsp; bl&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; __rt_uwrite4&nbsp; //在此处调用一个写4byte的操作函数 <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br>[0xe5c10000]&nbsp;&nbsp; strb&nbsp;&nbsp;&nbsp;&nbsp; r0,[r1,#0]&nbsp;&nbsp; //函数进行4次strb操作然后返回保证了数据正确的访问<br>[0xe1a02420]&nbsp;&nbsp; mov&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; r2,r0,lsr #8<br>[0xe5c12001]&nbsp;&nbsp; strb&nbsp;&nbsp;&nbsp;&nbsp; r2,[r1,#1]<br>[0xe1a02820]&nbsp;&nbsp; mov&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; r2,r0,lsr #16<br>[0xe5c12002]&nbsp;&nbsp; strb&nbsp;&nbsp;&nbsp;&nbsp; r2,[r1,#2]<br>[0xe1a02c20]&nbsp;&nbsp; mov&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; r2,r0,lsr #24<br>[0xe5c12003]&nbsp;&nbsp; strb&nbsp;&nbsp;&nbsp;&nbsp; r2,[r1,#3]<br>[0xe1a0f00e]&nbsp;&nbsp; mov&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; pc,r14<br>*/</p>
<p style="FONT-SIZE: 10pt">/*<br>如果q没有加__packed修饰则汇编出来指令是这样直接会导致奇地址处访问失败<br>[0xe59f2018]&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>*/</p>
<p style="FONT-SIZE: 10pt">//这样可以很清楚的看到非对齐访问是如何产生错误的<br>//以及如何消除非对齐访问带来问题<br>//也可以看到非对齐访问和对齐访问的指令差异导致效率问题<br>}<br>&nbsp; </p>
<p style="FONT-SIZE: 10pt"><br>本文来自CSDN博客，转载请标明出处：<a href="http://blog.csdn.net/jszj/archive/2009/02/20/3915328.aspx">http://blog.csdn.net/jszj/archive/2009/02/20/3915328.aspx</a></p>
<img src ="http://www.cppblog.com/jack-wang/aggbug/109901.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/jack-wang/" target="_blank">小王</a> 2010-03-17 15:55 <a href="http://www.cppblog.com/jack-wang/archive/2010/03/17/109901.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Visual C++2010的c++语言四大新特性</title><link>http://www.cppblog.com/jack-wang/archive/2010/03/15/109750.html</link><dc:creator>小王</dc:creator><author>小王</author><pubDate>Mon, 15 Mar 2010 10:25:00 GMT</pubDate><guid>http://www.cppblog.com/jack-wang/archive/2010/03/15/109750.html</guid><wfw:comment>http://www.cppblog.com/jack-wang/comments/109750.html</wfw:comment><comments>http://www.cppblog.com/jack-wang/archive/2010/03/15/109750.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/jack-wang/comments/commentRss/109750.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/jack-wang/services/trackbacks/109750.html</trackback:ping><description><![CDATA[<p style="FONT-SIZE: 10pt">转：<a href="http://blog.csdn.net/yincheng01/archive/2010/03/11/5367032.aspx">http://blog.csdn.net/yincheng01/archive/2010/03/11/5367032.aspx</a><br><br>微软即将在2010年4月12日发布VS2010的正式版，对于c++语言做了修改，使之更加符合c++标准。</p>
<p style="FONT-SIZE: 10pt">下面对于微软对于c++语言的修改做一下分析！</p>
<p style="FONT-SIZE: 10pt">Lambda表达式</p>
<p style="FONT-SIZE: 10pt">&nbsp; 很多编程编程语言都支持匿名函数(anonymous function)。所谓匿名函数，就是这个函数只有函数体，而没有函数名。Lambda表达式就是实现匿名函数的一种编程技巧，它为编写匿名函数提供了简明的函数式的句法。同样是Visual Studio中的开发语言，Visual Basic和Visual C#早就实现了对Lambda表达式的支持，终于Visual C++这次也不甘落后，在Visual Studio 2010中添加了对Lambda表达式的支持。<br>&nbsp; Lambda表达式使得函数可以在使用的地方定义，并且可以在Lambda函数中使用Lambda函数之外的数据。这就为针对集合操作带来了很大的便利。在作用上，Lambda表达式类似于函数指针和函数对象，Lambda表达式很好地兼顾了函数指针和函数对象的优点，却没有它们的缺点。相对于函数指针或是函数对象复杂的语法形式，Lambda表达式使用非常简单的语法就可以实现同样的功能，降低了Lambda表达式的学习难度，避免了使用复杂的函数对象或是函数指针所带来的错误。我们可以看一个实际的例子：</p>
<p style="FONT-SIZE: 10pt">view plaincopy to clipboardprint?<br>&#183;&#183;&#183;&#183;&#183;&#183;&#183;&#183;&#183;10&#183;&#183;&#183;&#183;&#183;&#183;&#183;&#183;20&#183;&#183;&#183;&#183;&#183;&#183;&#183;&#183;30&#183;&#183;&#183;&#183;&#183;&#183;&#183;&#183;40&#183;&#183;&#183;&#183;&#183;&#183;&#183;&#183;50&#183;&#183;&#183;&#183;&#183;&#183;&#183;&#183;60&#183;&#183;&#183;&#183;&#183;&#183;&#183;&#183;70&#183;&#183;&#183;&#183;&#183;&#183;&#183;&#183;80&#183;&#183;&#183;&#183;&#183;&#183;&#183;&#183;90&#183;&#183;&#183;&#183;&#183;&#183;&#183;&#183;100&#183;&#183;&#183;&#183;&#183;&#183;&#183;110&#183;&#183;&#183;&#183;&#183;&#183;&#183;120&#183;&#183;&#183;&#183;&#183;&#183;&#183;130&#183;&#183;&#183;&#183;&#183;&#183;&#183;140&#183;&#183;&#183;&#183;&#183;&#183;&#183;150<br>#include "stdafx.h"&nbsp;&nbsp; <br>#include &lt;algorithm&gt;&nbsp;&nbsp; <br>#include &lt;iostream&gt;&nbsp;&nbsp; <br>#include &lt;ostream&gt;&nbsp;&nbsp; <br>#include &lt;vector&gt;&nbsp;&nbsp; <br>&nbsp; <br>using namespace std;&nbsp;&nbsp; <br>&nbsp; <br>int _tmain(int argc, _TCHAR* argv[])&nbsp;&nbsp; <br>{&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp; vector&lt;int&gt; v;&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp; for (int i = 0; i &lt; 10; ++i) {&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; v.push_back(i);&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp; }&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; for_each(v.begin(), v.end(), [] (int n) {&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; cout &lt;&lt; n;&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (n % 2 == 0) {&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; cout &lt;&lt; " even ";&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; } else {&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; cout &lt;&lt; " odd ";&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp; });&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp; cout &lt;&lt; endl;&nbsp;&nbsp; <br>&nbsp; <br>&nbsp;&nbsp;&nbsp; return 0;&nbsp;&nbsp; <br>}&nbsp; <br>#include "stdafx.h"<br>#include &lt;algorithm&gt;<br>#include &lt;iostream&gt;<br>#include &lt;ostream&gt;<br>#include &lt;vector&gt;</p>
<p style="FONT-SIZE: 10pt">using namespace std;</p>
<p style="FONT-SIZE: 10pt">int _tmain(int argc, _TCHAR* argv[])<br>{<br>&nbsp;&nbsp;&nbsp; vector&lt;int&gt; v;<br>&nbsp;&nbsp;&nbsp; for (int i = 0; i &lt; 10; ++i) {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; v.push_back(i);<br>&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; for_each(v.begin(), v.end(), [] (int n) {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; cout &lt;&lt; n;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (n % 2 == 0) {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; cout &lt;&lt; " even ";<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; } else {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; cout &lt;&lt; " odd ";<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp; });<br>&nbsp;&nbsp;&nbsp; cout &lt;&lt; endl;</p>
<p style="FONT-SIZE: 10pt">&nbsp;&nbsp;&nbsp; return 0;<br>}<br>&nbsp;</p>
<p style="FONT-SIZE: 10pt">&nbsp; 这段代码循环遍历输出vector中的每一个数，并判断这个数是奇数还是偶数。我们可以随时修改Lambda表达式而改变这个匿名函数的实现，修改对集合的操作。在这段代码中，C++使用一对中括号&#8220;[]&#8221;来表示Lambda表达式的开始，其后的&#8221;(int n)&#8221;表示Lambda表达式的参数。这些参数将在Lambda表达式中使用到。为了体会Lambda表达式的简洁，我们来看看同样的功能，如何使用函数对象实现：<br>&nbsp;<br>view plaincopy to clipboardprint?<br>&#183;&#183;&#183;&#183;&#183;&#183;&#183;&#183;&#183;10&#183;&#183;&#183;&#183;&#183;&#183;&#183;&#183;20&#183;&#183;&#183;&#183;&#183;&#183;&#183;&#183;30&#183;&#183;&#183;&#183;&#183;&#183;&#183;&#183;40&#183;&#183;&#183;&#183;&#183;&#183;&#183;&#183;50&#183;&#183;&#183;&#183;&#183;&#183;&#183;&#183;60&#183;&#183;&#183;&#183;&#183;&#183;&#183;&#183;70&#183;&#183;&#183;&#183;&#183;&#183;&#183;&#183;80&#183;&#183;&#183;&#183;&#183;&#183;&#183;&#183;90&#183;&#183;&#183;&#183;&#183;&#183;&#183;&#183;100&#183;&#183;&#183;&#183;&#183;&#183;&#183;110&#183;&#183;&#183;&#183;&#183;&#183;&#183;120&#183;&#183;&#183;&#183;&#183;&#183;&#183;130&#183;&#183;&#183;&#183;&#183;&#183;&#183;140&#183;&#183;&#183;&#183;&#183;&#183;&#183;150<br>#include "stdafx.h"&nbsp;&nbsp; <br>#include &lt;algorithm&gt;&nbsp;&nbsp; <br>#include &lt;iostream&gt;&nbsp;&nbsp; <br>#include &lt;ostream&gt;&nbsp;&nbsp; <br>#include &lt;vector&gt;&nbsp;&nbsp; <br>using namespace std;&nbsp;&nbsp; <br>&nbsp; <br>struct LambdaFunctor {&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp; void operator()(int n) const {&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; cout &lt;&lt; n &lt;&lt; " ";&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (n % 2 == 0) {&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; cout &lt;&lt; " even ";&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; } else {&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; cout &lt;&lt; " odd ";&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }&nbsp;&nbsp; <br>&nbsp; <br>&nbsp;&nbsp;&nbsp; }&nbsp;&nbsp; <br>};&nbsp;&nbsp; <br>&nbsp; <br>int _tmain(int argc, _TCHAR* argv[])&nbsp;&nbsp; <br>{&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp; vector&lt;int&gt; v;&nbsp;&nbsp; <br>&nbsp; <br>&nbsp;&nbsp;&nbsp; for (int i = 0; i &lt; 10; ++i) {&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; v.push_back(i);&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp; }&nbsp;&nbsp; <br>&nbsp; <br>&nbsp;&nbsp;&nbsp; for_each(v.begin(), v.end(), LambdaFunctor());&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp; cout &lt;&lt; endl;&nbsp;&nbsp; <br>&nbsp; <br>&nbsp;&nbsp;&nbsp; return 0;&nbsp;&nbsp; <br>}&nbsp; <br>#include "stdafx.h"<br>#include &lt;algorithm&gt;<br>#include &lt;iostream&gt;<br>#include &lt;ostream&gt;<br>#include &lt;vector&gt;<br>using namespace std;</p>
<p style="FONT-SIZE: 10pt">struct LambdaFunctor {<br>&nbsp;&nbsp;&nbsp; void operator()(int n) const {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; cout &lt;&lt; n &lt;&lt; " ";<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (n % 2 == 0) {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; cout &lt;&lt; " even ";<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; } else {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; cout &lt;&lt; " odd ";<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</p>
<p style="FONT-SIZE: 10pt">&nbsp;&nbsp;&nbsp; }<br>};</p>
<p style="FONT-SIZE: 10pt">int _tmain(int argc, _TCHAR* argv[])<br>{<br>&nbsp;&nbsp;&nbsp; vector&lt;int&gt; v;</p>
<p style="FONT-SIZE: 10pt">&nbsp;&nbsp;&nbsp; for (int i = 0; i &lt; 10; ++i) {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; v.push_back(i);<br>&nbsp;&nbsp;&nbsp; }</p>
<p style="FONT-SIZE: 10pt">&nbsp;&nbsp;&nbsp; for_each(v.begin(), v.end(), LambdaFunctor());<br>&nbsp;&nbsp;&nbsp; cout &lt;&lt; endl;</p>
<p style="FONT-SIZE: 10pt">&nbsp;&nbsp;&nbsp; return 0;<br>}<br>&nbsp;<br>&nbsp; 通过比较我们就可以发现，Lambda表达式的语法更加简洁，使用起来更加简单高效</p>
<p style="FONT-SIZE: 10pt">静态断言static_assert</p>
<p style="FONT-SIZE: 10pt">&nbsp; 在之前的C++标准C++03中，我们可以使用两种断言：<br>&nbsp; &#8226; 使用预处理中的条件编译和#error指令，可以在预处理阶段检查一些编译条件<br>&nbsp; &#8226; 可以使用宏assert来进行运行时检查，以确保程序逻辑的正确性</p>
<p style="FONT-SIZE: 10pt">&nbsp; 但使用#error方法是非常烦琐的，并且不能够对模板参数进行检查，因为模板实例化是在编译时进行，而#error方法是在预处理阶段进行的。而assert宏是在运行时进行检查。不难发现，我们缺少了一样东西，那就是可用于在编译时检查的工具。于是，静态断言应运而生。</p>
<p style="FONT-SIZE: 10pt">&nbsp; 在新的C++标准C++0x中，加入了对静态断言的支持，引入了新的关键字static_assert来表示静态断言。使用静态断言，我们可以在程序的编译时期检测一些条件是否成立，这个特性在调试模板函数的模板参数时特别有用。在编译的时候，模板函数实例化，这时我们就可以使用静态断言去测试模板函数的参数是否按照我们的设计拥有合适的值。例如下面这段代码：</p>
<p style="FONT-SIZE: 10pt">view plaincopy to clipboardprint?<br>&#183;&#183;&#183;&#183;&#183;&#183;&#183;&#183;&#183;10&#183;&#183;&#183;&#183;&#183;&#183;&#183;&#183;20&#183;&#183;&#183;&#183;&#183;&#183;&#183;&#183;30&#183;&#183;&#183;&#183;&#183;&#183;&#183;&#183;40&#183;&#183;&#183;&#183;&#183;&#183;&#183;&#183;50&#183;&#183;&#183;&#183;&#183;&#183;&#183;&#183;60&#183;&#183;&#183;&#183;&#183;&#183;&#183;&#183;70&#183;&#183;&#183;&#183;&#183;&#183;&#183;&#183;80&#183;&#183;&#183;&#183;&#183;&#183;&#183;&#183;90&#183;&#183;&#183;&#183;&#183;&#183;&#183;&#183;100&#183;&#183;&#183;&#183;&#183;&#183;&#183;110&#183;&#183;&#183;&#183;&#183;&#183;&#183;120&#183;&#183;&#183;&#183;&#183;&#183;&#183;130&#183;&#183;&#183;&#183;&#183;&#183;&#183;140&#183;&#183;&#183;&#183;&#183;&#183;&#183;150<br>template &lt;int N&gt; struct Kitten {&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp; static_assert(N &lt; 2, "Kitten&lt;N&gt; requires N &lt; 2.");&nbsp;&nbsp; <br>};&nbsp;&nbsp; <br>&nbsp; <br>int main() {&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp; Kitten&lt;1&gt; peppermint;&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp; Kitten&lt;3&gt; jazz;&nbsp;&nbsp; <br>&nbsp; <br>&nbsp;&nbsp; return 0;&nbsp;&nbsp; <br>}&nbsp; <br>template &lt;int N&gt; struct Kitten {<br>&nbsp;&nbsp;&nbsp; static_assert(N &lt; 2, "Kitten&lt;N&gt; requires N &lt; 2.");<br>};</p>
<p style="FONT-SIZE: 10pt">int main() {<br>&nbsp;&nbsp;&nbsp; Kitten&lt;1&gt; peppermint;<br>&nbsp;&nbsp;&nbsp; Kitten&lt;3&gt; jazz;</p>
<p style="FONT-SIZE: 10pt">&nbsp;&nbsp; return 0;<br>}<br>&nbsp;<br>&nbsp; 当我们在主函数中使用&#8220;1&#8221;去实例化Kitten这个结构体时，在编译的时候，静态断言static_assert会测试参数N的值，当N的值小于2时就会产生一个断言错误，并将相应的调试帮助信息输出到&#8220;Error List&#8221;窗口中，这样程序员就可以对问题快速定位，解决问题就更加方便了。<br>&nbsp;</p>
<p style="FONT-SIZE: 10pt"><br>图2 static_assert断言及其输出</p>
<p style="FONT-SIZE: 10pt"><br>&nbsp; 另外，静态断言还带来很多其他的优势。例如静态断言在编译时进行处理，不会产生任何运行时刻空间和时间上的开销，这就使得它比assert宏具有更好的效率。另外比较重要的一个特性是如果断言失败，它会产生有意义且充分的诊断信息，帮助程序员快速解决问题。</p>
<p style="FONT-SIZE: 10pt">&nbsp; auto关键字</p>
<p style="FONT-SIZE: 10pt">&nbsp; 在C++0x中，auto关键字的意义发生了改变。从Visual C++ 2010开始，auto关键字将用于指引编译器根据变量的初始值来决定变量的数据类型。换句话说，我们可以把auto当成一种新的数据类型，它可以&#8220;从初始化器(initialize)中推导出所代表的变量的真正类型&#8221;。这种对auto关键字的使用方式可以大大消除当前替代方式所导致的冗长和易出错的代码。我们看一个实际的例子：</p>
<p style="FONT-SIZE: 10pt">view plaincopy to clipboardprint?<br>&#183;&#183;&#183;&#183;&#183;&#183;&#183;&#183;&#183;10&#183;&#183;&#183;&#183;&#183;&#183;&#183;&#183;20&#183;&#183;&#183;&#183;&#183;&#183;&#183;&#183;30&#183;&#183;&#183;&#183;&#183;&#183;&#183;&#183;40&#183;&#183;&#183;&#183;&#183;&#183;&#183;&#183;50&#183;&#183;&#183;&#183;&#183;&#183;&#183;&#183;60&#183;&#183;&#183;&#183;&#183;&#183;&#183;&#183;70&#183;&#183;&#183;&#183;&#183;&#183;&#183;&#183;80&#183;&#183;&#183;&#183;&#183;&#183;&#183;&#183;90&#183;&#183;&#183;&#183;&#183;&#183;&#183;&#183;100&#183;&#183;&#183;&#183;&#183;&#183;&#183;110&#183;&#183;&#183;&#183;&#183;&#183;&#183;120&#183;&#183;&#183;&#183;&#183;&#183;&#183;130&#183;&#183;&#183;&#183;&#183;&#183;&#183;140&#183;&#183;&#183;&#183;&#183;&#183;&#183;150<br>#include &lt;iostream&gt;&nbsp;&nbsp; <br>#include &lt;map&gt;&nbsp;&nbsp; <br>#include &lt;ostream&gt;&nbsp;&nbsp; <br>#include &lt;regex&gt;&nbsp;&nbsp; <br>#include &lt;string&gt;&nbsp;&nbsp; <br>using namespace std;&nbsp;&nbsp; <br>using namespace std::tr1;&nbsp;&nbsp; <br>&nbsp; <br>int main() {&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp; map&lt;string, string&gt; m;&nbsp;&nbsp; <br>&nbsp; <br>&nbsp;&nbsp;&nbsp; const regex r("(<a href="file://w/">\\w</a>+) (<a href="file://w/">\\w</a>+)");&nbsp;&nbsp; <br>&nbsp; <br>&nbsp;&nbsp;&nbsp; for (string s; getline(cin, s); ) {&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; smatch results;&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (regex_match(s, results, r)) {&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; m[results[1]] = results[2];&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp; }&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp; for (auto i = m.begin(); i != m.end(); ++i) {&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; cout &lt;&lt; i-&gt;second &lt;&lt; " are " &lt;&lt; i-&gt;first &lt;&lt; endl;&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp; }&nbsp;&nbsp; <br>&nbsp; <br>&nbsp;&nbsp;&nbsp; return 0;&nbsp;&nbsp; <br>}&nbsp; <br>#include &lt;iostream&gt;<br>#include &lt;map&gt;<br>#include &lt;ostream&gt;<br>#include &lt;regex&gt;<br>#include &lt;string&gt;<br>using namespace std;<br>using namespace std::tr1;</p>
<p style="FONT-SIZE: 10pt">int main() {<br>&nbsp;&nbsp;&nbsp; map&lt;string, string&gt; m;</p>
<p style="FONT-SIZE: 10pt">&nbsp;&nbsp;&nbsp; const regex r("(<a href="file://w/">\\w</a>+) (<a href="file://w/">\\w</a>+)");</p>
<p style="FONT-SIZE: 10pt">&nbsp;&nbsp;&nbsp; for (string s; getline(cin, s); ) {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; smatch results;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (regex_match(s, results, r)) {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; m[results[1]] = results[2];<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp; for (auto i = m.begin(); i != m.end(); ++i) {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; cout &lt;&lt; i-&gt;second &lt;&lt; " are " &lt;&lt; i-&gt;first &lt;&lt; endl;<br>&nbsp;&nbsp;&nbsp; }</p>
<p style="FONT-SIZE: 10pt">&nbsp;&nbsp;&nbsp; return 0;<br>}<br>&nbsp;<br>&nbsp; 在这段代码中，我们使用auto关键字来代替了真正的数据类型map&lt;string, string&gt;::iterator，这使得整个代码自然而简洁。</p>
<p style="FONT-SIZE: 10pt">&nbsp; 另外，跟其他数据类型一样，我们也可以对auto关键字进行修饰，例如添加const，指针(*)，左值引用(&amp;)，右值引用(&amp;&amp;)等等，编译器会根据auto类型所代表的真正的数据来决定这些修饰的具体含义。</p>
<p style="FONT-SIZE: 10pt">&nbsp; 为了兼容一些旧有的C++代码，我们可以使用/Zc:auto这个编译器选项，来告诉编译器是采用auto关键字的原有定义还是在新标准C++0x中的定义。</p>
<p style="FONT-SIZE: 10pt">右值引用</p>
<p style="FONT-SIZE: 10pt">&nbsp; 作为最重要的一项语言特性，右值引用(rvalue references)被引入到 C++0x中。我们可以通过操作符&#8220;&amp;&amp;&#8221;来声明一个右值引用，原先在C++中使用&#8220;&amp;&#8221;操作符声明的引用现在被称为左值引用。 </p>
<p style="FONT-SIZE: 10pt">int a;<br>int&amp; a_lvref = a;&nbsp; // 左值引用</p>
<p style="FONT-SIZE: 10pt">int b;<br>int&amp;&amp; b_rvref = b;&nbsp; // 右值应用<br>&nbsp; 左值引用和右值引用的表现行为基本一致，它们唯一的差别就是右值引用可以绑定到一个临时对象(右值)上，而左值引用不可以。例如： </p>
<p style="FONT-SIZE: 10pt">int&amp; a_lvref = int();&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // error C2440: 'initializing' : cannot convert from 'int' to 'int &amp;'&nbsp;&nbsp;&nbsp; <br>int&amp;&amp; b_rvref = int();&nbsp; // OK!<br>&nbsp; 在第一行代码中，我们将一个临时对象int()绑定到一个左值引用，将产生一个编译错误。而在第二行中，我们将临时对象绑定到右值引用，就可以顺利通过编译。</p>
<p style="FONT-SIZE: 10pt">&nbsp; 右值是无名的数据，例如函数的返回值一般说来就是右值。当对右值进行操作的时候，右值本身往往没有必要保留，因此在某些情况下可以直接&#8220;移动&#8221;之。通过右值引用，程序可以明确的区分出传入的参数是否为右值，从而避免了不必要的拷贝，程序的效率也就得到了提高。我们考虑一个简单的数据交换的小程序，从中来体会右值引用所带来的效率提升。我们可以写一个函数swap来实现两个变量值的交换：</p>
<p style="FONT-SIZE: 10pt">view plaincopy to clipboardprint?<br>&#183;&#183;&#183;&#183;&#183;&#183;&#183;&#183;&#183;10&#183;&#183;&#183;&#183;&#183;&#183;&#183;&#183;20&#183;&#183;&#183;&#183;&#183;&#183;&#183;&#183;30&#183;&#183;&#183;&#183;&#183;&#183;&#183;&#183;40&#183;&#183;&#183;&#183;&#183;&#183;&#183;&#183;50&#183;&#183;&#183;&#183;&#183;&#183;&#183;&#183;60&#183;&#183;&#183;&#183;&#183;&#183;&#183;&#183;70&#183;&#183;&#183;&#183;&#183;&#183;&#183;&#183;80&#183;&#183;&#183;&#183;&#183;&#183;&#183;&#183;90&#183;&#183;&#183;&#183;&#183;&#183;&#183;&#183;100&#183;&#183;&#183;&#183;&#183;&#183;&#183;110&#183;&#183;&#183;&#183;&#183;&#183;&#183;120&#183;&#183;&#183;&#183;&#183;&#183;&#183;130&#183;&#183;&#183;&#183;&#183;&#183;&#183;140&#183;&#183;&#183;&#183;&#183;&#183;&#183;150<br>template &lt;class T&gt; swap(T&amp; a, T&amp; b)&nbsp;&nbsp; <br>{&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp; T tmp(a);&nbsp;&nbsp; // tmp对象创建后，我们就拥有了a的两份拷贝&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp; a = b;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // 现在我们拥有b的两份拷贝&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp; b = tmp;&nbsp;&nbsp;&nbsp; // 现在我们拥有a的两份拷贝&nbsp;&nbsp; <br>}&nbsp; <br>template &lt;class T&gt; swap(T&amp; a, T&amp; b)<br>{<br>&nbsp;&nbsp;&nbsp; T tmp(a);&nbsp;&nbsp; // tmp对象创建后，我们就拥有了a的两份拷贝<br>&nbsp;&nbsp;&nbsp; a = b;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // 现在我们拥有b的两份拷贝<br>&nbsp;&nbsp;&nbsp; b = tmp;&nbsp;&nbsp;&nbsp; // 现在我们拥有a的两份拷贝<br>} <br>&nbsp; 在这段代码中，虽然我们只是为了进行简单的数据交换，但是却执行了多次对象拷贝。这些对象的拷贝操作，特别是当这些对象比较大的时候，无疑会影响程序的效率。</p>
<p style="FONT-SIZE: 10pt">&nbsp; 那么，如果使用右值引用如何实现呢？</p>
<p style="FONT-SIZE: 10pt">view plaincopy to clipboardprint?<br>&#183;&#183;&#183;&#183;&#183;&#183;&#183;&#183;&#183;10&#183;&#183;&#183;&#183;&#183;&#183;&#183;&#183;20&#183;&#183;&#183;&#183;&#183;&#183;&#183;&#183;30&#183;&#183;&#183;&#183;&#183;&#183;&#183;&#183;40&#183;&#183;&#183;&#183;&#183;&#183;&#183;&#183;50&#183;&#183;&#183;&#183;&#183;&#183;&#183;&#183;60&#183;&#183;&#183;&#183;&#183;&#183;&#183;&#183;70&#183;&#183;&#183;&#183;&#183;&#183;&#183;&#183;80&#183;&#183;&#183;&#183;&#183;&#183;&#183;&#183;90&#183;&#183;&#183;&#183;&#183;&#183;&#183;&#183;100&#183;&#183;&#183;&#183;&#183;&#183;&#183;110&#183;&#183;&#183;&#183;&#183;&#183;&#183;120&#183;&#183;&#183;&#183;&#183;&#183;&#183;130&#183;&#183;&#183;&#183;&#183;&#183;&#183;140&#183;&#183;&#183;&#183;&#183;&#183;&#183;150<br>#include "stdafx.h"&nbsp;&nbsp; <br>&nbsp; <br>template &lt;class T&gt;&nbsp;&nbsp;&nbsp; <br>T&amp;&amp; move(T&amp;&amp; a)&nbsp;&nbsp; <br>{&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp; return a;&nbsp;&nbsp; <br>}&nbsp;&nbsp; <br>&nbsp; <br>template &lt;class T&gt; void swap(T&amp; a, T&amp; b)&nbsp;&nbsp; <br>{&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp; T tmp(move(a)); // 对象a被移动到对象tmp，a被清空&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp; a = move(b);&nbsp;&nbsp;&nbsp; // 对象b被移动到对象a，b被清空&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp; b = move(tmp);&nbsp; // 对象tmp被移动到对象b&nbsp;&nbsp; <br>}&nbsp;&nbsp; <br>&nbsp; <br>int _tmain(int argc, _TCHAR* argv[])&nbsp;&nbsp; <br>{&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp; int a = 1;&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp; int b = 2;&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp; swap(a, b);&nbsp;&nbsp; <br>&nbsp; <br>&nbsp;&nbsp; return 0;&nbsp;&nbsp; <br>}&nbsp; <br>#include "stdafx.h"</p>
<p style="FONT-SIZE: 10pt">template &lt;class T&gt; <br>T&amp;&amp; move(T&amp;&amp; a)<br>{<br>&nbsp;&nbsp;&nbsp; return a;<br>}</p>
<p style="FONT-SIZE: 10pt">template &lt;class T&gt; void swap(T&amp; a, T&amp; b)<br>{<br>&nbsp;&nbsp;&nbsp; T tmp(move(a)); // 对象a被移动到对象tmp，a被清空<br>&nbsp;&nbsp;&nbsp; a = move(b);&nbsp;&nbsp;&nbsp; // 对象b被移动到对象a，b被清空<br>&nbsp;&nbsp;&nbsp; b = move(tmp);&nbsp; // 对象tmp被移动到对象b<br>}</p>
<p style="FONT-SIZE: 10pt">int _tmain(int argc, _TCHAR* argv[])<br>{<br>&nbsp;&nbsp;&nbsp; int a = 1;<br>&nbsp;&nbsp;&nbsp; int b = 2;<br>&nbsp;&nbsp;&nbsp; swap(a, b);</p>
<p style="FONT-SIZE: 10pt">&nbsp;&nbsp; return 0;<br>}<br>&nbsp;</p>
<p style="FONT-SIZE: 10pt"><br>&nbsp; 在这段重新实现的代码中，我们使用了一个move()函数来代替对象的赋值操作符&#8220;=&#8221;,move()只是简单地接受一个右值引用或者左值引用作为参数，然后直接返回相应对象的右值引用。这一过程不会产生拷贝(Copy)操作，而只会将源对象移动(Move)到目标对象。</p>
<p style="FONT-SIZE: 10pt">&nbsp; 正是拷贝(Copy)和移动(Move)的差别，使得右值引用成为C++0x中最激动人心的新特性之一。从实践角度讲，它能够完美是解决C++中长久以来为人所诟病的临时对象的效率问题。从语言本身讲，它健全了C++中的引用类型在左值右值方面的缺陷。从库设计者的角度讲，它给库设计者又带来了一把利器。而对于广大的库使用者而言，不动一兵一卒便能够获得&#8220;免费的&#8221;效率提升。</p>
<p style="FONT-SIZE: 10pt">&nbsp; 在Visual Studio 2010中，因为有了对这些C++0x新特性的支持，重新点燃了程序员们对C++的热情。C++重振雄风，指日可待！</p>
<p style="FONT-SIZE: 10pt"><br>本文来自CSDN博客，转载请标明出处：<a href="http://blog.csdn.net/yincheng01/archive/2010/03/11/5367032.aspx">http://blog.csdn.net/yincheng01/archive/2010/03/11/5367032.aspx</a></p>
<p style="FONT-SIZE: 10pt">&nbsp;</p>
<img src ="http://www.cppblog.com/jack-wang/aggbug/109750.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/jack-wang/" target="_blank">小王</a> 2010-03-15 18:25 <a href="http://www.cppblog.com/jack-wang/archive/2010/03/15/109750.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>今天工作中突然提到了stdcall，我都有些忘了，差点被主管问倒</title><link>http://www.cppblog.com/jack-wang/archive/2010/01/21/106195.html</link><dc:creator>小王</dc:creator><author>小王</author><pubDate>Thu, 21 Jan 2010 15:43:00 GMT</pubDate><guid>http://www.cppblog.com/jack-wang/archive/2010/01/21/106195.html</guid><wfw:comment>http://www.cppblog.com/jack-wang/comments/106195.html</wfw:comment><comments>http://www.cppblog.com/jack-wang/archive/2010/01/21/106195.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/jack-wang/comments/commentRss/106195.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/jack-wang/services/trackbacks/106195.html</trackback:ping><description><![CDATA[		<br />
		<font size="2">比如&#160;我们有这样一个C函数<br />#include&lt;stdio.h&gt;<br />long&#160;test(int&#160;a,int&#160;b)<br />{<br />&#160;&#160;&#160;&#160;a&#160;=&#160;a&#160;+&#160;1;<br />&#160;&#160;&#160;&#160;b&#160;=&#160;b&#160;+&#160;100;<br />&#160;&#160;&#160;&#160;return&#160;a&#160;+&#160;b;<br />}<br /><br />写成32位汇编就是这样<br />;//////////////////////////////////////////////////////////////////////////////////////////////////////<br />.386<br />.module&#160;flat,stdcall&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;;这里我们用stdcall&#160;就是函数参数&#160;压栈的时候从最后一个开始压，和被调用函数负责清栈<br />option&#160;casemap:none&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;;区分大小写<br /><br />includelib&#160;msvcrt.lib&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;;这里是引入类库&#160;相当于&#160;#include&lt;stdio.h&gt;了&#160;&#160;&#160;&#160;&#160;&#160;&#160;<br />printf&#160;&#160;PROTO&#160;C:DWORD,:VARARG&#160;&#160;;这个就是声明一下我们要用的函数头，到时候&#160;汇编程序会自动到msvcrt.lib里面找的了&#160;<br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;;:VARARG&#160;表后面的参数不确定&#160;因为C就是这样的printf(const&#160;char&#160;*,&#160;...);<br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;;这样的函数要注意&#160;不是被调用函数负责清栈&#160;因为它本身不知道有多少个参数<br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;;而是有调用者负责清栈&#160;&#160;下面会详细说明<br />.data<br />szTextFmt&#160;&#160;BYTE&#160;'%d',0&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;;这个是用来类型转换的，跟C的一样,字符用字节类型<br />a&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;dword&#160;1000&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;;假设<br />b&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;dword&#160;2000&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;;处理数值都用双字&#160;没有int&#160;跟long&#160;的区别<br /><br />;/////////////////////////////////////////////////////////////////////////////////////////<br />.code<br /><br />_test&#160;proc&#160;A:DWORD,B:DWORD&#160;<br />&#160;&#160;&#160;&#160;&#160;&#160;push&#160;ebp<br />&#160;&#160;&#160;&#160;&#160;&#160;mov&#160;&#160;ebp,esp<br />&#160;&#160;&#160;&#160;&#160;&#160;mov&#160;&#160;eax,dword&#160;ptr&#160;ss:[ebp+8]<br />&#160;&#160;&#160;&#160;&#160;&#160;add&#160;&#160;eax,1<br />&#160;&#160;&#160;&#160;&#160;&#160;mov&#160;&#160;edx,dword&#160;ptr&#160;ss:[ebp+0Ch]<br />&#160;&#160;&#160;&#160;&#160;&#160;add&#160;&#160;edx,100<br />&#160;&#160;&#160;&#160;&#160;&#160;add&#160;&#160;eax,edx<br />&#160;&#160;&#160;&#160;&#160;&#160;pop&#160;&#160;ebp&#160;&#160;&#160;&#160;&#160;&#160;<br />&#160;&#160;&#160;&#160;&#160;&#160;retn&#160;8<br />_test&#160;endp<br /><br />_main&#160;proc&#160;<br />&#160;&#160;&#160;&#160;&#160;&#160;push&#160;dword&#160;ptr&#160;ds:b&#160;&#160;&#160;&#160;&#160;&#160;&#160;;反汇编我们看到的b就不是b了而是一个[*****]数字&#160;dword&#160;ptr&#160;就是我们在ds(数据段)把[*****]<br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;;开始的一个双字长数值取出来<br />&#160;&#160;&#160;&#160;&#160;&#160;push&#160;dword&#160;ptr&#160;ds:a&#160;&#160;&#160;&#160;&#160;&#160;&#160;;跟她对应的还有&#160;byte&#160;ptr&#160;****就是取一个字节出来&#160;比如这样&#160;mov&#160;&#160;al,byte&#160;ptr&#160;ds:szTextFmt&#160;<br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;;就把&#160;%&#160;取出来&#160;而不包括&#160;d<br />&#160;&#160;&#160;&#160;&#160;&#160;call&#160;_test&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;<br />&#160;&#160;&#160;&#160;&#160;&#160;push&#160;eax&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;;假设push&#160;eax的地址是&#215;&#215;&#215;&#215;&#215;<br />&#160;&#160;&#160;&#160;&#160;&#160;push&#160;offset&#160;szTextFmt<br />&#160;&#160;&#160;&#160;&#160;&#160;call&#160;printf<br />&#160;&#160;&#160;&#160;&#160;&#160;add&#160;&#160;esp,8<br />&#160;&#160;&#160;&#160;&#160;&#160;ret&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;<br />_main&#160;endp<br />end&#160;&#160;_main<br /><br />;//////////////////////////////////////////////////////////////&#160;下面介绍堆栈的变化<br />首先要明白的是&#160;操作堆栈段&#160;ss&#160;只能用&#160;esp或ebp寄存器&#160;其他的寄存器eax&#160;ebx&#160;edx等都不能够用&#160;而&#160;esp永远指向堆栈栈顶&#160;ebp用来&#160;在堆栈段<br /><br />里面寻址<br />push&#160;指令是压栈&#160;ESP=ESP-4<br />pop&#160;&#160;指令是出栈&#160;ESP=ESP+4<br />我们假设main函数一开始堆栈定是&#160;ESP=400<br />push&#160;dword&#160;ptr&#160;ds:b&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;;ESP-4=396&#160;-&gt;里面的值就是&#160;2000&#160;就是b的数值<br />push&#160;dword&#160;ptr&#160;ds:a&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;;ESP-4=392&#160;-&gt;里面的值就是&#160;1000&#160;就是a的数值<br />call&#160;test&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;;ESP-4=388－&gt;里面的数值是什么？这个太重要了&#160;就是我们用来找游戏函数的原理所在。<br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;里面的数值就是call&#160;test&#160;指令下一条指令的地址－&gt;即push&#160;eax的地址&#215;&#215;&#215;&#215;&#215;<br /><br />到了test函数里面<br /><br />push&#160;ebp&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;;ESP-4=384-&gt;里面保存了当前ebp的值&#160;而不是把ebp清零<br />mov&#160;&#160;ebp,esp&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;;这里ESP＝384就没变化了，但是&#160;ebp=esp=384,为什么要这样做呢&#160;因为我们要用ebp到堆栈里面找参数<br />mov&#160;&#160;eax,dword&#160;ptr&#160;ss:[ebp+8]&#160;&#160;&#160;&#160;&#160;&#160;;反汇编是这样的&#160;想想为什么a就是[ebp+8]呢<br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;;我们往上看看堆栈里地址392处就保存着a的值&#160;这里ebp=384&#160;加上8正好就是392了<br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;;这样就把传递过来的1000拿了出来eax=1000<br />add&#160;&#160;eax,1&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;;相当于&#160;a+1了&#160;eax=1001<br />mov&#160;&#160;edx,dword&#160;ptr&#160;ss:[ebp+0Ch]&#160;&#160;&#160;&#160;;&#160;0Ch=12&#160;一样道理这里指向堆栈的地址是384+12=396&#160;就是2000了&#160;edx=2000<br />add&#160;&#160;edx,100&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;;相当于&#160;b+100&#160;edx=2100<br />add&#160;&#160;eax,edx&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;;eax=eax+edx＝1001＋2100＝3101&#160;这里eax已经保存了最终的结果了&#160;<br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;;因为win32汇编一般用eax返回结果&#160;所以如果最终结果不是在eax里面的话&#160;还要把它放到eax<br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;;比如假设我的结果保存在变量nRet里面&#160;最后还是要这样&#160;mov&#160;eax,dword&#160;ptr&#160;nRet<br />pop&#160;&#160;ebp&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;;ESP=384+4=388&#160;而保存在栈顶384的值&#160;保存到&#160;ebp中&#160;即恢复ebp原来的值&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;<br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;;因为一开始我们就把ebp的值压栈了，mov&#160;ebp,esp已经改变了ebp的值，这里恢复就是保证了堆栈平衡<br />retn&#160;&#160;8&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;;ESP+8-&gt;396&#160;这里retn是由系统调用的&#160;我们不用管&#160;系统会自动把EIP指针指向&#160;原来的call的下一条指令<br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;;由于是系统自动恢复了call那里的压栈所以&#160;真正返回到的时候ESP+4就是恢复了call压栈的堆栈<br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;;到了这个时候&#160;ESP=400&#160;就是函数调用开始的堆栈，就是说函数调用前跟函数调用后的堆栈是一样的<br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;;这就是堆栈平衡&#160;<br />由于我们用stdcall上面retn&#160;8就是被调用者负责恢复堆栈的意思了，函数test是被调用者，所以负责把堆栈加8,call&#160;那里是系统自动恢复的<br /><br />push&#160;eax&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;;ESP-4=396-&gt;里面保存了eax的值3101<br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;;上面已经看到了eax保存着返回值，我们要把它传给printf也是通过堆栈传递&#160;&#160;&#160;&#160;&#160;&#160;&#160;<br />push&#160;offset&#160;szTextFmt&#160;&#160;&#160;;ESP-4=392-&gt;里面保存了szTextFmt的地址&#160;也就是C里面的指针&#160;实际上没有什么把字符串传递的，我们传的都是地址<br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;;无论是在汇编或C&#160;所以在汇编里没有什么字符串类型&#160;用最多的就是DWORD。嘿嘿游戏里面传递参数&#160;简单多了<br />call&#160;printf&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;;ESP-4=388-&gt;里面保存了下一条指令的地址<br />add&#160;&#160;esp,8&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;;ESP+8=400&#160;恢复了调用printf前的堆栈状态<br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;;上面说了由于printf后面参数是:VARARG&#160;这样的类型是有调用者恢复堆栈的&#160;所以printf里面没有retn&#160;8之类的指令<br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;;这是由调用者负责清栈&#160;main是调用者&#160;所以下面一句就是&#160;add&#160;esp,8&#160;把堆栈恢复到调用printf之前<br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;;而call&#160;printf那里的压栈&#160;是由系统做的&#160;恢复的工作也是系统完成&#160;我们不用理&#160;只是知道里面保存是返回地址就够&#160;&#160;<br /><br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;;了<br />ret&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;;main&#160;函数返回&#160;其他的事情是系统自动搞定&#160;我们不用理&#160;任务完成<br /></font>
 <img src ="http://www.cppblog.com/jack-wang/aggbug/106195.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/jack-wang/" target="_blank">小王</a> 2010-01-21 23:43 <a href="http://www.cppblog.com/jack-wang/archive/2010/01/21/106195.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>最快速度找到内存泄漏</title><link>http://www.cppblog.com/jack-wang/archive/2010/01/21/106191.html</link><dc:creator>小王</dc:creator><author>小王</author><pubDate>Thu, 21 Jan 2010 15:14:00 GMT</pubDate><guid>http://www.cppblog.com/jack-wang/archive/2010/01/21/106191.html</guid><wfw:comment>http://www.cppblog.com/jack-wang/comments/106191.html</wfw:comment><comments>http://www.cppblog.com/jack-wang/archive/2010/01/21/106191.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/jack-wang/comments/commentRss/106191.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/jack-wang/services/trackbacks/106191.html</trackback:ping><description><![CDATA[
		<p>
				<font size="2">原帖：<a href="http://blog.csdn.net/i_like_cpp/archive/2007/06/28/1669962.aspx">http://blog.csdn.net/i_like_cpp/archive/2007/06/28/1669962.aspx</a><br />作者：许式伟 2006年11月某日</font>
		</p>
		<p>
				<font size="2">内存管理是C++程序员的痛。我的《内存管理变革》系列就是试图讨论更为有效的内存管理方式，以杜绝（或减少）内存泄漏，减轻C++程序员的负担。由于工作忙的缘故，这个系列目前未完，暂停。<br /> <br />这篇短文我想换个方式，讨论一下如何以最快的速度找到内存泄漏。<br /> <br />确认是否存在内存泄漏<br />我们知道，MFC程序如果检测到存在内存泄漏，退出程序的时候会在调试窗口提醒内存泄漏。例如：</font>
		</p>
		<p>
				<font size="2">class CMyApp : public CWinApp<br />{<br />public:<br />   BOOL InitApplication()<br />   {<br />       int* leak = new int[10];<br />       return TRUE;<br />   }<br />};<br />产生的内存泄漏报告大体如下：</font>
		</p>
		<p>
				<font size="2">Detected memory leaks!<br />Dumping objects -&gt;<br />c:\work\test.cpp(186) : {52} normal block at 0x003C4410, 40 bytes long.<br /> Data: &lt;                &gt; CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD <br />Object dump complete.<br />这挺好。问题是，如果我们不喜欢MFC，那么难道就没有办法？或者自己做？ </font>
		</p>
		<p>
				<font size="2">呵呵，这不需要。其实，MFC也没有自己做。内存泄漏检测的工作是VC++的C运行库做的。也就是说，只要你是VC++程序员，都可以很方便地检测内存泄漏。我们还是给个样例：</font>
		</p>
		<p>
				<font size="2">#include &lt;crtdbg.h&gt;</font>
		</p>
		<p>
				<font size="2">inline void EnableMemLeakCheck()<br />{<br />   _CrtSetDbgFlag(_CrtSetDbgFlag(_CRTDBG_REPORT_FLAG) | _CRTDBG_LEAK_CHECK_DF);<br />}</font>
		</p>
		<p>
				<font size="2">void main()<br />{<br />   EnableMemLeakCheck();<br />   int* leak = new int[10];<br />}<br /> 运行（提醒：不要按Ctrl+F5，按F5），你将发现，产生的内存泄漏报告与MFC类似，但有细节不同，如下：</font>
		</p>
		<p>
				<font size="2">Detected memory leaks!<br />Dumping objects -&gt;<br />{52} normal block at 0x003C4410, 40 bytes long.<br /> Data: &lt;                &gt; CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD <br />Object dump complete.<br />为什么呢？看下面。</font>
		</p>
		<p>
				<font size="2">定位内存泄漏由于哪一句话引起的<br />你已经发现程序存在内存泄漏。现在的问题是，我们要找泄漏的根源。</font>
		</p>
		<p>
				<font size="2">一般我们首先确定内存泄漏是由于哪一句引起。在MFC中，这一点很容易。你双击内存泄漏报告的文字，或者在Debug窗口中按F4，IDE就帮你定位到申请该内存块的地方。对于上例，也就是这一句：</font>
		</p>
		<p>
				<font size="2">   int* leak = new int[10];</font>
		</p>
		<p>
				<font size="2">这多多少少对你分析内存泄漏有点帮助。特别地，如果这个new仅对应一条delete（或者你把delete漏写），这将很快可以确认问题的症结。 </font>
		</p>
		<p>
				<font size="2">我们前面已经看到，不使用MFC的时候，生成的内存泄漏报告与MFC不同，而且你立刻发现按F4不灵。那么难道MFC做了什么手脚？ </font>
		</p>
		<p>
				<font size="2">其实不是，我们来模拟下MFC做的事情。看下例： </font>
		</p>
		<p>
				<font size="2">inline void EnableMemLeakCheck()<br />{<br />   _CrtSetDbgFlag(_CrtSetDbgFlag(_CRTDBG_REPORT_FLAG) | _CRTDBG_LEAK_CHECK_DF);<br />}</font>
		</p>
		<p>
				<font size="2">#ifdef _DEBUG<br />#define new   new(_NORMAL_BLOCK, __FILE__, __LINE__)<br />#endif</font>
		</p>
		<p>
				<font size="2">void main()<br />{<br />   EnableMemLeakCheck();<br />   int* leak = new int[10];<br />}</font>
		</p>
		<p>
				<font size="2">再运行这个样例，你惊喜地发现，现在内存泄漏报告和MFC没有任何分别了。</font>
		</p>
		<p>
				<font size="2">快速找到内存泄漏<br />单确定了内存泄漏发生在哪一行，有时候并不足够。特别是同一个new对应有多处释放的情形。在实际的工程中，以下两种情况很典型： </font>
		</p>
		<p>
				<font size="2">创建对象的地方是一个类工厂（ClassFactory）模式。很多甚至全部类实例由同一个new创建。对于此，定位到了new出对象的所在行基本没有多大帮助。 <br />  <br />COM对象。我们知道COM对象采用Reference Count维护生命周期。也就是说，对象new的地方只有一个，但是Release的地方很多，你要一个个排除。 <br />那么，有什么好办法，可以迅速定位内存泄漏？</font>
		</p>
		<p>
				<font size="2">答：有。</font>
		</p>
		<p>
				<font size="2">在内存泄漏情况复杂的时候，你可以用以下方法定位内存泄漏。这是我个人认为通用的内存泄漏追踪方法中最有效的手段。</font>
		</p>
		<p>
				<font size="2">我们再回头看看crtdbg生成的内存泄漏报告： </font>
		</p>
		<p>
				<font size="2">Detected memory leaks!<br />Dumping objects -&gt;<br />c:\work\test.cpp(186) : {52} normal block at 0x003C4410, 40 bytes long.<br /> Data: &lt;                &gt; CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD <br />Object dump complete. <br />除了产生该内存泄漏的内存分配语句所在的文件名、行号为，我们注意到有一个比较陌生的信息：{52}。这个整数值代表了什么意思呢？</font>
		</p>
		<p>
				<font size="2">其实，它代表了第几次内存分配操作。象这个例子，{52}代表了第52次内存分配操作发生了泄漏。你可能要说，我只new过一次，怎么会是第52次？这很容易理解，其他的内存申请操作在C的初始化过程调用的呗。:)</font>
		</p>
		<p>
				<font size="2">有没有可能，我们让程序运行到第52次内存分配操作的时候，自动停下来，进入调试状态？所幸，crtdbg确实提供了这样的函数：即 long _CrtSetBreakAlloc(long nAllocID)。我们加上它：</font>
		</p>
		<p>
				<font size="2">inline void EnableMemLeakCheck()<br />{<br />   _CrtSetDbgFlag(_CrtSetDbgFlag(_CRTDBG_REPORT_FLAG) | _CRTDBG_LEAK_CHECK_DF);<br />}</font>
		</p>
		<p>
				<font size="2">#ifdef _DEBUG<br />#define new   new(_NORMAL_BLOCK, __FILE__, __LINE__)<br />#endif</font>
		</p>
		<p>
				<font size="2">void main()<br />{<br />   EnableMemLeakCheck();<br />   _CrtSetBreakAlloc(52);<br />   int* leak = new int[10];<br />}<br />你发现，程序运行到 int* leak = new int[10]; 一句时，自动停下来进入调试状态。细细体会一下，你可以发现，这种方式你获得的信息远比在程序退出时获得文件名及行号有价值得多。因为报告泄漏文件名及行号，你获得的只是静态的信息，然而_CrtSetBreakAlloc则是把整个现场恢复，你可以通过对函数调用栈分析（我发现很多人不习惯看函数调用栈，如果你属于这种情况，我强烈推荐你去补上这一课，因为它太重要了）以及其他在线调试技巧，来分析产生内存泄漏的原因。通常情况下，这种分析方法可以在5分钟内找到肇事者。</font>
		</p>
		<p>
				<font size="2">当然，_CrtSetBreakAlloc要求你的程序执行过程是可还原的（多次执行过程的内存分配顺序不会发生变化）。这个假设在多数情况下成立。不过，在多线程的情况下，这一点有时难以保证。</font>
		</p>
		<p>
				<font size="2">
				</font> </p>
		<p>
				<br />
				<font size="2">附加说明：<br />对“内存管理”相关的技术感兴趣？这里可以看到我的所有关于内存管理的文章。</font>
		</p>
		<p>
				<font size="2">
				</font> </p>
		<p>
				<font size="2">本文来自CSDN博客，转载请标明出处：</font>
				<a href="http://blog.csdn.net/i_like_cpp/archive/2007/06/28/1669962.aspx">
						<font size="2">http://blog.csdn.net/i_like_cpp/archive/2007/06/28/1669962.aspx</font>
				</a>
		</p>
<img src ="http://www.cppblog.com/jack-wang/aggbug/106191.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/jack-wang/" target="_blank">小王</a> 2010-01-21 23:14 <a href="http://www.cppblog.com/jack-wang/archive/2010/01/21/106191.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>