<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>西瓜田 &#187; C/C++</title>
	<atom:link href="http://blog.thpiano.com/?feed=rss2&#038;tag=c" rel="self" type="application/rss+xml" />
	<link>http://blog.thpiano.com</link>
	<description>无复洛城东</description>
	<lastBuildDate>Tue, 19 Jan 2021 03:54:37 +0000</lastBuildDate>
	<language>zh-CN</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.4.2</generator>
		<item>
		<title>为什么负数取余操作（%）在c和python下结果不同</title>
		<link>http://blog.thpiano.com/?p=1023</link>
		<comments>http://blog.thpiano.com/?p=1023#comments</comments>
		<pubDate>Sun, 19 Jul 2015 15:32:55 +0000</pubDate>
		<dc:creator>suika</dc:creator>
				<category><![CDATA[编程]]></category>
		<category><![CDATA[C/C++]]></category>
		<category><![CDATA[div]]></category>
		<category><![CDATA[mod]]></category>
		<category><![CDATA[python]]></category>
		<category><![CDATA[不同]]></category>
		<category><![CDATA[余数]]></category>
		<category><![CDATA[取余]]></category>
		<category><![CDATA[取模]]></category>

		<guid isPermaLink="false">http://blog.thpiano.com/?p=1023</guid>
		<description><![CDATA[最近在用python，偶尔有一次涉及到了负数取余操作： -3 % 4 按照之前写c/c++、java的习惯，这里肯定是等于-3。但是python下返回的结果竟然是1！ 为什么会有不同的表现呢？先从取余操作本身说起： &#160; 关于取余(modulo operation) 取余的定义 由于不同的架构有不同的数字表示法和运算法，因此他们对于取余的定义也有可能略有不同。 一般来说，公认的取余操作（被除数a、除数n、商q以及余数r，n % a = r）需要满足以下三条： 然而这个定义是非常宽泛的。以 -3 % 4为例： -3既可以表示为：-3 = 4 * -1 + 1 也可以表示为：-3 = 4 * 0 + (-3) 这两种表示法分别对应了两种实现方法：Truncated Division及 Floored Division。两种实现方法的求商方式不同，导致其最终结果也不同。 Truncated Division 在Truncated Division下，求商时，采用的是q = trunc(a/n) ，即将算数除法结果的小数部分移除，余下的整数部分作为商。 （也有一种更形象的说法，除法取整时，是朝着靠近0的方向取整） 以-3 % 4为例： q = trunc(-3/4) = trunc(-0.75) = 0 因此，余数r = [...]]]></description>
			<content:encoded><![CDATA[<p>最近在用python，偶尔有一次涉及到了负数取余操作： -3 % 4</p>
<p>按照之前写c/c++、java的习惯，这里肯定是等于<span style="color: #0000ff;"><strong>-3</strong></span>。但是python下返回的结果竟然是<span style="color: #0000ff;"><strong>1</strong></span>！</p>
<p>为什么会有不同的表现呢？先从取余操作本身说起：</p>
<p>&nbsp;</p>
<h1>关于取余(modulo operation)</h1>
<h4>取余的定义</h4>
<p>由于不同的架构有不同的数字表示法和运算法，因此他们对于取余的定义也有可能略有不同。</p>
<p>一般来说，公认的取余操作（被除数<em>a</em>、除数<em>n</em>、商<em>q</em>以及余数<em>r</em>，<em>n</em> % <em>a</em> = <em>r</em>）需要满足以下三条：</p>
<table>
<tbody>
<tr>
<td><img src="https://upload.wikimedia.org/math/2/1/9/21923d0bed7d824da52419c946093e48.png" alt="\begin{align}&lt;br /&gt;&lt;br /&gt; q \,&amp;\in \mathbb{Z} \\&lt;br /&gt;&lt;br /&gt; a \,&amp;= n q + r \\&lt;br /&gt;&lt;br /&gt; |r| \,&amp;&lt; |n|.&lt;br /&gt;&lt;br /&gt; \end{align}" /></td>
</tr>
</tbody>
</table>
<p>然而这个定义是非常宽泛的。以 -3 % 4为例：</p>
<p>-3既可以表示为：-3 = 4 * -1 + 1</p>
<p>也可以表示为：-3 = 4 * 0 + (-3)</p>
<p>这两种表示法分别对应了两种实现方法：Truncated Division及 Floored Division。两种实现方法的求商方式不同，导致其最终结果也不同。<span id="more-1023"></span></p>
<h4>Truncated Division</h4>
<p>在Truncated Division下，求商时，采用的是<em>q</em> = trunc(<em>a</em>/<em>n</em>) ，即将算数除法结果的小数部分移除，余下的整数部分作为商。</p>
<p>（也有一种更形象的说法，除法取整时，是朝着靠近0的方向取整）</p>
<p>以-3 % 4为例：</p>
<p><em>q</em> = trunc(<em>-3</em>/<em>4</em>) = trunc(-0.75) = 0</p>
<p>因此，余数r = a - nq = -3 - (0 * 4) = -3</p>
<p>此实现有如下特点：</p>
<p>1. 余数的符号与<strong><span style="color: #0000ff;">被除数</span></strong>相同</p>
<p>2. 这种实现在某些地方被称之为<strong><span style="color: #0000ff;">取模</span></strong></p>
<p>&nbsp;</p>
<h4>Floored Division</h4>
<p>在Floored Division下，求商时，采用的是<em>q</em> = ⌊<em>a</em>/<em>n</em>⌋，即将算数除法结果进行向下取整作为商。</p>
<p>以-3 % 4为例：</p>
<p><em>q</em> = ⌊<em>-3</em>/<em>4</em>⌋ = ⌊<em>-0.75</em>⌋ = -1</p>
<p>因此，余数r = a - nq = -3 - (-1 * 4) = 1</p>
<p>此实现有如下特点：</p>
<p>1. 余数的符号与<strong><span style="color: #0000ff;">除数</span></strong>相同</p>
<p>2. 这种实现在某些地方被称之为<strong><span style="color: #0000ff;">求余</span></strong></p>
<p>&nbsp;</p>
<h1>关于语言标准</h1>
<p>由上可知，存在着多种实现方式，那么具体地，c和python是如何选取的呢？</p>
<h4>C语言标准中关于取余运算的说明</h4>
<p>C89中，并未做严格规定：</p>
<blockquote style="margin-bottom: 10px; padding: 10px; background-color: #fff9e3; border-left: 2px solid #ffeb8e;"><p>When integers are divided and the division is inexact, if both operands are positive the result of the/ operator is the largest integer less than the algebraic quotient and the result of the % operator is positive. <strong>If either operand is negative</strong>, whether the result of the / operator is the largest integer less than the algebraic quotient or the smallest integer greater than the algebraic quotient is <strong>implementation-defined</strong>, as is the sign of the result of the % operator. If the quotient a/b is representable, the expression (a/b)*b + a%b shall equal a.</p></blockquote>
<p>而C99，明确指定了需要使用Truncated Version：</p>
<blockquote style="margin-bottom: 10px; padding: 10px; background-color: #fff9e3; border-left: 2px solid #ffeb8e;"><p>When integers are divided, the result of the / operator is the algebraic quotient with any fractional part discarded.<sup>88)</sup> If the quotient a/b is representable, the expression (a/b)*b + a%b shall equal a.</p>
<p><sub>88) This is often called "truncation toward zero".</sub></p></blockquote>
<h4>Python标准中关于取余运算的说明</h4>
<p>Python 2：从说明中关于余数符号的部分，可以推断出其指定了Floored Version</p>
<blockquote style="margin-bottom: 10px; padding: 10px; background-color: #fff9e3; border-left: 2px solid #ffeb8e;">
<p id="index-55">The <tt>%</tt> (modulo) operator yields the remainder from the division of the first argument by the second. The numeric arguments are first converted to a common type. A zero right argument raises the <a title="exceptions.ZeroDivisionError" href="https://docs.python.org/2/library/exceptions.html#exceptions.ZeroDivisionError"><tt>ZeroDivisionError</tt></a> exception. The arguments may be floating point numbers, e.g., <tt>3.14%0.7</tt> equals <tt>0.34</tt> (since <tt>3.14</tt> equals <tt>4*0.7 + 0.34</tt>.) The modulo operator always yields a result with the same sign as its second operand (or zero); the absolute value of the result is strictly smaller than the absolute value of the second operand <a id="id10" href="https://docs.python.org/2/reference/expressions.html#id21">[2]</a>.</p>
<p>The integer division and modulo operators are connected by the following identity: <tt>x == (x/y)*y + (x%y)</tt>. Integer division and modulo are also connected with the built-in function <a title="divmod" href="https://docs.python.org/2/library/functions.html#divmod"><tt>divmod()</tt></a>: <tt>divmod(x, y) == (x/y,x%y)</tt>. These identities don’t hold for floating point numbers; there similar identities hold approximately where <tt>x/y</tt> is replaced by <tt>floor(x/y)</tt> or <tt>floor(x/y) - 1</tt> <a id="id11" href="https://docs.python.org/2/reference/expressions.html#id22">[3]</a>.</p></blockquote>
<p>Python 3：和2一样，可推断出指定Floored Version</p>
<blockquote style="margin-bottom: 10px; padding: 10px; background-color: #fff9e3; border-left: 2px solid #ffeb8e;">
<p id="index-51">The <tt>%</tt> (modulo) operator yields the remainder from the division of the first argument by the second. The numeric arguments are first converted to a common type. A zero right argument raises the <a title="ZeroDivisionError" href="https://docs.python.org/3/library/exceptions.html#ZeroDivisionError"><tt>ZeroDivisionError</tt></a> exception. The arguments may be floating point numbers, e.g., <tt>3.14%0.7</tt> equals <tt>0.34</tt> (since <tt>3.14</tt> equals <tt>4*0.7 + 0.34</tt>.) The modulo operator always yields a result with the same sign as its second operand (or zero); the absolute value of the result is strictly smaller than the absolute value of the second operand <a id="id8" href="https://docs.python.org/3/reference/expressions.html#id16">[1]</a>.</p>
<p>The floor division and modulo operators are connected by the following identity: <tt>x == (x//y)*y + (x%y)</tt>. Floor division and modulo are also connected with the built-in function <a title="divmod" href="https://docs.python.org/3/library/functions.html#divmod"><tt>divmod()</tt></a>: <tt>divmod(x, y) == (x//y, x%y)</tt>. <a id="id9" href="https://docs.python.org/3/reference/expressions.html#id17">[2]</a>.</p></blockquote>
<h1>结论</h1>
<div> c/c++标准中指定了采用Truncated Version进行实现，而Python标准中指定了采用Floored Version进行实现，因此造成了结果上的不同。</div>
<p>&nbsp;</p>
<h1>一些推断及其他</h1>
<p>最后，来小小地猜测一下，为什么c语言和python会选取不同的实现方案。</p>
<h4>对于c：</h4>
<p>在X86架构下，对于整数的除法和取余操作，都是通过同一个指令进行的（有符号除法使用idiv，而无符号为div），商和余数会同时作为结果返回在寄存器AX和DX中。</p>
<p>这个特性被c的库函数divmod所利用，导致编译器有可能会将除法及取余操作都优化为使用一个指令完成。</p>
<p>这样一来，通过如下三条：</p>
<p>1. c的除法正好为trunc除（即Truncated Version中取商的方式）</p>
<p>2. 通常程序里除法出现的频率都远远高过取余的频率，取余可能会被优化为一个指令</p>
<p>3. c语言设计风格为性能和简洁优先，而c++又要与c兼容</p>
<p>因此c语言采用了Truncated Version。</p>
<p>而这也为一个常用的判断埋下了坑：</p>
<p>如果你想判断一个数是否是奇数，</p>
<div class="codecolorer-container c geshi" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br /></div></td><td><div class="c codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">bool isEven<span style="color: #009900;">&#40;</span><span style="color: #993333;">int</span> x<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#123;</span> <span style="color: #b1b100;">return</span> x <span style="color: #339933;">%</span> <span style="color: #0000dd;">2</span> <span style="color: #339933;">=</span> <span style="color: #0000dd;">1</span><span style="color: #009900;">&#125;</span></div></td></tr></tbody></table></div>
<p>会埋下大坑（对负数存在bug）</p>
<p>而需要写成</p>
<div class="codecolorer-container c geshi" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br /></div></td><td><div class="c codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">bool isEven<span style="color: #009900;">&#40;</span><span style="color: #993333;">int</span> x<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#123;</span> <span style="color: #b1b100;">return</span> x <span style="color: #339933;">%</span> <span style="color: #0000dd;">2</span> <span style="color: #339933;">!=</span> <span style="color: #0000dd;">0</span><span style="color: #009900;">&#125;</span></div></td></tr></tbody></table></div>
<p>&nbsp;</p>
<h4>对于python：</h4>
<p>不会像c那样将性能放到第一位，会比c有更多的语法糖，更贴近常用思维，因此使用Floored Version会更加适合。</p>
<p>&nbsp;</p>
<h1>参考文献</h1>
<p>https://en.wikipedia.org/wiki/Modulo_operation#Remainder_calculation_for_the_modulo_operation</p>
<p>https://docs.python.org/2/reference/expressions.html#arithmetic-conversions</p>
<p>http://stackoverflow.com/questions/3609572/does-either-ansi-c-or-iso-c-specify-what-5-10-should-be</p>
<p>http://stackoverflow.com/questions/11630321/why-does-c-output-negative-numbers-when-using-modulo</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.thpiano.com/?feed=rss2&#038;p=1023</wfw:commentRss>
		<slash:comments>7</slash:comments>
		</item>
		<item>
		<title>CuTime</title>
		<link>http://blog.thpiano.com/?p=456</link>
		<comments>http://blog.thpiano.com/?p=456#comments</comments>
		<pubDate>Mon, 13 Feb 2012 02:57:15 +0000</pubDate>
		<dc:creator>suika</dc:creator>
				<category><![CDATA[编程]]></category>
		<category><![CDATA[C/C++]]></category>
		<category><![CDATA[qt]]></category>
		<category><![CDATA[windows小应用]]></category>

		<guid isPermaLink="false">http://blog.thpiano.com/?p=456</guid>
		<description><![CDATA[寒假没几天了@_@ 想来各项事宜进展不顺利，归根结底是时间利用率不高 盯着bluehost日志的我就在想，干脆做个小软件来统计每天花在各个程序上的时间好了 于是就做出了这么一个玩意…… 其原理是，利用GetForegroundWindow()来获取正在使用的窗口句柄，然后根据句柄找进程所对应的exe文件名 用GetLastInputInfo()来判断闲置 x掉以后会缩在墙角的任务栏里偷偷写日志记录 顺便也做了日志的导入功能，考虑到明文可看性，没有用二进制来写。不过目前只能按天来导入嗯（日志文件是按天数分的，只做了单文件的） 看着诸如devenv之类的记录名也感觉哭笑不得，不过懒得再加自定义映射模块了，反正自己用也没大碍 &#160; 当然只有如我这样极端缺乏记忆力和行动力的人才会想到做这种东西吧╮(╯_╰)╭ &#160; 界面用的是qt，以前做界面一直很苦手，用gdi画图，代码又啰嗦还丑；又不想重复发明轮子，于是借此机会正好熟手一个界面库 大家都说mfc又复杂又难学，正好我mfc零基础，直接就扔掉了；剩下的guichan，gtk+，qt，前者名气不够大，gtk+又是c为主，就选了qt。再说qt名字读起来好萌，cute啊 但是做这么一个小程序完全没法掌握qt的精髓，也没有可供对比的经验，所以也就无从而谈使用体会了。顺带说下，qt的dll体积非常大（vc编译好的两个主要dll加起来要超过10M，我的exe才70k），可能是因为其支持跨平台的特性导致的。而我这个程序从设计上就是windows only= = 有触手说做windows程序干脆用c#得了，但是我觉得自己开坑的语言实在太多了= = 再说c#大概有两年没碰过了 &#160; 这次的总结有三： 1.设计还没明确就开工了，导致很多功能都是测试时才确定下来的。但是测试只能看到短期需求，具体使用是长期需求口牙 2.选了个不怎么搭配的库，浪费了qt的跨平台特性 3.代码还是一团乱啊一团乱，模块没划分好+写出了长函数。就算是小程序也要认真对待啊]]></description>
			<content:encoded><![CDATA[<p>寒假没几天了@_@ 想来各项事宜进展不顺利，归根结底是时间利用率不高<br />
盯着bluehost日志的我就在想，干脆做个小软件来统计每天花在各个程序上的时间好了<span id="more-456"></span></p>
<p><a href="http://blog.thpiano.com/wp-content/uploads/2012/02/cutime.jpg"><img class="alignnone size-full wp-image-457" title="cutime" src="http://blog.thpiano.com/wp-content/uploads/2012/02/cutime.jpg" alt="" width="488" height="290" /></a></p>
<p>于是就做出了这么一个玩意……</p>
<p>其原理是，利用GetForegroundWindow()来获取正在使用的窗口句柄，然后根据句柄找进程所对应的exe文件名</p>
<p>用GetLastInputInfo()来判断闲置</p>
<p>x掉以后会缩在墙角的任务栏里偷偷写日志记录</p>
<p>顺便也做了日志的导入功能，考虑到明文可看性，没有用二进制来写。不过目前只能按天来导入嗯（日志文件是按天数分的，只做了单文件的）</p>
<p>看着诸如devenv之类的记录名也感觉哭笑不得，不过懒得再加自定义映射模块了，反正自己用也没大碍</p>
<p>&nbsp;</p>
<p>当然只有如我这样极端缺乏记忆力和行动力的人才会想到做这种东西吧╮(╯_╰)╭</p>
<p>&nbsp;</p>
<p>界面用的是qt，以前做界面一直很苦手，用gdi画图，代码又啰嗦还丑；又不想重复发明轮子，于是借此机会正好熟手一个界面库</p>
<p>大家都说mfc又复杂又难学，正好我mfc零基础，直接就扔掉了；剩下的guichan，gtk+，qt，前者名气不够大，gtk+又是c为主，就选了qt。再说qt名字读起来好萌，cute啊</p>
<p>但是做这么一个小程序完全没法掌握qt的精髓，也没有可供对比的经验，所以也就无从而谈使用体会了。顺带说下，qt的dll体积非常大（vc编译好的两个主要dll加起来要超过10M，我的exe才70k），可能是因为其支持跨平台的特性导致的。而我这个程序从设计上就是windows only= =</p>
<p>有触手说做windows程序干脆用c#得了，但是我觉得自己开坑的语言实在太多了= = 再说c#大概有两年没碰过了</p>
<p>&nbsp;</p>
<p>这次的总结有三：</p>
<p>1.设计还没明确就开工了，导致很多功能都是测试时才确定下来的。但是测试只能看到短期需求，具体使用是长期需求口牙</p>
<p>2.选了个不怎么搭配的库，浪费了qt的跨平台特性</p>
<p>3.代码还是一团乱啊一团乱，模块没划分好+写出了长函数。就算是小程序也要认真对待啊</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.thpiano.com/?feed=rss2&#038;p=456</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>STL之容器初探</title>
		<link>http://blog.thpiano.com/?p=352</link>
		<comments>http://blog.thpiano.com/?p=352#comments</comments>
		<pubDate>Thu, 08 Dec 2011 16:23:23 +0000</pubDate>
		<dc:creator>suika</dc:creator>
				<category><![CDATA[编程]]></category>
		<category><![CDATA[C/C++]]></category>
		<category><![CDATA[STL]]></category>

		<guid isPermaLink="false">http://172.18.126.172/blog/?p=352</guid>
		<description><![CDATA[说是stl的容器，其实这里主要就是比较vector、list和deque了。都能动态增加容量、以拷贝的方式存储对象的三者，各自的擅长领域是什么呢？ 先试着对三位进行介绍，再进行基于超级不正规测试的能力大比拼！ 说来STL想学好，还是必须要看书呀。学期初借的那本《泛型编程与STL》没看完，《STL源码剖析》没得借又觉得可能会有点难，真是微妙的心情 &#160; 介绍先行： 1.vector 擅长随机访问、尾部插入/删除操作；不擅长尾部以外位置的插入/删除操作；有点畏惧海量数据 &#160; 实现原理：类似原生数组一样的连续空间用来存放数据，所以具备原生数组的特点：随机访问，在插入/删除操作时会涉及大规模的数据移动 当容量不够时，会重新分配一个较大的容量（比如说大小为之前容量*2），然后进行内存拷贝、释放旧容量。因此在面对海量数据时，不仅要关注是否有如此大的连续空间，还要考虑在数据增长时重分配空间带来的额外大量拷贝、释放操作。有reserve()方法预分配一定大小的内存，可缓解一些额外操作。 &#160; 2.list 基本不支持随机访问；擅长对各个位置的插入/删除操作；遍历时取址操作多；数据量不管怎么增长，效率都不会有太大起伏 &#160; 实现原理：本质是双向链表，所以对各个位置的插入/删除操作游刃有余，也不用担心连续空间的分配问题；但是遍历时有额外取址操作，存储时每个对象也要额外存两个指针 &#160; 3.deque(double-ended queue) 支持随机访问；擅长头部和尾部的插入/删除操作； &#160; 实现原理：可视为是vector与list的中间态，是STL里stack和queue的默认容器。在STL中一般的实现方式是：以一小段连续内存作为整体单位的双向链表，即用一小段连续内存代替了链表中的节点。当遇到连续增长的数据时，也可以用小段连续内存为单位进行分配，比起vector来说平缓温和得多，限制也少。 &#160; &#160; 能力大比拼： ("&#62;"表示优于，测试平台为vs2008 release模式，电脑配置为cpu T4300，内存2G) 1.内存管理 测试方式：插入1000个对象，查看构造函数调用次数： 结果数据：vector 2024  &#124;   list 1001  &#124;  deque 1008 测试结果：list&#62;deque&#62;&#62;vector &#160; 2.随机访问速度 测试方式：插入1000个int，分别用[]和::at()随机访问一亿次的毫秒数（使用::at()会额外检查下标越界、异常处理） 结果数据（每次都使用rand()）：vector[]  2750  &#124;  vector::at() 3438  &#124;  deque[] 3390  &#124;  deque::at() 3547  ; 结果数据（预先生成随机数表）：vector[] [...]]]></description>
			<content:encoded><![CDATA[<p>说是stl的容器，其实这里主要就是比较vector、list和deque了。都能动态增加容量、以拷贝的方式存储对象的三者，各自的擅长领域是什么呢？</p>
<p>先试着对三位进行介绍，再进行基于超级不正规测试的能力大比拼！</p>
<p><span id="more-352"></span></p>
<p>说来STL想学好，还是必须要看书呀。学期初借的那本《泛型编程与STL》没看完，《STL源码剖析》没得借又觉得可能会有点难，真是微妙的心情</p>
<p>&nbsp;</p>
<p><span style="color: #0000ff;">介绍先行</span>：</p>
<p>1.vector</p>
<p>擅长随机访问、尾部插入/删除操作；不擅长尾部以外位置的插入/删除操作；有点畏惧海量数据</p>
<p>&nbsp;</p>
<p>实现原理：类似原生数组一样的连续空间用来存放数据，所以具备原生数组的特点：随机访问，在插入/删除操作时会涉及大规模的数据移动</p>
<p>当容量不够时，会重新分配一个较大的容量（比如说大小为之前容量*2），然后进行内存拷贝、释放旧容量。因此在面对海量数据时，不仅要关注是否有如此大的连续空间，还要考虑在数据增长时重分配空间带来的额外大量拷贝、释放操作。有reserve()方法预分配一定大小的内存，可缓解一些额外操作。</p>
<p>&nbsp;</p>
<p>2.list</p>
<p>基本不支持随机访问；擅长对各个位置的插入/删除操作；遍历时取址操作多；数据量不管怎么增长，效率都不会有太大起伏</p>
<p>&nbsp;</p>
<p>实现原理：本质是双向链表，所以对各个位置的插入/删除操作游刃有余，也不用担心连续空间的分配问题；但是遍历时有额外取址操作，存储时每个对象也要额外存两个指针</p>
<p>&nbsp;</p>
<p>3.deque(double-ended queue)</p>
<p>支持随机访问；擅长头部和尾部的插入/删除操作；</p>
<p>&nbsp;</p>
<p>实现原理：可视为是vector与list的中间态，是STL里stack和queue的默认容器。在STL中一般的实现方式是：以一小段连续内存作为整体单位的双向链表，即用一小段连续内存代替了链表中的节点。当遇到连续增长的数据时，也可以用小段连续内存为单位进行分配，比起vector来说平缓温和得多，限制也少。</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p><span style="color: #0000ff;">能力大比拼</span>：</p>
<p>("&gt;"表示优于，测试平台为vs2008 release模式，电脑配置为cpu T4300，内存2G)</p>
<p>1.内存管理</p>
<p>测试方式：插入1000个对象，查看构造函数调用次数：</p>
<p>结果数据：vector 2024  |   list 1001  |  deque 1008</p>
<p>测试结果：list&gt;deque&gt;&gt;vector</p>
<p>&nbsp;</p>
<p>2.随机访问速度</p>
<p>测试方式：插入1000个int，分别用[]和::at()随机访问一亿次的毫秒数（使用::at()会额外检查下标越界、异常处理）</p>
<p>结果数据（每次都使用rand()）：vector[]  2750  |  vector::at() 3438  |  deque[] 3390  |  deque::at() 3547  ;</p>
<p>结果数据（预先生成随机数表）：vector[] 141   |   vector::at() 1062  |  deque[]1047   |  deque::at() 1047 ;</p>
<p>测试结果：vector&gt;deque&gt;&gt;list</p>
<p>&nbsp;</p>
<p>3.头部插入速度</p>
<p>测试方式：push_front 一百万个int的毫秒数</p>
<p>结果数据：list 1594  |  deque 438</p>
<p>测试结果：deque&gt;list&gt;&gt;vector</p>
<p>&nbsp;</p>
<p>4.中间插入/删除速度</p>
<p>测试方式：在已有1000个int数据的情况下，从第500个位置开始插入十万个int、再删除十万个int的毫秒数</p>
<p>结果数据：list插入 157   |  deque插入 1218  |   list删除 1532  |  deque删除 1172</p>
<p>测试结果：list&gt;deque&gt;&gt;vector</p>
<p>（为啥deque的删除会比list快呢，有疑问）</p>
<p>&nbsp;</p>
<p>5.iterator遍历操作</p>
<p>测试方式：在已有十万个int数据的情况下，使用iterator顺序遍历1000遍的毫秒数</p>
<p>结果数据：vector 1016  |   list 1437  |  deque 1203</p>
<p>测试结果：vector&gt;deque&gt;list</p>
<p>===================================================================</p>
<p>测试的时候再一次发现了stl在debug和release下的速度巨大差异，说来当初就是这个使自己对stl产生兴趣的</p>
<p>现在想想，大概主要是debug下没有inline吧！毕竟stl基本操作都是函数</p>
<p>但是vs的参数让人感觉还是不够自由，还是要换平台试试sgi stl为好</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.thpiano.com/?feed=rss2&#038;p=352</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>关于C++中运算符重载的豆知识</title>
		<link>http://blog.thpiano.com/?p=319</link>
		<comments>http://blog.thpiano.com/?p=319#comments</comments>
		<pubDate>Tue, 29 Nov 2011 17:06:07 +0000</pubDate>
		<dc:creator>suika</dc:creator>
				<category><![CDATA[编程]]></category>
		<category><![CDATA[C/C++]]></category>
		<category><![CDATA[Effective C++]]></category>
		<category><![CDATA[More Effective C++]]></category>
		<category><![CDATA[操作符重载]]></category>

		<guid isPermaLink="false">http://172.18.126.172/blog/?p=319</guid>
		<description><![CDATA[就是总结一下运算符重载的一些细微小技巧，目前一共六条 今日程老师的课刚好提到运算符重载，正好小小应景一下 主要内容来源是《Effective C++》和《More Effective C++》这两本书 ================================================================ 一、继承“=”时的小陷阱 对父类重载了“=”运算符，子类却没有继承过来! 究其原因，竟是系统自动生成的拷贝赋值函数（即对“=”的重载），覆盖了父类的同名函数 想原汁原味地继承对“=”的重载目前也找不到什么好方法 当然其他的运算符重载无论是继承还是virtual，都遵守普通成员函数的规则 &#160; 二、重载“=”时请返回引用 因为有连等的情况出现，例如： a=b=c=d=5，实际上是a=(b=(c=(d=5))) 所以至少要有返回值。那返回对象还是返回对象引用呢？当然是return *this的形式返回引用了！ 不然的话，每次都要生成临时变量，进行构造、析构，电脑都觉得累~ 顺便说一下，同理要返回引用的还有[]、+=系列、前置的++等。 从返回引用与返回对象的效率对比上，还可以推出另一个好用技巧： 无论什么函数，当函数的参数是较复杂的变量时（比如你自定义的某个类对象），将函数的参数类型改为const &#38; 同样可以提高效率！道理一样是节省构造、析构，前面的const还能保证调入者方面的安心，并保留了值传递时不改动原有对象的特性 最后在重载“=”时别忘了处理自身赋值的情况，比如a=a这样的，把拷贝构造新对象放在析构旧对象的语句之前就好了 &#160; 三、重载“()”的妙用：传说中的函数对象（伪函数） 是的，“()”也可以重载，比如： Class Sum{ int operator ()(int value1, int value2){ return (value1+value2); } } 然后Sum sum;cout&#60;&#60;sum(2,3)就可以看到5了，调用起来和函数指针一模一样 但是具备了几个函数指针不具备的特征： 1.方便实现inline inline是在编译期的动作，像函数指针这种运行时的行为不太容易做到inline，但是在以成员函数的方式声明的函数对象想inline还是很容易的。 在VC下，可能会发现qsort比sort还慢，难道那个q不是quick的意思？！ 其实是因为qsort使用了函数指针，sort使用了伪函数，因而在内联优化上后者会胜出 2.更多的辅助存储，干掉全局变量！ 因为是以成员方法的形式调用的，即是说这个对象的其他成员属性可以拿来做辅助存储，代替了全局变量的作用 3.可以加virtual关键字实现多态 &#160; 运算符重载的本质是静态链接，而virtual则会导致动态链接，于是这二者在此结合于一身了 &#160; 四、前置的++与后置的++ 1.不同的参数以进行重载函数的区分 [...]]]></description>
			<content:encoded><![CDATA[<p>就是总结一下运算符重载的一些细微小技巧，目前一共六条</p>
<p>今日程老师的课刚好提到运算符重载，正好小小应景一下</p>
<p>主要内容来源是《Effective C++》和《More Effective C++》这两本书</p>
<p><span id="more-319"></span></p>
<p>================================================================</p>
<p>一、继承“=”时的小陷阱</p>
<p>对父类重载了“=”运算符，子类却没有继承过来!</p>
<p>究其原因，竟是系统自动生成的拷贝赋值函数（即对“=”的重载），覆盖了父类的同名函数</p>
<p>想原汁原味地继承对“=”的重载目前也找不到什么好方法</p>
<p>当然其他的运算符重载无论是继承还是virtual，都遵守普通成员函数的规则</p>
<p>&nbsp;</p>
<p>二、重载“=”时请返回引用</p>
<p>因为有连等的情况出现，例如：</p>
<p>a=b=c=d=5，实际上是a=(b=(c=(d=5)))</p>
<p>所以至少要有返回值。那返回对象还是返回对象引用呢？当然是<span style="color: #0000ff;">return *this的形式返回引用</span>了！</p>
<p>不然的话，每次都要生成临时变量，进行构造、析构，电脑都觉得累~</p>
<p>顺便说一下，同理要返回引用的还有[]、+=系列、前置的++等。</p>
<p>从返回引用与返回对象的效率对比上，还可以推出另一个好用技巧：</p>
<p>无论什么函数，当函数的参数是较复杂的变量时（比如你自定义的某个类对象），<span style="color: #0000ff;">将函数的参数类型改为const &amp;</span> 同样可以提高效率！道理一样是节省构造、析构，前面的const还能保证调入者方面的安心，并保留了值传递时不改动原有对象的特性</p>
<p>最后在重载“=”时<span style="color: #0000ff;">别忘了处理自身赋值的情况</span>，比如a=a这样的，把拷贝构造新对象放在析构旧对象的语句之前就好了</p>
<p>&nbsp;</p>
<p>三、重载“()”的妙用：传说中的函数对象（伪函数）</p>
<p>是的，“()”也可以重载，比如：</p>
<pre>Class Sum{

    int operator ()(int value1, int value2){

        return (value1+value2);

    }

}</pre>
<p>然后Sum sum;cout&lt;&lt;sum(2,3)就可以看到5了，调用起来和函数指针一模一样</p>
<p>但是具备了几个函数指针不具备的特征：</p>
<p><span style="color: #0000ff;">1.方便实现inline</span></p>
<p>inline是在编译期的动作，像函数指针这种运行时的行为不太容易做到inline，但是在以成员函数的方式声明的函数对象想inline还是很容易的。</p>
<p>在VC下，可能会发现qsort比sort还慢，难道那个q不是quick的意思？！</p>
<p>其实是因为qsort使用了函数指针，sort使用了伪函数，因而在内联优化上后者会胜出</p>
<p><span style="color: #0000ff;">2.更多的辅助存储，干掉全局变量！</span></p>
<p>因为是以成员方法的形式调用的，即是说这个对象的其他成员属性可以拿来做辅助存储，代替了全局变量的作用</p>
<p><span style="color: #0000ff;">3.可以加virtual关键字实现多态</span></p>
<p>&nbsp;</p>
<p>运算符重载的本质是静态链接，而virtual则会导致动态链接，于是这二者在此结合于一身了</p>
<p>&nbsp;</p>
<p>四、前置的++与后置的++</p>
<p><span style="color: #0000ff;">1.不同的参数以进行重载函数的区分</span></p>
<p>二者都是单目运算符，但是运算符重载时需要用不同参数进行区分，后置的要给一个int参数</p>
<p>前置的++：type<span style="color: #0000ff;">&amp;</span> operator ++(){...};</p>
<p>后置的++：<span style="color: #0000ff;">const</span> type operator ++(<span style="color: #0000ff;">int</span>){...};</p>
<p><span style="color: #0000ff;">2.前置++请返回引用</span></p>
<p>道理同上文提到的“=”重载，同样这也能允许++++i 这样的写法</p>
<p><span style="color: #0000ff;">3.后置++请返回const对象</span></p>
<p>后置++返回对象应该没什么争议，因为在返回时，原对象的值已经改变了。想要达到“原对象自增一，返回自增前的值”只能返回一个新对象了。</p>
<p>为什么要加const呢？因为如果不这样的话，i++++ 这样的写法过后，先不管能不能编译通过，就算能通过，i只是自增了1次而非两次</p>
<p>因为返回值是新对象，所以链式调用里第二次自增的不是i 而是刚刚产生的新对象</p>
<p>既然无论如何达不到目的，干脆就以编译器报错的方式告诉用户不能这么写好了</p>
<p><span style="color: #0000ff;">4.后置++的实现请调用前置++</span></p>
<p>因为在 “对原对象自增1” 的操作上，二者是完全一致的，所以后置++就直接调用前置++好了</p>
<p><span style="color: #0000ff;">5.后置++比前置++慢</span></p>
<p>因为要多生成新对象所以慢。只想要自增效果的话就调用前置++好了，我也没把握编译器会不会帮你把孤零零的后置++优化为前置++</p>
<p>&nbsp;</p>
<p>五、基础四则运算也有小技巧!</p>
<p>这回轮到加减乘除了</p>
<p>小技巧就是<span style="color: #0000ff;">请返回const对象</span></p>
<p>这样做的好处是，当你把if (a*b==c)误写成了if (a*b=c)的时候，编译器会显式地指出你这个错误</p>
<p>另外由于运算得到了新值，所以要生成新对象，不能返回引用。</p>
<p>当然如果你写成了a=a+5，不妨改为a+=5： “+=” 的操作符返回的是引用，效率更高</p>
<p>&nbsp;</p>
<p>六、重载&amp;&amp;、||前请三思</p>
<p>&amp;&amp;和||有着非常好用的短路特性</p>
<p>比如说if (p!=null &amp;&amp; strlen(p)&gt;8)这种写法，你完全不必担心strlen取了一个空的p，因为p为空的话在p!=null后就会立刻跳转了，不会执行第二个逻辑判断条件</p>
<p>但是如果要重载&amp;&amp;和||的话，这个<span style="color: #0000ff;">短路特性就要丢失</span>咯</p>
<p>因为重载的本质就是函数，函数调用的时候，无论如何你也无法阻止编译器把传递的参数一个个计算好然后压进栈，更糟糕的是你还可能无法控制在入栈之前各参数计算的顺序。</p>
<p>同理还有逗号运算符，逗号运算符是从左向右依次执行的，重载后这个特性也将丢失</p>
<p>================================================================</p>
<p>其实还有更多技巧未能总结过来，比如说隐式转换对运算符重载带来的危害、operator new和new operator的区别等等，有兴趣的童鞋可以自行翻阅相关书籍</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.thpiano.com/?feed=rss2&#038;p=319</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>数组循环移位，你能想到多少种算法？</title>
		<link>http://blog.thpiano.com/?p=251</link>
		<comments>http://blog.thpiano.com/?p=251#comments</comments>
		<pubDate>Fri, 11 Nov 2011 16:15:27 +0000</pubDate>
		<dc:creator>suika</dc:creator>
				<category><![CDATA[编程]]></category>
		<category><![CDATA[C/C++]]></category>
		<category><![CDATA[循环位移]]></category>
		<category><![CDATA[编程之美]]></category>

		<guid isPermaLink="false">http://172.18.126.172/blog/?p=251</guid>
		<description><![CDATA[设计一个算法，把一个含有 N 个元素的数组循环右移K 位 例如： abcd12345循环右移5位，得到12345abcd 第一次遇到这个问题，还是一个朋友当成考研题来问我的，并给出了进一步的限制：时间复杂度为O（N），且只允许使用两个附加变量。 后来在《编程珠玑》、《编程之美》上都看到了这道题 想来按照那个限制的难度，肯定是不会出现在考研卷上的吧，不适合做考题，所以就先无视这个限制 &#160; 抛开各种算法不谈，就题目本身，也可以得到一些优化信息： 1.虽然题目是右移，但是左移的话也可以一样完成（数组长度为N，右移K位相当于左移N-K位） 在这种情况下，可以分别写出左移和右移的算法，然后根据K与N-K的大小关系，调用移动较少的那个 2.在K大于N的情况下，可以将右移K位简化为右移K%N位 &#160; 自己当初也想出了多种烂算法，就当抛砖引玉啦，后面会附上几个大牛给出的做法 共有3个自己想的烂算法，3个大牛算法 ============================================================== 烂算法1： 是自己最先想到的算法，时间复杂度O(N)，空间复杂度O(1) 实际上是普通排序算法的变异，以两数交换为主，只不过临时变量做了小改动 还是以abcd12345右移5位为例： a右移5位，跑到2的位置，同时临时变量记录2，此时原串变为abcd1a345 再从2的位置右移5位（位置取模），让2跑到b的位置，同时临时变量记录b，此时原串变为a2cd1a345 再从b的位置右移5位，让b跑到3的位置，同时临时变量记录3…… 如此循环数组长度N次之后即可，有个缺点是取模运算比较多 ============================================================== 烂算法2： 想要避免取模运算，该怎么办呢？ 干脆直接把数组叠两遍，变成abcd12345abcd12345，然后从第5个位置开始截取一段连续的就行了！ 其实最后一个元素不用叠，到abcd12345abcd1234就足够了 需要三次memcpy，空间复杂度比其他算法高，需要新开辟空间 ============================================================== 烂算法3： 应该是最原始的算法，每次将原数组循环右移一次 temp=a[0]; for (i=0;i&#60;length-1;++i)     a[i]=a[i+1]; a[length-1]=temp; 移动距离为K就这么做K次 时间复杂度O(K*N) ===================笨蛋和牛人的分界线========================= 牛人提供的算法4： 如果有频繁的移位操作需求的话，干脆做一个封装用来获取元素，而不是直接取下标 这样的话所有的访问都从封装走，每次位置取个余就行了，直接一步到位了 时间复杂度O(1)，空间复杂度O(1) ============================================================== 牛人提供的算法5：（个人认为是最佳算法） 对于abcd12345，将其根据位移长度（5）分为两部分，第一部分为第1个元素至第N-K个元素： abcd和12345 然后先将这两个部分内部分别倒序，得到dcba 54321 然后再将整个数组倒序，就能得到结果12345abcd 这个大概是符合那位同学所说的“时间复杂度为O（N），且只允许使用两个附加变量”变态要求的答案了。 [...]]]></description>
			<content:encoded><![CDATA[<p>设计一个算法，把一个含有 N 个元素的数组循环右移K 位</p>
<p>例如： abcd12345循环右移5位，得到12345abcd<span id="more-251"></span></p>
<p>第一次遇到这个问题，还是一个朋友当成考研题来问我的，并给出了进一步的限制：时间复杂度为O（N），且只允许使用两个附加变量。</p>
<p>后来在《编程珠玑》、《编程之美》上都看到了这道题</p>
<p>想来按照那个限制的难度，肯定是不会出现在考研卷上的吧，不适合做考题，所以就先无视这个限制</p>
<p>&nbsp;</p>
<p>抛开各种算法不谈，就题目本身，也可以得到一些优化信息：<br />
1.虽然题目是右移，但是左移的话也可以一样完成（数组长度为N，右移K位相当于左移N-K位）<br />
在这种情况下，可以分别写出左移和右移的算法，然后根据K与N-K的大小关系，调用移动较少的那个<br />
2.在K大于N的情况下，可以将右移K位简化为右移K%N位</p>
<p>&nbsp;</p>
<p>自己当初也想出了多种烂算法，就当抛砖引玉啦，后面会附上几个大牛给出的做法<br />
共有3个自己想的烂算法，3个大牛算法</p>
<p>==============================================================</p>
<p>烂算法1：</p>
<p>是自己最先想到的算法，时间复杂度O(N)，空间复杂度O(1)</p>
<p>实际上是普通排序算法的变异，以两数交换为主，只不过临时变量做了小改动</p>
<p>还是以abcd12345右移5位为例：</p>
<p>a右移5位，跑到2的位置，同时临时变量记录2，此时原串变为abcd1a345</p>
<p>再从2的位置右移5位（位置取模），让2跑到b的位置，同时临时变量记录b，此时原串变为a2cd1a345</p>
<p>再从b的位置右移5位，让b跑到3的位置，同时临时变量记录3……</p>
<p>如此循环数组长度N次之后即可，有个缺点是取模运算比较多</p>
<p>==============================================================</p>
<p>烂算法2：</p>
<p>想要避免取模运算，该怎么办呢？</p>
<p>干脆直接把数组叠两遍，变成abcd12345abcd12345，然后从第5个位置开始截取一段连续的就行了！</p>
<p>其实最后一个元素不用叠，到abcd12345abcd1234就足够了</p>
<p>需要三次memcpy，空间复杂度比其他算法高，需要新开辟空间</p>
<p>==============================================================</p>
<p>烂算法3：</p>
<p>应该是最原始的算法，每次将原数组循环右移一次</p>
<pre>temp=a[0];

for (i=0;i&lt;length-1;++i)

    a[i]=a[i+1];

a[length-1]=temp;</pre>
<p>移动距离为K就这么做K次<br />
时间复杂度O(K*N)</p>
<p>===================笨蛋和牛人的分界线=========================</p>
<p>牛人提供的算法4：</p>
<p>如果有频繁的移位操作需求的话，干脆做一个封装用来获取元素，而不是直接取下标</p>
<p>这样的话所有的访问都从封装走，每次位置取个余就行了，直接一步到位了<br />
时间复杂度O(1)，空间复杂度O(1)</p>
<p>==============================================================</p>
<p><span style="color: #0000ff;">牛人提供的算法5：（个人认为是最佳算法）</span></p>
<p>对于abcd12345，将其根据位移长度（5）分为两部分，第一部分为第1个元素至第N-K个元素：</p>
<p>abcd和12345</p>
<p>然后先将这两个部分内部分别倒序，得到dcba 54321</p>
<p>然后再将整个数组倒序，就能得到结果12345abcd</p>
<p>这个大概是符合那位同学所说的“时间复杂度为O（N），且只允许使用两个附加变量”变态要求的答案了。</p>
<p>其原理为UV=((V-1) (U-1))-1（-1为上标，表示取逆）</p>
<p>真是太巧妙了。</p>
<p>&nbsp;</p>
<p>顺便说一下，STL里面的rotate函数&lt;algorithm&gt;，就是用这种方法实现的</p>
<pre>template&lt;class _BidIt&gt; inline
void _Rotate(_BidIt _First, _BidIt _Mid, _BidIt _Last,   bidirectional_iterator_tag)
{
    std::reverse(_First, _Mid);
    std::reverse(_Mid, _Last);
    std::reverse(_First, _Last);
}</pre>
<p>===============================================================</p>
<p>牛人提供的算法6：（很长很长，涉及数学理论，只好一股脑转载了。这也是一个时间复杂度O(N)的算法）<br />
（将此算法与烂算法1进行对比，可深刻体会笨蛋与牛人的区别）<br />
循环置换分解定理：对于给定数组A[0..N-1]向后循环换位N-K位运算，可分解为恰好gcd(K,N-K)个循环置换，且0,...,gcd(K,N-K)-1中的每个数恰属于一个循环置换。其中gcd(x,y)表示x和y的最大公因数。</p>
<p>我们从头开始分析这个问题，对于数组A[0..n-1]，要将其向后循环移动k位元素。因为每个元素右移n位后又回到了原来的位置上，所以右移k位等于右移k mod n位。考虑每个元素右移k位后的最终位置，比如对于A[0]，右移k位后在k mod n位置上，原来在k mod n位置上的元素右移k位后到了2*k mod n的位置上，把如此因为A[0]的移动而受到连环影响必须移动的位置列出来，就是下面这样一个位置序列：0,k,2*k,...,(t-1)k。其中每一项都是在模n的意义下的位置。t*k mod n 的结果是0。t是使得t*k mod n的结果为0的最小正整数。</p>
<p>这个位置序列实质上是模n加法群中由元素k生成的一个循环子群。由群论中的结论(该结论的证明见最后)知，循环子群(k)的周期为n / gcd(k,n)，元数为n / gcd(k,n)，其陪集个数为gcd(k,n)。换句话说，A[0..n-1]循环右移k位的结果是循环子群(k)以及它的所有陪集循环右移一位。例如，将A[0..5] = {1,2,3,4,5,6}循环右移4位，这里n = 6, k = 4, gcd(k, n) = 2。A[0]的最终位置是4,A[4]的最终位置是2,A[2]的最终位置是0，这样，位置0,4,2便是由k=4生成的循环群，周期为6 / gcd(4,6) = 6 / 2 = 3，这样的循环子群共有gcd(4,6) = 2个。</p>
<p>完整代码如下：<br />
// 数组的循环移位</p>
<pre>#include &lt;cstdio&gt;

int gcd(int m, int n) {

    int r;

    while(r = m % n) {

        m = n; n = r;

    }

    return n;
}

void shiftArray(int A[], int n, int k) {

    // 因为左移的代码比右移的代码好实现的多，而右移k位

    // 等价于左移-k位，-k = n - k。以下代码是通过左移-k位来实现右移k位

    k = n - (k % n);

    for(int i = 0, cnt = gcd(n, k); i &lt; cnt; i++) {

        int t = A[i], p = i, j = (k+i) % n;

        while(j != i) {

            A[p] = A[j]; p = j; j = (k + p) % n;

        }

        A[p] = t;

    }

}

void printArray(int A[], int n) {
    for(int i = 0; i &lt; n; i++) {

        printf("%-3d", A[i]);

        if((i+1)%10 == 0) printf("/n");

    }

}

int main() {

    int A[] = {1,2,3,4,5,6, 7};

    shiftArray(A, 7, 4);

    printArray(A, 7);

    return 0;

}</pre>
<p>上述所用到的那个群论结论的证明：</p>
<p>结论：设G是一个循环群，其中一个生成元素为a，若r和n的最大公约数为d，则a^r的周期为n / d。</p>
<p><img src="http://p.blog.csdn.net/images/p_blog_csdn_net/jcwKyl/EntryImages/20090210/zm633698809141718750.JPG" alt="" width="422" height="240" border="0" /></p>
<p>在模n加法群中，1是一个生成元素，任意元素k=1*k，所以任意元素k生成的循环子群(k)的周期为n / gcd(k,n)。因为gcd(k,n)=gcd(k,n-k)，所以也可以写成n / gcd(k, n-k)。</p>
<p>&nbsp;</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.thpiano.com/?feed=rss2&#038;p=251</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>关于c中可变参数函数的实现及默认参数提升</title>
		<link>http://blog.thpiano.com/?p=221</link>
		<comments>http://blog.thpiano.com/?p=221#comments</comments>
		<pubDate>Wed, 09 Nov 2011 03:31:27 +0000</pubDate>
		<dc:creator>suika</dc:creator>
				<category><![CDATA[编程]]></category>
		<category><![CDATA[C/C++]]></category>

		<guid isPermaLink="false">http://172.18.126.172/blog/?p=221</guid>
		<description><![CDATA[先来讲可变参数函数，这个最常见的就是printf，scanf 一般来说这两个函数应该算是最早接触的那一批才是，但是其参数数目可变的特殊性却被我忽略了好久，直到前一阵程老师上课才发现 于是就稍微整理下好了，主要是va_list, va_start, va_arg, va_end的使用 不多说，直接上代码： sum是一个求和的函数，其第一个参数为参数数目，后面是要求和的数 #include &#60;stdio.h&#62; #include &#60;stdarg.h&#62; int sum(int num,...); int main(void) { int a=sum(8,2,5,3,6,4,7,8,5); int b=sum(4,5,1,2,3); printf("a=%d, b=%d\n",a,b); getch(); return 0; } int sum(int num,...){ va_list ap; int s=0; va_start(ap,num);/* ap初始化，从num开始*/ while (num&#62;0){ s+=va_arg(ap,int);/*从参数里取一个新值，同时ap向后移至下一个参数的位置*/ --num; } va_end(ap); return s; } 输出结果为a=40,b=11，好用 可变参数函数的声明特点是，参数列表里有“...”出现，表示此处可以接纳无限多个参数 我们都知道函数传参的时候是从右往左依次压栈的，所以参数的地址是连续的，只要从头往下一直移啊移的又不越界，多少个参数的值都能取得到。 &#160; 在具体代码里，首先定义一个va_list指针ap，取各个参数的值就靠他了； va_start给ap的地址初始化；注意此时ap指向的地址其实是num的下一个参数 之后每次调用va_arg时，会返回ap指向的参数的值，同时ap会移动至下一个参数的地址 所以在调用va_arg时要传入你要读入的变量的类型（即大小）使之能移动合适的距离 最后使用va_end来释放资源。 [...]]]></description>
			<content:encoded><![CDATA[<p>先来讲可变参数函数，这个最常见的就是printf，scanf</p>
<p>一般来说这两个函数应该算是最早接触的那一批才是，但是其参数数目可变的特殊性却被我忽略了好久，直到前一阵程老师上课才发现</p>
<p>于是就稍微整理下好了，主要是va_list, va_start, va_arg, va_end的使用<span id="more-221"></span></p>
<p>不多说，直接上代码：</p>
<p>sum是一个求和的函数，其第一个参数为参数数目，后面是要求和的数</p>
<pre>#include &lt;stdio.h&gt;
#include &lt;stdarg.h&gt;

int sum(int num,...);

int main(void)
{
    int a=sum(8,2,5,3,6,4,7,8,5);
    int b=sum(4,5,1,2,3);
    printf("a=%d, b=%d\n",a,b);
    getch();
    return 0;
}

int sum(int num,...){
    va_list ap;
    int s=0;
    va_start(ap,num);/* ap初始化，从num开始*/
    while (num&gt;0){
        s+=va_arg(ap,int);/*从参数里取一个新值，同时ap向后移至下一个参数的位置*/
        --num;
    }
    va_end(ap);
    return s;
}</pre>
<p>输出结果为a=40,b=11，好用</p>
<p>可变参数函数的声明特点是，参数列表里有“...”出现，表示此处可以接纳无限多个参数</p>
<p>我们都知道函数传参的时候是从右往左依次压栈的，所以参数的地址是连续的，只要从头往下一直移啊移的又不越界，多少个参数的值都能取得到。</p>
<p>&nbsp;</p>
<p>在具体代码里，首先定义一个va_list指针ap，取各个参数的值就靠他了；</p>
<p>va_start给ap的地址初始化；注意此时ap指向的地址其实是num的下一个参数</p>
<p>之后每次调用va_arg时，会返回ap指向的参数的值，同时ap会移动至下一个参数的地址</p>
<p>所以在调用va_arg时要传入你要读入的变量的类型（即大小）使之能移动合适的距离</p>
<p>最后使用va_end来释放资源。</p>
<p>&nbsp;</p>
<p>通过翻头文件，发现其实va系列的全都是宏：</p>
<p>va_list的：</p>
<pre>typedef char * va_list;</pre>
<p>（原来就是一个char*的指针啦）</p>
<p>va_start的：</p>
<pre>#define va_start(ap,v)  (ap = (va_list)&amp;v + _INTSIZEOF(v))</pre>
<p>（可以看到ap指向的是v的地址+v的大小，即v的下一个参数）<br />
va_arg的：</p>
<pre>#define va_arg(ap,t) (*(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)))</pre>
<p>（可以看到，会让ap挪到下一个参数，同时返回ap挪之前那个参数的值）<br />
va_end的：</p>
<pre>#define va_end(ap) (ap = (va_list)0)</pre>
<p>（看上去好像没什么用，但是说不准哪个平台下va_list是分配的堆内存，那届时这里的释放操作就很必要了，所以va_end不能省）<br />
同时注意到他取变量的大小，使用的不是sizeof而是另一个宏_INTSIZEOF：</p>
<pre>#define _INTSIZEOF(n) ( (sizeof(n) + sizeof(int) - 1) &amp; ~(sizeof(int) -1))</pre>
<p>n的大小都取整到int的倍数了</p>
<p>这意味着就算你输入了va_arg(ap,char)，他也会向后移4个字节指向下一个变量。即：传进来的char变量，实际上占用了int的大小！</p>
<p>令人不禁想起之前提到的struct内存对齐。</p>
<p>&nbsp;</p>
<p>上网查询资料，得到了“默认参数提升”（Default Argument Promotions）这个说法：</p>
<p>当函数的参数数量不定时，会发生默认参数提升。默认参数提升，会使<span style="color: #0000ff;">char,short变量转换为int</span>类型再压进栈，而<span style="color: #0000ff;">float变量会转换为double</span>类型再压进栈。</p>
<p>这样就可以理解为什么printf里%d同时支持int到char，%f同时支持float和double了。scanf里面有%lf来对应double，是因为scanf传递的是地址而不是变量，不受默认参数提升的影响。</p>
<p>&nbsp;</p>
<div style="color: #FFF;">
可以看到，因为无法得知具体参数大小，出现了默认参数提升这种做法。那如果连参数的类型都不告诉你，会发生什么事呢？</p>
<p>看如下代码：</p>
<pre>#include "stdio.h"

printit(a,b,c){
printf("%u\n%u\n%u\n",sizeof(a),sizeof(b),sizeof(c));
}

int main(void)
{
float a=3.45f;
char b='C';
double c=6.6;
printit(a,b,c);
return 0;
}</pre>
<p>注意到函数printit(a,b,c)，其参数都完全没有说明其类型。这个能通过tc和gcc的编译</p>
<p>最后执行的结果竟然是3个int的大小!</p>
<p>可以看到，在参数类型未知的情况下，会全部当做int来处理。鉴于浮点数硬转整数的奇葩性，且该特性未写入标准，我认为没有人会使用这种写法。</p>
<p>&nbsp;</p>
<p>当然程老师课上的演示代码中也有缺失类型的函数出现。不得不赞叹程老师的课还是有很多可以挖掘的细节。
</p></div>
<p>最后关于默认参数提升的编译器细节实现，可以移步看某位大牛的博客：</p>
<p><a href="http://hi.baidu.com/xyk34/blog/item/b4fd61351e63b50391ef3915.html">C语言中变长参数中的一个有趣的现象。关于默认参数提升（default argument promotion）</a></p>
]]></content:encoded>
			<wfw:commentRss>http://blog.thpiano.com/?feed=rss2&#038;p=221</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>关于struct的字节对齐</title>
		<link>http://blog.thpiano.com/?p=175</link>
		<comments>http://blog.thpiano.com/?p=175#comments</comments>
		<pubDate>Thu, 03 Nov 2011 13:44:42 +0000</pubDate>
		<dc:creator>suika</dc:creator>
				<category><![CDATA[编程]]></category>
		<category><![CDATA[C/C++]]></category>
		<category><![CDATA[struct]]></category>
		<category><![CDATA[内存对齐]]></category>

		<guid isPermaLink="false">http://172.18.126.172/blog/?p=175</guid>
		<description><![CDATA[今天的课是由同届的同学来讲的，真是讲得好精彩！ 就我个人感觉来看，已经达到平均水准以上了。稍稍留意了一下，无论是习惯、用语、节奏，都是与我一直接触的老师们别无二样； 作为和他一届的同学真是倍感压力。 啊话题扯远了，临下课前老师借由#pragma提到了struct字节对齐的内容，我也只是不久前刚了解的，真是堪堪赶上教学进度~于是写这篇日志总结顺便再温习一下。 给出这样一段代码： struct test{     char a;     long b; }; void main(){     struct test t1,t2;     memset(&#38;t1,0,sizeof(struct test));     t2.a=0;     t2.b=0;     if (memcmp(&#38;t1,&#38;t2,sizeof(struct test))==0){         printf("yes\n");     }else{         printf("no\n");     }     printf("%d\n",(int)sizeof(struct test)); } 看到声明了两个同类结构体t1,t2，t1使用memset归零，t2则是将成员变量全部归零。在主流编译器上，其结果竟然是no! 最后打印出的struct大小也不是char+long的5字节，而是8字节。 &#160; 究其原因，是因为编译器对struct进行了对齐处理。因而t2只归零了5字节，memcmp会返回不一致。 查看详情，char占据了第一个字节没问题，但是之后的3个字节都空着，直到第4个字节才开始放long。 &#160; 为什么要进行对齐处理呢？ 有些平台上变量不对齐会出错，而有些平台上变量对齐可以提升对内存的读写速度（比如说一个读周期读4个字节，连续读取下每次读都是从偶地址开始。如果long是放在2-5字节的话，就要读两次才行） 对struct对齐处理的规则有两条： 1.每个变量相对头部的偏移量必须是它大小的整数倍，例：char a;long b;  b就要从他体积的整数倍（4的倍数）处开始放，因而放完一个字节的char后，要再空3个字节才可以放b 2.所有变量都存放完毕后，整个struct的体积要能被其最大成员的体积整除 例： struct s1{     char a;   [...]]]></description>
			<content:encoded><![CDATA[<p>今天的课是由同届的同学来讲的，真是讲得好精彩！</p>
<p>就我个人感觉来看，已经达到平均水准以上了。稍稍留意了一下，无论是习惯、用语、节奏，都是与我一直接触的老师们别无二样；</p>
<p>作为和他一届的同学真是倍感压力。</p>
<p>啊话题扯远了，临下课前老师借由#pragma提到了struct字节对齐的内容，我也只是不久前刚了解的，真是堪堪赶上教学进度~于是写这篇日志总结顺便再温习一下。<span id="more-175"></span></p>
<p>给出这样一段代码：</p>
<pre><div>struct test{</div>
<div> <wbr>  <wbr> char a;</wbr></wbr></div>
<div> <wbr>  <wbr> long b;</wbr></wbr></div>
<div>};</div>
<div>void main(){</div>
<div> <wbr>  <wbr> struct test t1,t2;</wbr></wbr></div>
<div> <wbr>  <wbr> memset(&amp;t1,0,sizeof(struct test));</wbr></wbr></div>
<div> <wbr>  <wbr> t2.a=0;</wbr></wbr></div>
<div> <wbr>  <wbr> t2.b=0;</wbr></wbr></div>
<div> <wbr>  <wbr> if (memcmp(&amp;t1,&amp;t2,sizeof(struct test))==0){</wbr></wbr></div>
<div> <wbr>  <wbr>  <wbr>  <wbr> printf("yes\n");</wbr></wbr></wbr></wbr></div>
<div> <wbr>  <wbr> }else{</wbr></wbr></div>
<div> <wbr>  <wbr>  <wbr>  <wbr> printf("no\n");</wbr></wbr></wbr></wbr></div>
<div> <wbr>  <wbr> }</wbr></wbr></div>
<div>    printf("%d\n",(int)sizeof(struct test));</div>
<div>}</div></pre>
<div>看到声明了两个同类结构体t1,t2，t1使用memset归零，t2则是将成员变量全部归零。在主流编译器上，其结果竟然是no!</div>
<div>最后打印出的struct大小也不是char+long的5字节，而是8字节。</div>
<p>&nbsp;</p>
<div>究其原因，是因为编译器对struct进行了对齐处理。因而t2只归零了5字节，memcmp会返回不一致。</div>
<div>查看详情，char占据了第一个字节没问题，但是之后的3个字节都空着，直到第4个字节才开始放long。</div>
<p>&nbsp;</p>
<div>为什么要进行对齐处理呢？</div>
<div>有些平台上变量不对齐会出错，而有些平台上变量对齐可以提升对内存的读写速度（比如说一个读周期读4个字节，连续读取下每次读都是从偶地址开始。如果long是放在2-5字节的话，就要读两次才行）</div>
<div>对struct对齐处理的规则有两条：</div>
<div>1.<span style="color: #0000ff;">每个变量相对头部的偏移量必须是它大小的整数倍</span>，例：char a;long b;  b就要从他体积的整数倍（4的倍数）处开始放，因而放完一个字节的char后，要再空3个字节才可以放b</div>
<div>2.<span style="color: #0000ff;">所有变量都存放完毕后，整个struct的体积要能被其最大成员的体积整除</span></div>
<div>例：</div>
<pre><div>struct s1{</div>
<div>    char a;</div>
<div>    long b;</div>
<div>};</div>
<div>struct s2{</div>
<div>    char a;</div>
<div>    struct s1 s;</div>
<div>    char b;</div>
<div>};</div></pre>
<div>则s1的大小为8字节，<span style="color: #0000ff;">其作为整体时对齐长度不再是体积，而是其内部最大成员即long的4字节</span></div>
<div>s2在地址对齐时，先分配给a一个字节，再空3个字节保证s的偏移量能被4整除，再放s（8个字节），再放一个字节的b</div>
<div>这时的大小为1+3+8+1=13，最后再补3个字节13+3=16，使得16能被最大成员s的对齐长度4整除。</div>
<p>&nbsp;</p>
<div>可以看到，内存对齐是与具体变量的大小息息相关的。</div>
<div>有的变量大小在不同平台上有差异，这个时候可以使用#pragma pack(x)强制定义对齐字节的大小为x</div>
<div>一旦使用了#pragma pack(x)，则每次计算地址对齐时，使用的不再是变量的大小，而是变量大小与x二者的最小值。</div>
<div>比如说先写了#pragma pack(2)，则对于char类型还是按1字节对齐，而对于其他体积大于1字节的变量，都按照2字节对齐</div>
<div>可以使用#pragma pack()换回默认计算方式</div>
<p>&nbsp;</p>
<div>目前的主流编译器都实现了对struct的内存对齐，但并不是所有编译器都支持（手头的老掉牙wintc就不支持，用这个来比赛真是太令人郁闷了）</div>
<div>另外寻求完美的代码高亮方案中，希望能不用插件、复制不会有行号</div>
]]></content:encoded>
			<wfw:commentRss>http://blog.thpiano.com/?feed=rss2&#038;p=175</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>初探#pragma</title>
		<link>http://blog.thpiano.com/?p=173</link>
		<comments>http://blog.thpiano.com/?p=173#comments</comments>
		<pubDate>Thu, 03 Nov 2011 13:12:20 +0000</pubDate>
		<dc:creator>suika</dc:creator>
				<category><![CDATA[编程]]></category>
		<category><![CDATA[#pragma]]></category>
		<category><![CDATA[C/C++]]></category>
		<category><![CDATA[预编译]]></category>

		<guid isPermaLink="false">http://172.18.126.172/blog/?p=173</guid>
		<description><![CDATA[今天老师上课提到了宏定义的用途，抛出一个问题： #pragma的哪个功能最常见？ 我凭借印象回答说是once，老师没点我；在黑板上写下#pragma pack(1)，引出struct的内存对齐。 不过凭印象里面，的确是#pragma once比较常见呀，还有去掉warning、链接lib什么的。可能和老师精通嵌入式开发有关 于是在这里整理下#pragma的几种常见用途好了 1.#pragma pack(x) 表示接下来的struct内存对齐以x字节为标准单位（即内存对齐长度不会小于x字节）。使用#pragma pack()来恢复使用默认对齐方式 &#160; 2.#pragma once 加入这个之后，可使头文件在被多次include的情况下只编译一次，避免重复定义等麻烦事； 当然有一个传统的做法是#ifndef xxx -&#62; #define xxx  -&#62; 代码段 -&#62; #endif，用途一样 二者的差异是： #pragma once编译速度快一些，不用怕想出的宏名冲突，对整个文件有效； #ifndef支持性更好（特别是非vs编译器上），可针对文件内的部分代码生效 &#160; 3.#pragma comment( comment-type ,["commentstring"] ) 这里的功能比较多，光是comment-type就可以分为5种：compiler，exestr，lib，linker，user 其中lib用的最多，#pragma comment(lib,"bass.lib") 就把bass.lib加入到工程里了，这样就不需要再从工程配置文件里进行设置 其他的就不多提了，这方面的文章一搜一堆~ &#160; 4.#pragma warning( warning-type , warning-number...) 可以控制编译器的警告输出！比如某个警告一出一大堆你又看着碍眼，就可以利用这个屏蔽掉 warning-type可填once、error、disable、enable、default等，分别对应：显示一次、按错误处理、不显示警告、显示警告、默认设置 warning-number就是警告的代号。当然填法还是多样的，可以几个处理填到一行里，比如： #pragma warning( disable :  4100 4511 4512 4663 4245 [...]]]></description>
			<content:encoded><![CDATA[<p>今天老师上课提到了宏定义的用途，抛出一个问题：</p>
<p>#pragma的哪个功能最常见？</p>
<p>我凭借印象回答说是once，老师没点我；在黑板上写下#pragma pack(1)，引出struct的内存对齐。</p>
<p>不过凭印象里面，的确是#pragma once比较常见呀，还有去掉warning、链接lib什么的。可能和老师精通嵌入式开发有关</p>
<p>于是在这里整理下#pragma的几种常见用途好了</p>
<p><span id="more-173"></span>1.#pragma pack(x)</p>
<p>表示接下来的struct内存对齐以x字节为标准单位（即内存对齐长度不会小于x字节）。使用#pragma pack()来恢复使用默认对齐方式</p>
<p>&nbsp;</p>
<p>2.#pragma once</p>
<p>加入这个之后，可使头文件在被多次include的情况下只编译一次，避免重复定义等麻烦事；</p>
<p>当然有一个传统的做法是#ifndef xxx -&gt; #define xxx  -&gt; 代码段 -&gt; #endif，用途一样</p>
<p>二者的差异是：</p>
<p>#pragma once编译速度快一些，不用怕想出的宏名冲突，对整个文件有效；</p>
<p>#ifndef支持性更好（特别是非vs编译器上），可针对文件内的部分代码生效</p>
<p>&nbsp;</p>
<p>3.#pragma comment( comment-type ,["commentstring"] )</p>
<p>这里的功能比较多，光是comment-type就可以分为5种：compiler，exestr，lib，linker，user</p>
<p>其中lib用的最多，#pragma comment(lib,"bass.lib") 就把bass.lib加入到工程里了，这样就不需要再从工程配置文件里进行设置</p>
<p>其他的就不多提了，这方面的文章一搜一堆~</p>
<p>&nbsp;</p>
<p>4.#pragma warning( warning-type , warning-number...)</p>
<p>可以控制编译器的警告输出！比如某个警告一出一大堆你又看着碍眼，就可以利用这个屏蔽掉</p>
<p>warning-type可填once、error、disable、enable、default等，分别对应：显示一次、按错误处理、不显示警告、显示警告、默认设置</p>
<p>warning-number就是警告的代号。当然填法还是多样的，可以几个处理填到一行里，比如：</p>
<p>#pragma warning( disable :  4100 4511 4512 4663 4245 4018 4514)</p>
<p>#pragma warning( disable : 4507; once : 4385; error : 164 )</p>
<p>#prama warning只对所在的头文件、以及包含该头文件的有效</p>
<p>&nbsp;</p>
<p>其他的一些用法比如显示编译消息的#pragma message()、开发驱动会用到的#pragma code_seg()、引入资源的#pragma resource ，以及一些针对特定编译器的用法，就不多提了</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.thpiano.com/?feed=rss2&#038;p=173</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Day 8244 鲨1</title>
		<link>http://blog.thpiano.com/?p=164</link>
		<comments>http://blog.thpiano.com/?p=164#comments</comments>
		<pubDate>Wed, 02 Nov 2011 10:31:44 +0000</pubDate>
		<dc:creator>suika</dc:creator>
				<category><![CDATA[日常流水账]]></category>
		<category><![CDATA[C/C++]]></category>
		<category><![CDATA[sha1]]></category>

		<guid isPermaLink="false">http://172.18.126.172/blog/?p=164</guid>
		<description><![CDATA[那个该死的sha1终于被我弄完了，代码写得很土（使用了全局变量），但是功能基本都实现了； 注释也懒得写了；一个期望半小时搞定的程序竟然拖了三天，真是让人充满了鲨欲 第一天：先在有符号数的移位上卡了一阵，后来出现了未知原因，debug到晚上1点也未有进展 第二天：情绪低落中（这种情况哪能不郁闷啊！），后来元气稍稍恢复，推倒重写，在vs上取得了成功；但是tc怎么就是跑不对，我能想到的只有变量长度，也不知道这个该死的wintc怎么debug。继续郁闷中 第三天：在其他电脑上gcc编译了下，发现是64位系统，稍作改动后也成功了；于是果断决定抛弃wintc，太纱布了；加入了对大小4G以上文件的处理功能 至此总算是填上坑了。 说来做sha1的目的一是温习文件操作，二是克服对数学的恐惧。如今看来文件操作方面根本没什么温习到的，数学的恐惧则又加深了一层…… 顺带也引出了个小坑：怎么方便地用define来判断操作系统位数呢？自己找到的一个看上去比较巧妙的解决办法是#define OS_BITS (((int)((int *)0 + 1)) &#60;&#60; 3)，但是这个起不到判断的效果啊 &#160; 接下来就是opencv的helloworld和坑了老久的三角形了（望天 舍友开始晚上跑步了，我也考虑要不要一起跑。唯一的阻力是洗澡不方便，我多么想要一个热水器……]]></description>
			<content:encoded><![CDATA[<p>那个该死的sha1终于被我弄完了，代码写得很土（使用了全局变量），但是功能基本都实现了；</p>
<p>注释也懒得写了；一个期望半小时搞定的程序竟然拖了三天，真是让人充满了鲨欲<span id="more-164"></span></p>
<p>第一天：先在有符号数的移位上卡了一阵，后来出现了未知原因，debug到晚上1点也未有进展</p>
<p>第二天：情绪低落中（这种情况哪能不郁闷啊！），后来元气稍稍恢复，推倒重写，在vs上取得了成功；但是tc怎么就是跑不对，我能想到的只有变量长度，也不知道这个该死的wintc怎么debug。继续郁闷中</p>
<p>第三天：在其他电脑上gcc编译了下，发现是64位系统，稍作改动后也成功了；于是果断决定抛弃wintc，太纱布了；加入了对大小4G以上文件的处理功能</p>
<p>至此总算是填上坑了。</p>
<p>说来做sha1的目的一是温习文件操作，二是克服对数学的恐惧。如今看来文件操作方面根本没什么温习到的，数学的恐惧则又加深了一层……</p>
<p>顺带也引出了个小坑：怎么方便地用define来判断操作系统位数呢？自己找到的一个看上去比较巧妙的解决办法是#define OS_BITS (((int)((int *)0 + 1)) &lt;&lt; 3)，但是这个起不到判断的效果啊</p>
<p>&nbsp;</p>
<p>接下来就是opencv的helloworld和坑了老久的三角形了（望天</p>
<p>舍友开始晚上跑步了，我也考虑要不要一起跑。唯一的阻力是洗澡不方便，我多么想要一个热水器……</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.thpiano.com/?feed=rss2&#038;p=164</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>[转载]为DrawPrimitiveUP(DrawUserPrimitive)洗冤</title>
		<link>http://blog.thpiano.com/?p=133</link>
		<comments>http://blog.thpiano.com/?p=133#comments</comments>
		<pubDate>Thu, 27 Oct 2011 13:49:39 +0000</pubDate>
		<dc:creator>suika</dc:creator>
				<category><![CDATA[编程]]></category>
		<category><![CDATA[C/C++]]></category>
		<category><![CDATA[DirectX]]></category>

		<guid isPermaLink="false">http://172.18.126.172/blog/?p=133</guid>
		<description><![CDATA[在做D3D画线时，处理顶点缓冲发现的问题！所以转一下 最初只因DXSDK文档里说了句推荐用Vertex Buffer而不要用DrawPrimitiveUP（C#里叫DrawUserPrimitive），DrawPrimitiveUP很快被描绘成传说 中的瘟疫，人人都在警告不要接近它。估计有人会想过，既然DrawPrimitiveUP这么不好，为什么还要提供它，难道只是为了显示DX也可以像 OpenGL一样简单地画三角形？ 记得当年我就是抱着这种想法在网上狂搜，功夫不负有心人，还真找到了。不过看了很不好意思，人家上来就批判不实际测试、以讹传讹的问题，我也比较懒，没动手测一下。那么为了和我一样的懒人，我把问题用中文解释一遍。 首 先，DrawPrimitiveUP内部其实就是一个dynamic vertex buffer（动态顶点缓冲），和我们自己实现一个动态顶点缓冲没区别。一般情况下，DrawPrimitiveUP和用动态顶点缓冲的效率也没多大区 别。也就是说DrawPrimitiveUP其实很高效的，而且简单易用。Irrlicht引擎几乎所有绘制都用的DrawPrimitiveUP，也很 快的。 那为什么不推荐用？ 原因一：DX8发布时显存容量已经有了很大提高，静态顶点缓冲可以缓存在显存或AGP内存里，从而节省带宽占用。所以推荐能用静态顶点缓冲的一定要用静态的。静态的可以比DrawPrimitiveUP和动态顶点缓冲都快很多。 原 因二：DrawPrimitiveUP相对动态顶点缓冲而言，需要将用户内存里的顶点数据复制到内部动态顶点缓冲，即多了一次复制，如果顶点数量较大，复 制开销也会加大。但很多程序里的动态缓冲设计并不太好，为了抽象或方便使用，也会复制一次数据。所以便丧失了这条优势。 原因三：这个比较 复杂，我们知道一帧内Batch（批）的数量直接影响CPU的占用率，1G处理器30FPS下每帧700Batch左右就会占用100%CPU。每个设置 设备状态到发出绘制命令的转换都将产生一个Batch。动态顶点缓冲的推荐使用模式是一个可以合并Batch的模式，即不断地填充顶点数据，但不立刻绘 制，在缓冲填满时才提交绘制一次，当然能合并的前提是各个batch都使用相同的设备状态，即纹理、材质、RenderStates、变换矩阵等。 原因四：DrawPrimitiveUP只支持一个顶点流。这其实是个不算是原因的原因。当然是只用一个顶点流时才用它。 综上所述，这其实是个优化问题，用动态顶点缓冲有可能做更多的优化，但如果做得不好，会比DrawPrimitiveUP差。如果正确使用了，但没有进一步的优化或者引擎的用法不具备可优化的特性，那么也就和DrawPrimitiveUP效率相当。 但 事实上用动态顶点缓冲做错了的也很多，最常见的就是没有正确使用Lock标志位，用锁定静态缓冲的方法锁定，根本得不到动态缓冲的效果。另外用C#和 MDX的，如果用返回数组的Lock方法重载，也完全没有意义，因为在内部整个缓冲被复制到数组，Unlock时再复制回去。即使用 GraphicsStream写顶点数据也很慢，因为会导致大量的Boxing，只有直接用指针写数据才能发挥动态缓冲的优势。 怎么才算做得好，DXSDK里有明确的样例，为了懒人，我帖出来： // 用法 1 // 每次绘制抛弃整个顶点缓冲内容并重新填充几千个顶点 // 可能包含多个物体，有可能需要按设备状态分几次DrawPrimitive // 计算需要填充的字节数 UINT nSizeOfData = nNumberOfVertices * m_nVertexStride; // 抛弃并重新填充 CONST DWORD dwLockFlags = D3DLOCK_DISCARD; // 锁定顶点缓冲内存 BYTE* pBytes; if( [...]]]></description>
			<content:encoded><![CDATA[<p>在做D3D画线时，处理顶点缓冲发现的问题！所以转一下</p>
<p>最初只因DXSDK文档里说了句推荐用Vertex Buffer而不要用DrawPrimitiveUP（C#里叫DrawUserPrimitive），DrawPrimitiveUP很快被描绘成传说 中的瘟疫，人人都在警告不要接近它。估计有人会想过，既然DrawPrimitiveUP这么不好，为什么还要提供它，难道只是为了显示DX也可以像 OpenGL一样简单地画三角形？<span id="more-133"></span></p>
<p>记得当年我就是抱着这种想法在网上狂搜，功夫不负有心人，还真找到了。不过看了很不好意思，人家上来就批判不实际测试、以讹传讹的问题，我也比较懒，没动手测一下。那么为了和我一样的懒人，我把问题用中文解释一遍。</p>
<p>首 先，DrawPrimitiveUP内部其实就是一个dynamic vertex buffer（动态顶点缓冲），和我们自己实现一个动态顶点缓冲没区别。一般情况下，DrawPrimitiveUP和用动态顶点缓冲的效率也没多大区 别。也就是说DrawPrimitiveUP其实很高效的，而且简单易用。Irrlicht引擎几乎所有绘制都用的DrawPrimitiveUP，也很 快的。</p>
<p>那为什么不推荐用？</p>
<p>原因一：DX8发布时显存容量已经有了很大提高，静态顶点缓冲可以缓存在显存或AGP内存里，从而节省带宽占用。所以推荐能用静态顶点缓冲的一定要用静态的。静态的可以比DrawPrimitiveUP和动态顶点缓冲都快很多。</p>
<p>原 因二：DrawPrimitiveUP相对动态顶点缓冲而言，需要将用户内存里的顶点数据复制到内部动态顶点缓冲，即多了一次复制，如果顶点数量较大，复 制开销也会加大。但很多程序里的动态缓冲设计并不太好，为了抽象或方便使用，也会复制一次数据。所以便丧失了这条优势。</p>
<p>原因三：这个比较 复杂，我们知道一帧内Batch（批）的数量直接影响CPU的占用率，1G处理器30FPS下每帧700Batch左右就会占用100%CPU。每个设置 设备状态到发出绘制命令的转换都将产生一个Batch。动态顶点缓冲的推荐使用模式是一个可以合并Batch的模式，即不断地填充顶点数据，但不立刻绘 制，在缓冲填满时才提交绘制一次，当然能合并的前提是各个batch都使用相同的设备状态，即纹理、材质、RenderStates、变换矩阵等。</p>
<p>原因四：DrawPrimitiveUP只支持一个顶点流。这其实是个不算是原因的原因。当然是只用一个顶点流时才用它。</p>
<p>综上所述，这其实是个优化问题，用动态顶点缓冲有可能做更多的优化，但如果做得不好，会比DrawPrimitiveUP差。如果正确使用了，但没有进一步的优化或者引擎的用法不具备可优化的特性，那么也就和DrawPrimitiveUP效率相当。</p>
<p>但 事实上用动态顶点缓冲做错了的也很多，最常见的就是没有正确使用Lock标志位，用锁定静态缓冲的方法锁定，根本得不到动态缓冲的效果。另外用C#和 MDX的，如果用返回数组的Lock方法重载，也完全没有意义，因为在内部整个缓冲被复制到数组，Unlock时再复制回去。即使用 GraphicsStream写顶点数据也很慢，因为会导致大量的Boxing，只有直接用指针写数据才能发挥动态缓冲的优势。</p>
<p>怎么才算做得好，DXSDK里有明确的样例，为了懒人，我帖出来：<br />
// 用法 1<br />
// 每次绘制抛弃整个顶点缓冲内容并重新填充几千个顶点<br />
// 可能包含多个物体，有可能需要按设备状态分几次DrawPrimitive</p>
<p>// 计算需要填充的字节数<br />
UINT nSizeOfData = nNumberOfVertices * m_nVertexStride;</p>
<p>// 抛弃并重新填充<br />
CONST DWORD dwLockFlags = D3DLOCK_DISCARD;</p>
<p>// 锁定顶点缓冲内存<br />
BYTE* pBytes;<br />
if( FAILED( m_pVertexBuffer-&gt;Lock( 0, 0, &amp;pBytes, dwLockFlags ) ) )<br />
return false;</p>
<p>// 将顶点数据复制到顶点缓冲<br />
memcpy( pBytes, pVertices, nSizeOfData );<br />
m_pVertexBuffer-&gt;Unlock();</p>
<p>// 绘制<br />
m_pDevice-&gt;DrawPrimitive( D3DPT_TRIANGLELIST, 0, nNumberOfVertices/3)</p>
<p>// 用法 2<br />
// 对多个物体复用一个顶点缓冲</p>
<p>// 计算需要填充的字节数<br />
UINT nSizeOfData = nNumberOfVertices * m_nVertexStride;</p>
<p>// 如果顶点缓冲内的剩余空间可以容纳要填充的顶点数量，则指定不覆盖原有数据<br />
DWORD dwLockFlags = D3DLOCK_NOOVERWRITE;</p>
<p>// 检查顶点缓冲空间是否用光<br />
if( m_nNextVertexData &gt; m_nSizeOfVB - nSizeOfData )<br />
{<br />
// 没有足够的空间，抛弃原有数据重新开始<br />
dwLockFlags = D3DLOCK_DISCARD;<br />
m_nNextVertexData = 0;<br />
}</p>
<p>// 锁定顶点缓冲内存<br />
BYTE* pBytes;<br />
if( FAILED( m_pVertexBuffer-&gt;Lock( (UINT)m_nNextVertexData, nSizeOfData,<br />
&amp;pBytes, dwLockFlags ) ) )<br />
return false;</p>
<p>// 将顶点数据复制到顶点缓冲<br />
memcpy( pBytes, pVertices, nSizeOfData );<br />
m_pVertexBuffer-&gt;Unlock();</p>
<p>// 绘制<br />
m_pDevice-&gt;DrawPrimitive( D3DPT_TRIANGLELIST,<br />
m_nNextVertexData/m_nVertexStride, nNumberOfVertices/3)</p>
<p>// 计算下一次的写入位置<br />
m_nNextVertexData += nSizeOfData;</p>
<p>当 然，这只是个正确用法样例，优化起来可还是会面目全非的。如果你的D3D功夫不够剑豪剑圣级别，大可安心地用DrawPrimitiveUP，对付一些杂 碎三角面，也大可不必杀鸡用牛刀，用DrawPrimitiveUP剁几下就行了。另外注意测试的时候一定要用硬件模式测，软件模式的结果是完全不同的。</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.thpiano.com/?feed=rss2&#038;p=133</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>
