<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
<channel>
<title><![CDATA[我是程序员   -Shosh's Blog]]></title>
<link>http://www.wscxy.com/shosh/</link>
<description><![CDATA[]]></description>
<language>zh-cn</language>
<copyright><![CDATA[Copyright 2005 PBlog3 v2.8]]></copyright>
<webMaster><![CDATA[shosh.zhu@qisda.com(shosh)]]></webMaster>
<generator>PBlog2 v2.4</generator> 
<image>
	<title>我是程序员   -Shosh&#39;s Blog</title>
	<url>http://www.wscxy.com/shosh/images/logos.gif</url>
	<link>http://www.wscxy.com/shosh/</link>
	<description>我是程序员   -Shosh&#39;s Blog</description>
</image>

			<item>
			<link>http://www.wscxy.com/shosh/article.asp?id=102</link>
			<title><![CDATA[无法安全拔下usb设备怎么办？]]></title>
			<author>shosh.zhu@qisda.com(shosh)</author>
			<category><![CDATA[电脑综合]]></category>
			<pubDate>Thu,11 Jun 2009 00:02:02 +0800</pubDate>
			<guid>http://www.wscxy.com/shosh/default.asp?id=102</guid>
		<description><![CDATA[在windows下拔出usb设备（如u盘）的时候，经常碰到“无法止通用卷”的问题。我们知道，如果usb设备和计算机直接还有数据传输，强制拔下USB设备很有可能会损伤USB设备甚至计算机，所以我们最好能够每次都安全拔出。<br/><br/>一般在数据拷贝的时候，我们不会选择拔下USB设备。如果在确认未从USB设备拷贝文件也未往USB设备写入数据的情况下碰到“无法止通用卷”的问题，请先检查usb设备中的程序有没有仍在运行，或是usb设备中的文件仍处于打开状态，抑或是仍然开启着资源管理器（就是我的电脑窗口，打开着USB设备中的目录）。如果以上检查了都没有问题，仍然“无法止通用卷”，教你一招，一般都能奏效：<span style="color:Blue">重启资源管理器</span>。具体方法是：CTRL+ALT+DEL打开任务管理器，选择进程页，找到explorer.exe进程项，选择之再按“结束进程”按钮（或在右键菜单中选择“结束进程”菜单项），你会发现窗口底下的任务栏消失了，不过没关系，windows任务管理器还在那里，选择windows任务管理器的“文件”菜单，在弹出的菜单中选择“新建任务（运行）”，在弹出的创建新任务窗口输入explorer.exe，按确定按钮，你会发现刚才消失的任务栏又出现了。接下来再尝试拔下USB设备，一般不会再有“无法止通用卷”提示了。]]></description>
		</item>
		
			<item>
			<link>http://www.wscxy.com/shosh/article.asp?id=101</link>
			<title><![CDATA[TabWidget使用简介]]></title>
			<author>shosh.zhu@qisda.com(shosh)</author>
			<category><![CDATA[brew开发]]></category>
			<pubDate>Wed,25 Mar 2009 22:11:21 +0800</pubDate>
			<guid>http://www.wscxy.com/shosh/default.asp?id=101</guid>
		<description><![CDATA[<p>上一次为了给同仁们将 BUIW 相关的 Training，用一张图总结了 TabWidget 的结构及其简单使用方法，这张图刚好被设计成了一个房屋的形状。 BUIW中的 form, widget 都是可扩展的，所以，为了满足各自的需求，基本上每个OEM厂家都会自己在高通的 BUIW 基础上进行扩展，所以各个OEM厂商之间应该是不同的。不过因为自己扩展的和高通提供的 TabWidget 在使用方法上很相似，所以在准备 TabWidget 的时候，没有做专门的区分。</p>
<p><img alt="" src="http://www.wscxy.com/shosh/attachments/month_0903/k2009325211825.jpg" /></p>
<p>从上图可以看出，使用 TabWidget 的时候，我们一般需要通过ISHELL_CreateInstance 的 TabWidget, VecterModel, CardContainer 的实例，然后通过 IWIDGET_SetModel 来建立 TabWidget 和 VectorModel之间的联系；使用I DECORATOR_SetWidget 来建立 TabWidget 和 CardContainer 之间的联系，不过，之前我们需要通过 IWIDGET_QueryInterface (第二个参数AEEIID_DECORATOR)来查询到 TabWidget 的 IDecorator 接口指针，通过ICARDCONTAINER_QueryInterface (第二个参数AEEIID_WIDGET)来查询到 IWidget 接口指针。接下来的事情就是准备数据了，使用IVECTORMODEL_Add 或 IVECTORMODEL_InsertAt&nbsp; 等&ldquo;函数&rdquo;往 VectorModel 中插入IMageStaticInfo结构类型的数据，使用ICARDCONTAINER_Insert 往CardContainer中插入创建好的Widget（要显示的内容，content widget，多数情况下是用 container 组装起来的，在这里形象地说成一张张 Card），这一张张的Card 是和 VectorModel 中的 ModelItem 是一一对应的。</p>]]></description>
		</item>
		
			<item>
			<link>http://www.wscxy.com/shosh/article.asp?id=100</link>
			<title><![CDATA[[图]linux QQ]]></title>
			<author>shosh.zhu@qisda.com(shosh)</author>
			<category><![CDATA[电脑综合]]></category>
			<pubDate>Sun,22 Mar 2009 15:06:53 +0800</pubDate>
			<guid>http://www.wscxy.com/shosh/default.asp?id=100</guid>
		<description><![CDATA[<p>最开始的时候使用Pidgin Internet Messenger，它最厉害的地方是它可以登录到很多通信软件，其中也包括腾讯的QQ。如下图：</p>
<p><img alt="" src="http://www.wscxy.com/shosh/attachments/month_0903/e2009322144253.png" /></p>
<p>不过可惜的是，该软件只能实现简单的通信，比如登录QQ之后没有好友分组，不会显示用户备注（这样可能让你认不出他是谁来），不会下载不在线的朋友，用户的资料显示非常简单，不能看到对方是否是手机登录等，另外加载好朋友的速度也很慢，刚登录时需要耐心等待；聊天也有很多限制，比如只能发送文本消息和软件内置的一些表情图片（是该软件内置的，而非QQ内置的表情），不能发送图片（虽然有插入图片菜单，但是是disable掉的，可能是留给其他软件使用的），更没有语音视频聊天。最关键的，如果你收到消息，一点提示也没有的。</p>
<p>要聊qq，还是得使用腾讯自己开发的linux版的qq软件。有几张截图，现过目一下：</p>
<p><img alt="" src="http://www.wscxy.com/shosh/attachments/month_0903/l2009322145643.png" /><img alt="" src="http://www.wscxy.com/shosh/attachments/month_0903/j200932214574.png" /><img src="http://www.wscxy.com/shosh/attachments/month_0903/t200932217569.png" alt="" /><img alt="" src="http://www.wscxy.com/shosh/attachments/month_0903/82009322145733.png" /></p>
<p>使用Linux QQ比起Pidgin Internet Messenger要好多了，毕竟Pidgin Internet Messenger不是专门为qq设计的。个人感觉Linux QQ最大的特点是多人的聊天窗口共享一个窗口，在窗口下面通过tab页切换，不过也可以将窗口单独切出来的。但是Linux QQ的功能还是很弱的，虽然可以截图可以发送图片，但是同样没有音频视频聊天功能，也没有远程控制，另外消息提醒也不怎的，别人发来消息，你经常不会注意到。（在没有打开聊天窗口的时候，如果收到消息，QQ图表会闪烁，如果没有将Notification Area添加到桌面的panel上，就察觉不到了，没有声音提示的）</p>]]></description>
		</item>
		
			<item>
			<link>http://www.wscxy.com/shosh/article.asp?id=99</link>
			<title><![CDATA[Ubuntu学习笔记（1）]]></title>
			<author>shosh.zhu@qisda.com(shosh)</author>
			<category><![CDATA[操作系统]]></category>
			<pubDate>Fri,20 Mar 2009 23:52:30 +0800</pubDate>
			<guid>http://www.wscxy.com/shosh/default.asp?id=99</guid>
		<description><![CDATA[<ul>
    <li><b>背景：</b></li>
</ul>
<p>&nbsp;&nbsp;&nbsp; 之前使用wubi安装的方法安装了Ubuntu8.10的中文版，而在Vista系统的虚拟机上安装的是Ubuntu8.10的英文版，不过为了较好地使用Ubuntu的所有功能，尤其是高级的画面特效等，所以主要使用的还是硬盘上的Ubuntu，而非虚拟机上的。</p>
<p>&nbsp;&nbsp;&nbsp; 用了一阵Ubuntu之后，发现它其实它要比想象中的要容易使用，也被它的桌面效果、窗口特效等深深吸引。不过毕竟刚开始学习使用Ubuntu，也遇到了一些问题。一是刚开始安装的是中文版本，在中文版本的基础上也顺利装上了中文输入法，但是我希望使用英文版的Ubuntu，在成功切换回英文版后，中文输入法虽然已经启动，但是却不能使用它来输出中文了，即使重装输入法也没效果。但是在登入时如果选择汉语登入，输入法就没有问题。不过这不是主要的，我想这个问题在网上搜搜是可以找到答案的。另一个问题是设置了compizconfig settings manager中的一些设置之后，本来好好的，在看到Appearance Preferences下的Visual Effects中的三个选项（None, Normal, Extra）竟然一个都没选中，本来应该选中Extra的才对，所以我就选中它，不料屏幕的画面竟然放大了，而且光标的位置和实际的位置存在几个厘米的偏差，怎么都弄不回去了。几次重启都没有效果，所以就卸掉重新安装了一下。</p>
<p>&nbsp;&nbsp;&nbsp; 本系列日志将我配置Ubuntu的主要操作记录下来，方便以后碰到同样的问题可以快速解决，同时也可以帮助Ubuntu的初学者快速入门。</p>
<ul>
    <li><b>第一天2009-03-19</b></li>
</ul>
<p>&nbsp;&nbsp;1、在windows(vista)下安装Ubuntu8.10。这个自己去配置就好了，这一次我配置了20GB的空间给它，上次只给了10GB，感觉太少了，以后软件装多了，还要使用一定的方法重新给它分配（这也是选择重装的一个原因）。安装语言选择English。安装很快，重新启动后在配置的时候会需要几十分钟的等待。</p>
<p>&nbsp;&nbsp;2、成功进入Ubuntu桌面之后，结合上一次使用的经验，第一步做的就是选择最快的源，否则下载软件或更新的速度太慢，所以这一步很重要，不然会浪费你很多时间的。方法如下：<br />
&nbsp;&nbsp;&nbsp; 依次选择System -&gt; Administration -&gt; Software Sources<br />
&nbsp;&nbsp;&nbsp; 在弹出的Software Sources窗口的Ubuntu Software页的Source Code选择Download from &quot;Others...&quot;, 在随后弹出的Choose a Download Server窗口点击Select Best Server，系统会自动从200多个源中选择速度最快的源，在一两分钟之后便会自动选中一个源，再点击Choose Server按钮。</p>
<p>&nbsp;&nbsp;3、在选择好最快的源之后，我们就可以开始下载工作了。下载的内容主要有自动更新、中文字库和一些软件的安装等。虽然我们选择使用英文版的系统，但是我们还是要读写中文的内容的，并且要让输入法支持中文输出。这里我先做的事情就是设置语言支持，方法如下：<br />
&nbsp;&nbsp;&nbsp; 依次选择System -&gt; Administration -&gt; Language Support<br />
&nbsp;&nbsp;&nbsp; 在弹出的Language Support窗口，在Supported Languages下列出了很多语言，其中English已经默认选中了（因为我们装的是英文版），我们再手动选中Chinese，在Input method中也选中Enable support to enter complex characters复选框，再点击Apply按钮。<br />
&nbsp;&nbsp;&nbsp; 这次的下载速度平均能够达到245KB/s，四五分钟就能下载完毕，而我第一次配置的时候，没有选择最快的源，下载语言支持和自动更新都用了好几个小时，真是天壤之别呀！完成之后窗口会提示: Successfully applied all changes. You can close the window now. 按照提示关闭窗口即可。这个时候你会发现Language Support窗口的Default Language多了几个可供选择语言，其中包括Chinese(Chinese), Chinese(Hong Kong)等，不用修改它，使用默认的英语即可，因为我想使用的是英文版的。下一步安装输入法。</p>
<p>&nbsp;&nbsp;4、打开终端（依次Applications -&gt; Accessories -&gt; Terminal，一般习惯将它加到Panel去，方便启动，以为它是经常需要用到的程序，方法是在Terminal点右键选择Add this launcher to panel即可），使用下面的命令下载并安装输入法：<br />
&nbsp;&nbsp;&nbsp;&nbsp;<font color="#0000ff"><b>sudo apt-get install im-switch fcitx </b></font><br />
&nbsp;&nbsp;&nbsp; <b>命令说明：</b><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <b>sudo: </b>使用管理员的身份运行命令，运行很多命令的时候会经常用到。<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <b>apt-get install:</b>获取并安装，安装软件的时候会经常用到。<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<b>im-switch:</b> apt-get install命令的参数，是输入法切换软件，安装了它以后，我们可以使用im-switch命令来设置输入法。<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <b>fcitx:</b>也是apt-get install命令的参数，在这里和im-switch是并列关系，它是我们选择安装的输入法。<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <b>整句话的意思：</b>用管理员的权限下载并安装im-switch和fcitx两个软件。<br />
&nbsp;&nbsp;&nbsp; <b>小插曲：</b>最开始使用该命令的时候，忘记了使用sudo，会有如下提示：<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <font color="#0000ff">Could not open lock file /var/lib/dpkg/lock - open (13 Permission denied)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Unable to lock the administration directory (/var/lib/dpkg/), are you root?<br />
&nbsp;</font>&nbsp;&nbsp;&nbsp;然后回车，会要求输入密码，输入账户的密码即可自动下载和安装了。提示一下，在终端输入密码的时候，屏幕上是不会出现输入的字符，连***等替换字符都不会出现，所以不要以为按键无效哦！我第一次使用终端输入密码的时候，就认为键盘无效或屏幕了我的输入，折腾了大半天。</p>
<p>&nbsp;&nbsp;5、安装完输入法还不可以直接使用，需要设置输入法，分别使用如下两条命令：<br />
&nbsp;&nbsp;&nbsp;&nbsp;<font color="#0000ff"><b>im-switch -s fcitx -z default<br />
</b></font>&nbsp;&nbsp;&nbsp;&nbsp;<font color="#0000ff"><b>sudo im-switch -s fcitx -z default<br />
</b></font><br />
&nbsp;&nbsp;&nbsp; 简单介绍以下im-switch命令，改名来就是用来设置或查看输入法的，-s指定输入法的名称，-z指定语言，这里选择了默认（如果重启之后都不能输出中文，可以将default改成zh_CN试试），还有可以使用-l来查看当前的设置。请使用<b><font color="#0000ff">im-switch -h</font></b>来查看详细的使用说明。如果命令运行结束后不能使用Ctrl+Space切换出中文输入法，请重启试试。</p>
<p>&nbsp;&nbsp;6、安装更新。第一次安装或换源之后往往会有更新提示，如果已经在面板显示了Notification Area，可以点击更新的那个icon进行自动更新。也可以依次选择System -&gt; Administration -&gt; Update Manager来打开更新窗口，如果没有提示Your system is up-to-date，可以点击Install Updates来下载并安装更新。这个自动化很高，让它自己下载安装好了，因为我们已经选择了速度最快的源，所以几十分钟就可以完成的。完成之后可能需要重新启动。</p>
<p>&nbsp;&nbsp;7、使用视觉效果。该功在虚拟机下不能使用，并且需要显卡驱动的支持。依次选择System -&gt; -&gt; Preferences -&gt; Appearance,在弹出的Appearance Preferences窗口选择Visual Effects页，选择Extra，这个时候系统会去检测，如我的笔记本的显卡为NV GeForce 9600M GS, 会提示它的驱动受限，并弹出窗口允许我激活驱动（提供了两个，随便选择一个，不同的显卡会不一样），该窗口同样可以通过依次选择System -&gt; Administration -&gt; Hardware Drivers来打开。至于为什么某些驱动受限，在帮助文档中又解答：Restricted drivers are drivers for your hardware which are not freely available or open-source...usually because the hardware manufacturer has not release details of their hardware which would make it possible to create such a driver. 也就是说你的硬件不能自由获得或不是开源的，通常是因为硬件制造商没有发布他们的硬件细节导致（给ubuntu写驱动的人）无法为这些硬件开发驱动程序。设置好Extra的Visual Effects之后就已经具备了一些效果了，比如在桌面空白处可以通过滚动鼠标中键切换视口，使用Super(Windows)+Tab开切换窗口等，移动的窗口会变形、在边界处会停靠等。<br />
&nbsp;&nbsp;&nbsp; 更多的视觉效果还可以安装compizconfig settings manager，然后使用它来设置。安装的方法是依次选择System -&gt; Administration -&gt; Synaptic Package Manager，在弹出的Synaptic Package Manager窗口选择菜单Edit -&gt; Search...，然后在弹出的Find窗口的Search后面输入Compizconfig再点Search按钮，然后Synaptic Package Manager窗口的列表中找到compizconfig-backend-gconf，然后点击工具栏的Apply，如果弹出需要同时选择其他Package的话也同时选中，如果没有就OK了，它会自动完成下载安装的。完成之后依次选择System -&gt; Preferences -&gt; CompizConfig Settings Manager，在打开的窗口有很多可以设置的，你自己慢慢设吧。我测试这些效果弄了大半天，现在决定不去弄它了，也不在这里具体写了。如果有需要，可以自己玩玩或到网上找找相关资料。（我想玩玩KDE，那个似乎好看很多。以后会继续写这方面的文章。）</p>
<p>　</p>]]></description>
		</item>
		
			<item>
			<link>http://www.wscxy.com/shosh/article.asp?id=98</link>
			<title><![CDATA[初学Linux, 调整Ubuntu在VirtualBox下的分辨率]]></title>
			<author>shosh.zhu@qisda.com(shosh)</author>
			<category><![CDATA[操作系统]]></category>
			<pubDate>Mon,16 Mar 2009 22:28:29 +0800</pubDate>
			<guid>http://www.wscxy.com/shosh/default.asp?id=98</guid>
		<description><![CDATA[初学Linux，选择的是现今比较流行也比较容易上手的ubuntu8.10。版本号8.10是指08年10月的版本的意思，而不是我之前所认为的大版本号为8，小版本号为10。ubuntu每年会release两个版本，其中下半年的版本都是在10月份发布的。<br/><br/>我先是直接在windows vista下直接安装了ubuntu，采用的是傻瓜式的安装。安装后要求重新启动，然后ubuntu会进行较长时间的配置。进度条到达82%的时候基本上就像卡住了，很难前进，需要耐心等待。因为我安装选择的是简体中文版，所以过了82%之后还会自动下载语言包，发现下载速度实在太慢，所以将下载语言包的步骤取消了（幸亏提供了取消按钮）。<br/><br/>因为系统也刚安装上，所以刚进入ubuntu很多功能都还不能够使用，比如不能播放音频视频文件等。所以还是需要在windows下做很多事情，边在windows下打开学习资料学习，边在ubuntu下实践练习。所以又用VirtualBox装了个英文版ubuntu系统（因为已经装了简体中文版的了，所以再装一个英文版的），在该虚拟机上运行之。<br/><br/>据说傻瓜式地在windows下安装ubuntu和在裸机上直接安装ubuntu除了文件系统以外，其它都是一样的（在windows下像安装一般windows下的应用程序一样安装的ubuntu使用的是windows的文件系统，而非Linux本身的文件系统，其他功能都是一样的）；但是使用虚拟机和不使用虚拟机来运行的ubuntu就有很大的差异了。<br/><br/>现在要解决的是分辨率的问题。我的本本是15.4英寸的IdeaPadY530，正常使用的分辨率是1280*800，直接运行的ubuntu的分辨率也可以设置成1280*800，但是虚拟机下的ubuntu的分辨率只有两种选择，分别是640*480和800*600，也就是说最高也就只能设置成800*600，这样的分辨率未免太低了。而且还有一个很有趣的是，屏幕刷新屏幕是61Hz，且只有这一种选择。我的电脑的正常屏幕刷新频率是60Hz，它61Hz从数据的大小上倒也符合，只是为什么会多一个零头1来呢？<br/><br/>昨天晚上一直想把屏幕分辨率调整得大一些，至少也要到1024*768，在网上找了很多资料没有成功。今天晚上终于把分辨率调整到1280*800了。方法如下：<br/><br/>不要让ubuntu的运行窗口全屏（默认情况下使用“右边的”Ctrl键 + F键），然后选择设备菜单，依次“分配光驱”，“虚拟光驱”，在“虚拟光驱”Tab页会列出一些光驱映像文件，其中一个为VBoxGuestAdditions.iso，将光标移至其上，点右键，在弹出的菜单中选择“释放”，然后按“选择”按钮关闭当前窗口。这时就可以在ubuntu的桌面上可以看到VBOXADDITIONS_2.1.0_4114的光驱（名字根据VirtualBox的版本会略有不同），然后打开ubuntu的Applications菜单，在Accessories下选择Terminal，打开Terminal我们就可以输入linux命令了。<br/><br/>输入如下命令：<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style="color:Blue"><strong>sudo</strong> sh /media/cdrom0/VBoxLinuxAdditions-x86.run</span><br/>然后回车，要求你输入用户密码，你输入密码再回车即可（有点让人不习惯的是，在该窗口输入密码在屏幕上是不会有任何显示的，就连***都不会出现，会给人按键无效的错觉，记得我昨天晚上就在这上面花费了不少时间，一直不明白怎么将密码正确输入，今天晚上才明白过来）。<br/><br/>上面的命令的后面指定的是要打开的文件的完整路径，如果提示文件找不到，可以手动打开光驱看里面的文件，看文件名是否和上面的命令中的一致，如果不一致，请使用光驱中的文件名（提到这一点的原因是：我在网上找的命令的文件名是VBoxLinuxAdditions.run，可能用的VirtualBox版本不一致导致的）。与文件VBoxLinuxAdditions-x86.run相对应的还有一个文件，名为VBoxLinuxAdditions-arm64.run，很明显，它是64位系统下的。如果你装的是64位的Ubuntu，应该使用这个。<br/><br/>完成之后再重新启动一下Ubuntu，就会发现分辨率调高了，在全屏模式下我的电脑的分辨率自动调整到了1280*800，打开Screen Resolution可以看到分辨率调整选项里多了1280*800和1024*768的选项，而Refresh Rate也变成了60Hz而不是之前奇怪的61Hz。不过如果按Ctrl(右)+F退出全屏，分辨率会自动调整回800*600，即使打开Screen Resolution也不会看到1280*800和1024*768的选项，不过Refresh Rate不会变到61Hz去。<br/><br/>继续学习，得先解决音频视频文件的播放的问题了。]]></description>
		</item>
		
			<item>
			<link>http://www.wscxy.com/shosh/article.asp?id=97</link>
			<title><![CDATA[软件安装流水账]]></title>
			<author>shosh.zhu@qisda.com(shosh)</author>
			<category><![CDATA[电脑综合]]></category>
			<pubDate>Sun,15 Mar 2009 01:13:15 +0800</pubDate>
			<guid>http://www.wscxy.com/shosh/default.asp?id=97</guid>
		<description><![CDATA[2009年3月11-12号 周三 - 四<br/>安装几天前下载好的Visual Studio 2008，选择完全安装。虽然安装在H盘（我H盘主要用来安装一般软件），不过VS里还是有很多东西是需要安装到系统盘的。我眼睁睁看着我的系统盘剩余的空间逐渐减少，最后只剩下1.3GB左右。虽然系统盘有30GB，不过Vista就吃盘，虽然系统刚装上的时候，系统盘的剩余空间有10多GB，但是日积月累，系统盘的空间还是会在不知不觉中减少！<br/>删除掉一些系统盘里无用的文件，不过这也只节约下几百MB的空间而已。按照网上搜索的资料，狠一下心，关闭了系统还原功能（系统还原功能：系统每天都会创建还原点，还有在发生显著的系统事件如安装程序或驱动等之前也会创建还原点。每次创建还原点，都会占用一定的硬盘空间，且之前的还原点不会自动删除，除非空间不足，系统才会帮忙将古老的还原点删除），也把虚拟内存（虚拟内存：把一部分硬盘空间当做内存使用，虽然硬盘的访问速度比内存的访问速度慢很多，但是它很够很好地解决内存不足的问题，其实虚拟内存就是文件，叫做页面文件pagefile。）给关闭了，因为我的计算机的物理内存为4GB，虽然系统能够正常使用的只有2.5GB多一点，但是想象不用虚拟内存也是够用的。这样一来，在重新启动电脑以后，发现我的系统盘剩余可用空间变成了6.8GB，看来非常有效。不过发现反正已经又有了这么多的可用空间，所以重新打开了系统还原功能，以防不测。<br/><br/>2009年3月13-14号 周五 - 六<br/>学习自己动手搭建版本控制环境，公司里用的Changeman Version Manager是Lock-Modify-Unlock（锁住-修改-解锁）解决方案的，这一次我学习使用的是Subversion（简称svn），它的解决方案是Copy-Modify-Merge（复制-修改-整合）。第一次玩svn，发现这东西不怎么好学，走了些弯路。先是下载了svn-python-1.5.6.win32-py2.5.exe，安装的时候发现需要python2.5的支持，于是去下载python，发现python的最新的稳定版本是3.0.1，于是就下了这个版本来安装后，安装完毕后再执行svn-python-1.5.6.win32-py2.5.exe，发现它还是提示没有找到python2.5，看来它只认2.5版本的python。所以又去网上下了python2.5，安装完毕后再安装svn-python-1.5.6.win32-py2.5.exe，貌似它是安装进python2.5的安装目录的，不过去python2.5的安装目录看，却并没有什么新的文件安装进来。后来在网上看到说svn只是个zip压缩包，都不需要安装，只要解压就可以用了的，于是又去找，最后下了svn-win32-1.5.4.zip。按照介绍，先下载安装了TortoiseSVN（TortoiseSVN 是一个客户端程序，用来与 subvers 服务器端通讯。Subversion 自带一个客户端程序 svn.exe,但 TortoiseSVN 更好操作，提高效率。），这个比较顺利，然后解压svn-win32-1.5.4.zip。不过我安装的不是ApacheSVN 服务器，而是SVNServer，本机用用是够的。然后测试了一下check in,check out等功能。<br/><br/>今天下午使用QQLive看视频的时候，竟然弹出提示说内存不足，建议我将某些软件关闭，其中也包括QQLive。没想到没有虚拟内存，有2.5GB的物理内存也有不够用的情况。想想还是使用原来的用过的方法，将多余的内存当成虚拟内存来用，这样既不必占用硬盘空间，又可以提高虚拟内存的读写速度。<br/><br/>另外今天晚上用iis在本机开通了网站，网站的数据就是从本站下载过去的。设置了宿舍里的路由，使外网用户可以通过路由的ip访问到我的电脑上的网站。因为每次路由的启动都会重新分配ip地址，所以下载安装了花生壳2008，当我的电脑开启在线的时候，外网的朋友可以通过 <a href="http://Shosh.gicp.net" target="_blank" rel="external">http://Shosh.gicp.net</a> 访问的我电脑上的网站。<br/>]]></description>
		</item>
		
			<item>
			<link>http://www.wscxy.com/shosh/article.asp?id=96</link>
			<title><![CDATA[如何让程序开发又快又好]]></title>
			<author>shosh.zhu@qisda.com(shosh)</author>
			<category><![CDATA[程序开发]]></category>
			<pubDate>Thu,12 Mar 2009 11:49:06 +0800</pubDate>
			<guid>http://www.wscxy.com/shosh/default.asp?id=96</guid>
		<description><![CDATA[<p>注意代码的质量，尽量在设计的时候就能够站在整体的高度来找到一条简便可行的方向，在编写的时候注意检查改进，可以节约很多成本。下文转载自：&nbsp;<br />
<br />
文章出处：http://www.limodev.cn/blog&nbsp;作者联系方式：李先静&nbsp;&lt;xianjimli&nbsp;at&nbsp;hotmail&nbsp;dot&nbsp;com&gt;&nbsp;<br />
<br />
<font color="#4b0082">-----------------------------------</font><br />
<br />
&ldquo;快&rdquo;是指开发效率高，&ldquo;好&rdquo;是指软件质量高。呵呵，写得又快又好的人就是高手了。记得这是林锐博士下的定义，读他那篇著名的《C<font color="#4b0082">/</font>C<font color="#4b0082">++</font>高质量编程》时，我还是个初学者，印象特别深。我现在仍然赞同他的观点，不过这里标题改为成为高手的秘诀，感觉就有点像标题党了，所以还是用比较通俗的说法吧。废话少说，请读者回顾一下这段时间的编程经验，回答下面两个问题：&nbsp;<br />
<br />
<font color="#ff0000">1</font><font color="#4b0082">.</font>快与好是什么关系？写得快就不能写得好？写得好就不能写得快？还是写得好才能写得快？是不是绕晕了？不过这确实是值得思考的问题。&nbsp;<br />
<br />
<font color="#ff0000">2</font><font color="#4b0082">.</font>我们的时间花在哪里了？记得刚来深圳时到华为面试，面试的人是我的学长。他问我，你一天能写多少行代码？我想了想说，<font color="#ff0000">100</font>行吧。他用看外行的眼光看着我说，能写<font color="#ff0000">100</font>行吗？我知道说错话了，赶快补充说，嗯，从整个项目来看可能没有吧。他才点了点头。一天只写<font color="#ff0000">100</font>行代码？初学者可能觉得不可思议，以同时应付<font color="#ff0000">10</font>个网友聊天的速度，写<font color="#ff0000">100</font>行代码不用三分钟。不过，经过这段时间的练习后，我们想大家已经明白，敲代码不是花时间最多的地方，那时间又花到哪里去了呢？&nbsp;<br />
<br />
<font color="#4b0082">-----------------------------------</font><br />
<br />
代码阅读法&nbsp;<br />
<br />
软件工程实践已经证明Code&nbsp;Review是提高代码质量最有效的手段之一，极限编程<font color="#800080"><b>(</b></font>XP<font color="#800080"><b>)</b></font>更是把Code&nbsp;Review推向极致，形成著名的结对编程工作方式，两个程序员在一台电脑前面工作，一个人编写程序，另一个Review输入每一行代码，写程序人的专注于目前细节上的工作，Review的人同时要从高层次考虑如何改进代码质量，两个人的角色会经常互换。&nbsp;<br />
<br />
可惜我即没有结对编程的经验，也没有在CMM3<font color="#800080"><b>(</b></font>及以上<font color="#800080"><b>)</b></font>团队中工作过。不过现在我要介绍比结对编程更敏捷更轻量级，但是同样有效的Review方法。这种方法不需要其他程序员配合，有你自己就够了。为了把这种方法与传统的Code&nbsp;Review区分开来，我把它称为代码阅读法吧。&nbsp;<br />
<br />
很多初学者包括一些有经验的程序员，在敲完代码的最后一个字符后，马上开始编译和运行，迫不急待的想看到自己的工作成果。快速反馈有助于满足自己的成就感，但是同时也会带来一些问题：&nbsp;<br />
<br />
让编译器帮你检查语法错误可以省些时间，但程序员往往太专注这些错误了，以为改完这些错误就万事大吉了。其实不然，很多错误编译器是发现不了的，像内存错误和线程死锁等等，这些错误可能逃过简单的测试而遗留在代码中，直到集成测试或者软件发布之后才暴露出来，那时就要花更大代价去修改它们了。&nbsp;<br />
<br />
修改完编译错误之后就是运行程序了，运行起来有错误，就轮到调试器上场了。花了不少时间去调试，发现无非是些低级错误，或许你会自责自己粗心大意，但是下次可能还是犯同样的错误。更严重的是这种debug&nbsp;<font color="#4b0082">&amp;</font>&nbsp;fix的方法，往往是头痛医头脚痛医脚，导致低质量的软件。&nbsp;<br />
<br />
让编译器帮你检查语法错误，让调试器帮你查BUG，这是天经地义的事，但这确实是又慢又烂的方法。就像你要到离家东边<font color="#ff0000">1000</font>米的地方开会，结果你往西边走，又是坐车又是搭飞机，花了一周时间，也绕着地球转了一周，终于到了会议室，你还大发感慨说，现代的交通工具真是发达啊。其实你往东走，走路也只要十多分钟就到了。不管你的调试技巧有多高，都不如一次性写好更高效。&nbsp;<br />
<br />
我以前也一样，想赶时间结果花了更多时间，在经过很多痛苦的经历之后，我开始学会放松自己，让自己慢下来。写完程序之后，我会花些时间去阅读它，一遍两遍甚至多遍之后，才开始编译它，只要有时间，在通过测试之后，我还会阅读它们，每读一遍都有不同的收获，有时候会发现一些错误，有时候会做些改进，有时候也有新的想法。&nbsp;<br />
<br />
下面是我在阅读自己代码时的一些方法：&nbsp;<br />
<br />
o检查常见错误。&nbsp;<br />
<br />
第一遍阅读时主要关注语法错误、代码排版和命名规则等等问题，只要看不顺眼就修改它们。读完之后，你的代码很少有低级错误，看起来也比较干净清爽。第二遍重点关注常见编程错误，比如内存泄露和可能的越界访问，变量没有初始化，函数忘记返回值等等，在后面的章节中，我会介绍这些常见错误，避免这些错误可以为你省大量的时间。如果有时间，在测试完成之后，还可以考虑是否有更好的实现方法，甚至尝试重新去实现它们。说了读者可能不相信，在学习编程的前几年，我经常重写整个模块，只我觉得能做得更好，能验证我的一些想法，或提高我的编程能力，即使连续几天加班到晚上十一点，我也要重写它们。&nbsp;<br />
<br />
o模拟计算机执行。&nbsp;<br />
<br />
常见错误是比较死的东西，按照检查列表一条一条的做就行了。有些逻辑通常不是这么直观的，这时可以自己模拟计算机去执行，假想你自己是计算机，读入这些代码时你会怎么处理。这种方法能有效的完善我们的思路，考虑不同的输入数据，各种边界值，这能帮助我们想到一些没有处理的情况，让程序的逻辑更严谨。&nbsp;<br />
<br />
o假想讲给朋友听。&nbsp;<br />
<br />
据说在Code&nbsp;Review时发现错误的，往往不是Review的人而是程序员自己。我也有很多这样的经历，在讲给别人听的时候，别人还没有听明白，自己已经发现里面存在的错误了。上大学时，我常常把写的或者学到的东西讲给隔壁寝室的一个同学听，他说他从我这里学到很多知识，其实我从讲的过程中，经常发现一些问题，对提高自己的能力大有帮助。可惜并不是随时都能找到好的听众，幸好我们有另外一个替代办法，记得刚开始写程序时看过一本书<font color="#800080"><b>(</b></font>忘记名字了<font color="#800080"><b>)</b></font>，作者说他在写程序时，常常把思路讲给他的布娃娃听。我没有布娃娃当听众，讲给鼠标听总是有点怪怪的，所以就假想旁边有个朋友，我把自己的思路讲给他听，同时也假想他来质疑我。这种方法很效，能够让自己的思路更清晰，据说一些大师也经常使用这种方法。&nbsp;<br />
<br />
这种代码阅读法会花你一些时间，但是可以省下更多调试时间，而且能够提高代码质量，可以说是名符其实的&ldquo;又快又好的&rdquo;&nbsp;秘诀之一。至于读几遍合适，要根据情况而定，个人觉得读两到三遍是最佳的投资。&nbsp;<br />
<br />
<font color="#4b0082">-----------------------------------</font><br />
<br />
避免常见错误&nbsp;<br />
<br />
在C语言中，内存错误是最为人诟病的。这些错误让项目延期或者被取消，引发无数的安全问题，甚至出现人命关天的灾难。抛开这些大道理不谈，它们确实浪费了我们大量时间，这些错误引发的是随机现象，即使有一些先进工具的帮助，为了找到重现的路径，花上几天时间也不足为怪。如果能够在编写代码的时候避免这些错误，开发效率至少提高一倍以上，质量可以提高几倍了。这里列举一些常见的内存错误，供新手参考。&nbsp;<br />
<br />
o&nbsp;内存泄露&nbsp;<br />
<br />
大家都知道，在堆上分配的内存，如果不再使用了，应该把它释放掉，以便后面其它地方可以重用。在C<font color="#4b0082">/</font>C<font color="#4b0082">++</font>中，内存管理器不会帮你自动回收不再使用的内存。如果你忘了释放不再使用的内存，这些内存就不能被重用了，这就造成了所谓的内存泄露。&nbsp;<br />
<br />
把内存泄露列为首位，倒并不是因为它有多么严重的后果，而因为它是最为常见的一类错误。一两处内存泄露通常不至于让程序崩溃，也不会出现逻辑上的错误，加上进程退出时，系统会自动释放该进程所有相关的内存<font color="#800080"><b>(</b></font>共享内存除外<font color="#800080"><b>)</b></font>，所以内存泄露的后果相对来说还是比较温和的。但是，量变会导致质变，一旦内存泄露过多以致于耗尽内存，后续内存分配将会失败，程序可能因此而崩溃。&nbsp;<br />
<br />
现在PC机的内存够大了，加上进程有独立的内存空间，对于一些小程序来说，内存泄露已经不是太大的威胁。但对于大型软件，特别是长时间运行的软件，或者嵌入式系统来说，内存泄露仍然是致命的因素之一。&nbsp;<br />
<br />
不管在什么情况下，采取谨慎的态度，杜绝内存泄露的出现，都是可取的。相反，认为内存有的是，对内存泄露放任自流都不是负责的。尽管一些工具可以帮助我们检查内存泄露问题，我认为还是应该在编程时就仔细一点，及早排除这类错误，工具只是用作验证的手段。&nbsp;<br />
<br />
o&nbsp;内存越界访问&nbsp;<br />
<br />
内存越界访问有两种：一种是读越界，即读了不属于自己的数据，如果所读的内存地址是无效的，程度立刻就崩溃了。如果所读内存地址是有效的，在读的时候不会出问题，但由于读到的数据是随机的，它会产生不可预料的后果。另外一种是写越界，又叫缓冲区溢出，所写入的数据对别人来说是随机的，它也会产生不可预料的后果。&nbsp;<br />
<br />
内存越界访问造成的后果非常严重，是程序稳定性的致命威胁之一。更麻烦的是，它造成的后果是随机的，表现出来的症状和时机也是随机的，让BUG的现象和本质看似没有什么联系，这给BUG的定位带来极大的困难。&nbsp;<br />
<br />
一些工具可以够帮助检查内存越界访问的问题，但也不能太依赖于工具。内存越界访问通常是动态出现的，即依赖于测试数据，在极端的情况下才会出现，除非精心设计测试数据，工具也无能为力。工具本身也有一些限制，甚至在一些大型项目中，工具变得完全不可用。比较保险的方法还是在编程是就小心，特别是对于外部传入的参数要仔细检查。&nbsp;<br />
<br />
我们来看一个例子：&nbsp;<br />
<br />
<font color="#0000ff">#include</font>&nbsp;<font color="#4b0082">&lt;</font>stdlib<font color="#4b0082">.</font>h<font color="#4b0082">&gt;</font><br />
<font color="#0000ff">#include</font>&nbsp;<font color="#4b0082">&lt;</font>string<font color="#4b0082">.</font>h<font color="#4b0082">&gt;</font><br />
&nbsp;<br />
<font color="#0000ff">int</font>&nbsp;main<font color="#800080"><b>(</b></font><font color="#0000ff">int</font>&nbsp;argc<font color="#4b0082">,</font>&nbsp;<font color="#0000ff">char</font><font color="#4b0082">*</font>&nbsp;argv<font color="#800080"><b>[])</b></font><br />
<font color="#800080"><b>{</b></font><br />
&nbsp;&nbsp;&nbsp;&nbsp;<font color="#0000ff">char</font>&nbsp;str<font color="#800080"><b>[</b></font><font color="#ff0000">10</font><font color="#800080"><b>]</b></font><font color="#4b0082">;</font><br />
&nbsp;&nbsp;&nbsp;&nbsp;<font color="#0000ff">int</font>&nbsp;array<font color="#800080"><b>[</b></font><font color="#ff0000">10</font><font color="#800080"><b>]</b></font>&nbsp;<font color="#4b0082">=</font>&nbsp;<font color="#800080"><b>{</b></font><font color="#ff0000">0</font><font color="#4b0082">,</font><font color="#ff0000">1</font><font color="#4b0082">,</font><font color="#ff0000">2</font><font color="#4b0082">,</font><font color="#ff0000">3</font><font color="#4b0082">,</font><font color="#ff0000">4</font><font color="#4b0082">,</font><font color="#ff0000">5</font><font color="#4b0082">,</font><font color="#ff0000">6</font><font color="#4b0082">,</font><font color="#ff0000">7</font><font color="#4b0082">,</font><font color="#ff0000">8</font><font color="#4b0082">,</font><font color="#ff0000">9</font><font color="#800080"><b>}</b></font><font color="#4b0082">;</font><br />
&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;<font color="#0000ff">int</font>&nbsp;data&nbsp;<font color="#4b0082">=</font>&nbsp;array<font color="#800080"><b>[</b></font><font color="#ff0000">10</font><font color="#800080"><b>]</b></font><font color="#4b0082">;</font><br />
&nbsp;&nbsp;&nbsp;&nbsp;array<font color="#800080"><b>[</b></font><font color="#ff0000">10</font><font color="#800080"><b>]</b></font>&nbsp;<font color="#4b0082">=</font>&nbsp;data<font color="#4b0082">;</font><br />
&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;<font color="#0000ff">if</font><font color="#800080"><b>(</b></font>argc&nbsp;<font color="#4b0082">==</font>&nbsp;<font color="#ff0000">2</font><font color="#800080"><b>)</b></font><br />
&nbsp;&nbsp;&nbsp;&nbsp;<font color="#800080"><b>{</b></font><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;strcpy<font color="#800080"><b>(</b></font>str<font color="#4b0082">,</font>&nbsp;argv<font color="#800080"><b>[</b></font><font color="#ff0000">1</font><font color="#800080"><b>])</b></font><font color="#4b0082">;</font><br />
&nbsp;&nbsp;&nbsp;&nbsp;<font color="#800080"><b>}</b></font><br />
&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;<font color="#0000ff">return</font>&nbsp;<font color="#ff0000">0</font><font color="#4b0082">;</font><br />
<font color="#800080"><b>}</b></font><br />
这个例子中有两个错误是新手常犯的：&nbsp;<br />
<br />
其一：<font color="#0000ff">int</font>&nbsp;array<font color="#800080"><b>[</b></font><font color="#ff0000">10</font><font color="#800080"><b>]</b></font>&nbsp;定义了<font color="#ff0000">10</font>个元素大小的数组，由于C语言中数组的索引是从<font color="#ff0000">0</font>开始的，所以只能访问array<font color="#800080"><b>[</b></font><font color="#ff0000">0</font><font color="#800080"><b>]</b></font>到array<font color="#800080"><b>[</b></font><font color="#ff0000">9</font><font color="#800080"><b>]</b></font>，访问array<font color="#800080"><b>[</b></font><font color="#ff0000">10</font><font color="#800080"><b>]</b></font>就造成了越界错误。&nbsp;<br />
<br />
其二：strcpy<font color="#800080"><b>(</b></font>str<font color="#4b0082">,</font>&nbsp;argv<font color="#800080"><b>[</b></font><font color="#ff0000">1</font><font color="#800080"><b>])</b></font><font color="#4b0082">;</font>这里是否存在越界错误依赖于外部输入的数据，这样的写法在正常下可能没有问题，但受到一点恶意攻击就完蛋了。除非你确定输入数据是在你控制内的，否则不要用strcpy、strcat和sprintf之类的函数，而要用strncpy、strncat和snprintf代替。&nbsp;<br />
<br />
o&nbsp;野指针。&nbsp;<br />
<br />
野指针是指那些你已经释放掉的内存指针。当你调用free<font color="#800080"><b>(</b></font>p<font color="#800080"><b>)</b></font>时，你真正清楚这个动作背后的内容吗？你会说p指向的内存被释放了。没错，p本身有变化吗？答案是p本身没有变化。它指向的内存仍然是有效的，你继续读写p指向的内存，没有人能拦得住你。&nbsp;<br />
<br />
释放掉的内存会被内存管理器重新分配，此时，野指针指向的内存已经被赋予新的意义。对野指针指向内存的访问，无论是有意还是无意的，都为此会付出巨大代价，因为它造成的后果，如同越界访问一样是不可预料的。&nbsp;<br />
<br />
释放内存后立即把对应指针置为空值，这是避免野指针常用的方法。这个方法简单有效，只是要注意，当然指针是从函数外层传入的时，在函数内把指针置为空值，对外层的指针没有影响。比如，你在析构函数里把<font color="#0000ff">this</font>指针置为空值，没有任何效果，这时应该在函数外层把指针置为空值。&nbsp;<br />
<br />
o&nbsp;访问空指针。&nbsp;<br />
<br />
空指针在C<font color="#4b0082">/</font>C<font color="#4b0082">++</font>中占有特殊的地址，通常用来判断一个指针的有效性。空指针一般定义为<font color="#ff0000">0</font>。现代操作系统都会保留从<font color="#ff0000">0</font>开始的一块内存，至于这块内存有多大，视不同的操作系统而定。一旦程序试图访问这块内存，系统就会触发一个异常<font color="#4b0082">/</font>信号。&nbsp;<br />
<br />
操作系统为什么要保留一块内存，而不是仅仅保留一个字节的内存呢？原因是：一般内存管理都是按页进行管理的，无法单纯保留一个字节，至少要保留一个页面。保留一块内存也有额外的好处，可以检查诸如p<font color="#4b0082">=</font>NULL<font color="#4b0082">;</font>&nbsp;p<font color="#800080"><b>[</b></font><font color="#ff0000">1</font><font color="#800080"><b>]</b></font>之类的内存错误。&nbsp;<br />
<br />
在一些嵌入式系统<font color="#800080"><b>(</b></font>如arm7<font color="#800080"><b>)</b></font>中，从<font color="#ff0000">0</font>开始的一块内存是用来安装中断向量的，没有MMU的保护，直接访问这块内存好像不会引发异常。不过这块内存是代码段的，不是程序中有效的变量地址，所以用空指针来判断指针的有效性仍然可行。&nbsp;<br />
<br />
o&nbsp;引用未初始化的变量。&nbsp;<br />
<br />
未初始化变量的内容是随机的<font color="#800080"><b>(</b></font>有的编译器会在调试版本中把它们初始化为固定值，如0xcc<font color="#800080"><b>)</b></font>，使用这些数据会造成不可预料的后果，调试这样的BUG也是非常困难的。&nbsp;<br />
<br />
对于态度严谨的程度员来说，防止这类BUG非常容易。在声明变量时就对它进行初始化，是一个好的编程习惯。另外也要重视编译器的警告信息，发现有引用未初始化的变量，立即修改过来。&nbsp;<br />
<br />
在下面这个例子中，全局变量g_count是确定的，因为它在bss段中，自动初始化为<font color="#ff0000">0</font>了。临时变量a是没有初始化的，堆内存str是没有初始化的。但这个例子有点特殊，因为程序刚运行起来，很多东西是确定的，如果你想把它们当作随机数的种子是不行的，因为它们还不够随机。&nbsp;<br />
<br />
<font color="#0000ff">#include</font>&nbsp;<font color="#4b0082">&lt;</font>stdlib<font color="#4b0082">.</font>h<font color="#4b0082">&gt;</font><br />
<font color="#0000ff">#include</font>&nbsp;<font color="#4b0082">&lt;</font>string<font color="#4b0082">.</font>h<font color="#4b0082">&gt;</font><br />
&nbsp;<br />
<font color="#0000ff">int</font>&nbsp;g_count<font color="#4b0082">;</font><br />
&nbsp;<br />
<font color="#0000ff">int</font>&nbsp;main<font color="#800080"><b>(</b></font><font color="#0000ff">int</font>&nbsp;argc<font color="#4b0082">,</font>&nbsp;<font color="#0000ff">char</font><font color="#4b0082">*</font>&nbsp;argv<font color="#800080"><b>[])</b></font><br />
<font color="#800080"><b>{</b></font><br />
&nbsp;&nbsp;&nbsp;&nbsp;<font color="#0000ff">int</font>&nbsp;a<font color="#4b0082">;</font><br />
&nbsp;&nbsp;&nbsp;&nbsp;<font color="#0000ff">char</font><font color="#4b0082">*</font>&nbsp;str&nbsp;<font color="#4b0082">=</font>&nbsp;<font color="#800080"><b>(</b></font><font color="#0000ff">char</font><font color="#4b0082">*</font><font color="#800080"><b>)</b></font>malloc<font color="#800080"><b>(</b></font><font color="#ff0000">100</font><font color="#800080"><b>)</b></font><font color="#4b0082">;</font><br />
&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;<font color="#0000ff">return</font>&nbsp;<font color="#ff0000">0</font><font color="#4b0082">;</font><br />
<font color="#800080"><b>}</b></font><br />
<br />
o&nbsp;不清楚指针运算。&nbsp;<br />
<br />
对于一些新手来说，指针常常让他们犯糊涂。&nbsp;<br />
<br />
比如<font color="#0000ff">int</font>&nbsp;<font color="#4b0082">*</font>p&nbsp;<font color="#4b0082">=</font>&nbsp;&hellip;<font color="#4b0082">;</font>&nbsp;p<font color="#4b0082">+</font><font color="#ff0000">1</font>等于<font color="#800080"><b>(</b></font>size_t<font color="#800080"><b>)</b></font>p&nbsp;<font color="#4b0082">+</font>&nbsp;<font color="#ff0000">1</font>吗&nbsp;<br />
<br />
老手自然清楚，新手可能就搞不清了。事实上<font color="#4b0082">,</font>&nbsp;p<font color="#4b0082">+</font>n&nbsp;等于&nbsp;<font color="#800080"><b>(</b></font>size_t<font color="#800080"><b>)</b></font>p&nbsp;<font color="#4b0082">+</font>&nbsp;n&nbsp;<font color="#4b0082">*</font>&nbsp;<font color="#0000ff">sizeof</font><font color="#800080"><b>(</b></font><font color="#4b0082">*</font>p<font color="#800080"><b>)</b></font>&nbsp;<br />
<br />
指针是C<font color="#4b0082">/</font>C<font color="#4b0082">++</font>中最有力的武器，功能非常强大，无论是变量指针还是函数指针，都应该非常熟练的掌握。只要有不确定的地方，马上写个小程序验证一下。对每一个细节了然于胸，在编程时会省下不少时间。&nbsp;<br />
<br />
o&nbsp;结构的成员顺序变化引发的错误。&nbsp;<br />
<br />
在初始化一个结构时，老手可能很少像新手那样老老实实的，一个成员一个成员的为结构初始化，而是采用快捷方式，如：&nbsp;<br />
<br />
<font color="#0000ff">struct</font>&nbsp;s<br />
<font color="#800080"><b>{</b></font><br />
&nbsp;&nbsp;&nbsp;&nbsp;<font color="#0000ff">int</font>&nbsp;&nbsp;&nbsp;l<font color="#4b0082">;</font><br />
&nbsp;&nbsp;&nbsp;&nbsp;<font color="#0000ff">char</font><font color="#4b0082">*</font>&nbsp;p<font color="#4b0082">;</font><br />
<font color="#800080"><b>}</b></font><font color="#4b0082">;</font><br />
&nbsp;<br />
<font color="#0000ff">int</font>&nbsp;main<font color="#800080"><b>(</b></font><font color="#0000ff">int</font>&nbsp;argc<font color="#4b0082">,</font>&nbsp;<font color="#0000ff">char</font><font color="#4b0082">*</font>&nbsp;argv<font color="#800080"><b>[])</b></font><br />
<font color="#800080"><b>{</b></font><br />
&nbsp;&nbsp;&nbsp;&nbsp;<font color="#0000ff">struct</font>&nbsp;s&nbsp;s1&nbsp;<font color="#4b0082">=</font>&nbsp;<font color="#800080"><b>{</b></font><font color="#ff0000">4</font><font color="#4b0082">,</font>&nbsp;<font color="#800000">&quot;abcd&quot;</font><font color="#800080"><b>}</b></font><font color="#4b0082">;</font><br />
&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;<font color="#0000ff">return</font>&nbsp;<font color="#ff0000">0</font><font color="#4b0082">;</font><br />
<font color="#800080"><b>}</b></font><br />
以上这种方式是非常危险的，原因在于你对结构的内存布局作了假设。如果这个结构是第三方提供的，他很可能调整结构中成员的相对位置。而这样的调整往往不会在文档中说明，你自然很少去关注。如果调整的两个成员具有相同数据类型，编译时不会有任何警告，而程序的逻辑可能相距十万八千里了。&nbsp;<br />
<br />
正确的初始化方法应该是（当然，一个成员一个成员的初始化也行）：&nbsp;<br />
<br />
<font color="#0000ff">struct</font>&nbsp;s<br />
<font color="#800080"><b>{</b></font><br />
&nbsp;&nbsp;&nbsp;&nbsp;<font color="#0000ff">int</font>&nbsp;&nbsp;&nbsp;l<font color="#4b0082">;</font><br />
&nbsp;&nbsp;&nbsp;&nbsp;<font color="#0000ff">char</font><font color="#4b0082">*</font>&nbsp;p<font color="#4b0082">;</font><br />
<font color="#800080"><b>}</b></font><font color="#4b0082">;</font><br />
&nbsp;<br />
<font color="#0000ff">int</font>&nbsp;main<font color="#800080"><b>(</b></font><font color="#0000ff">int</font>&nbsp;argc<font color="#4b0082">,</font>&nbsp;<font color="#0000ff">char</font><font color="#4b0082">*</font>&nbsp;argv<font color="#800080"><b>[])</b></font><br />
<font color="#800080"><b>{</b></font><br />
&nbsp;&nbsp;&nbsp;&nbsp;<font color="#0000ff">struct</font>&nbsp;s&nbsp;s1&nbsp;<font color="#4b0082">=</font>&nbsp;<font color="#800080"><b>{</b></font><font color="#4b0082">.</font>l<font color="#4b0082">=</font><font color="#ff0000">4</font><font color="#4b0082">,</font>&nbsp;<font color="#4b0082">.</font>p&nbsp;<font color="#4b0082">=</font>&nbsp;<font color="#800000">&quot;abcd&quot;</font><font color="#800080"><b>}</b></font><font color="#4b0082">;</font><br />
&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;<font color="#0000ff">return</font>&nbsp;<font color="#ff0000">0</font><font color="#4b0082">;</font><br />
<font color="#800080"><b>}</b></font><br />
<font color="#800080"><b>(</b></font>有的编译器可能不支持新标准<font color="#800080"><b>)</b></font>&nbsp;<br />
<br />
o&nbsp;结构的大小变化引发的错误。&nbsp;<br />
<br />
我们看看下面这个例子：&nbsp;<br />
<br />
<font color="#0000ff">struct</font>&nbsp;base<br />
<font color="#800080"><b>{</b></font><br />
&nbsp;&nbsp;&nbsp;&nbsp;<font color="#0000ff">int</font>&nbsp;n<font color="#4b0082">;</font><br />
<font color="#800080"><b>}</b></font><font color="#4b0082">;</font><br />
&nbsp;<br />
<font color="#0000ff">struct</font>&nbsp;s<br />
<font color="#800080"><b>{</b></font><br />
&nbsp;&nbsp;&nbsp;&nbsp;<font color="#0000ff">struct</font>&nbsp;base&nbsp;b<font color="#4b0082">;</font><br />
&nbsp;&nbsp;&nbsp;&nbsp;<font color="#0000ff">int</font>&nbsp;m<font color="#4b0082">;</font><br />
<font color="#800080"><b>}</b></font><font color="#4b0082">;</font><br />
在OOP中，我们可以认为第二个结构继承了第一结构，这有什么问题吗？当然没有，这是C语言中实现继承的基本手法。&nbsp;<br />
<br />
现在假设第一个结构是第三方提供的，第二个结构是你自己的。第三方提供的库是以DLL方式分发的，DLL最大好处在于可以独立替换。但随着软件的进化，问题可能就来了。&nbsp;<br />
<br />
当第三方在第一个结构中增加了一个新的成员<font color="#0000ff">int</font>&nbsp;k<font color="#4b0082">;</font>，编译好后把DLL给你，你直接把它给了客户了，让他们替换掉老版本。程序加载时不会有任何问题，在运行逻辑可能完全改变！原因是两个结构的内存布局重叠了。&nbsp;<br />
<br />
解决这类错误的唯一办法就是重新编译全部代码。由此看来，动态库并不见得可以动态替换，如果你想了解更多相关内容，建议你阅读《COM本质论》。&nbsp;<br />
<br />
o&nbsp;分配<font color="#4b0082">/</font>释放不配对。&nbsp;<br />
<br />
大家都知道malloc要和free配对使用，<font color="#0000ff">new</font>要和<font color="#0000ff">delete</font><font color="#4b0082">/</font><font color="#0000ff">delete</font><font color="#800080"><b>[]</b></font>配对使用，重载了类<font color="#0000ff">new</font>操作，应该同时重载类的<font color="#0000ff">delete</font><font color="#4b0082">/</font><font color="#0000ff">delete</font><font color="#800080"><b>[]</b></font>操作。这些都是书上反复强调过的，除非当时晕了头，一般不会犯这样的低级错误。&nbsp;<br />
<br />
而有时候我们却被蒙在鼓里，两个代码看起来都是调用的free函数，实际上却调用了不同的实现。比如在Win32下，调试版与发布版，单线程与多线程是不同的运行时库，不同的运行时库使用的是不同的内存管理器。一不小心链接错了库，那你就麻烦了。程序可能动则崩溃，原因在于在一个内存管理器中分配的内存，在另外一个内存管理器中释放时就会出现问题。&nbsp;<br />
<br />
o&nbsp;返回指向临时变量的指针&nbsp;<br />
<br />
大家都知道，栈里面的变量都是临时的。当前函数执行完成时，相关的临时变量和参数都被清除了。不能把指向这些临时变量的指针返回给调用者，这样的指针指向的数据是随机的，会给程序造成不可预料的后果。&nbsp;<br />
<br />
下面是个错误的例子：&nbsp;<br />
<br />
<font color="#0000ff">char</font><font color="#4b0082">*</font>&nbsp;get_str<font color="#800080"><b>(</b></font><font color="#0000ff">void</font><font color="#800080"><b>)</b></font><br />
<font color="#800080"><b>{</b></font><br />
&nbsp;&nbsp;&nbsp;&nbsp;<font color="#0000ff">char</font>&nbsp;str<font color="#800080"><b>[]</b></font>&nbsp;<font color="#4b0082">=</font>&nbsp;<font color="#800080"><b>{</b></font><font color="#800000">&quot;abcd&quot;</font><font color="#800080"><b>}</b></font><font color="#4b0082">;</font><br />
&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;<font color="#0000ff">return</font>&nbsp;str<font color="#4b0082">;</font><br />
<font color="#800080"><b>}</b></font><br />
&nbsp;<br />
<font color="#0000ff">int</font>&nbsp;main<font color="#800080"><b>(</b></font><font color="#0000ff">int</font>&nbsp;argc<font color="#4b0082">,</font>&nbsp;<font color="#0000ff">char</font><font color="#4b0082">*</font>&nbsp;argv<font color="#800080"><b>[])</b></font><br />
<font color="#800080"><b>{</b></font><br />
&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;<font color="#0000ff">char</font><font color="#4b0082">*</font>&nbsp;p&nbsp;<font color="#4b0082">=</font>&nbsp;get_str<font color="#800080"><b>()</b></font><font color="#4b0082">;</font><br />
&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;printf<font color="#800080"><b>(</b></font><font color="#800000">&quot;%s\n&quot;</font><font color="#4b0082">,</font>&nbsp;p<font color="#800080"><b>)</b></font><font color="#4b0082">;</font><br />
&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;<font color="#0000ff">return</font>&nbsp;<font color="#ff0000">0</font><font color="#4b0082">;</font><br />
<font color="#800080"><b>}</b></font><br />
下面这个例子没有问题，大家知道为什么吗？&nbsp;<br />
<br />
<font color="#0000ff">char</font><font color="#4b0082">*</font>&nbsp;get_str<font color="#800080"><b>(</b></font><font color="#0000ff">void</font><font color="#800080"><b>)</b></font><br />
<font color="#800080"><b>{</b></font><br />
&nbsp;&nbsp;&nbsp;&nbsp;<font color="#0000ff">char</font><font color="#4b0082">*</font>&nbsp;str&nbsp;<font color="#4b0082">=</font>&nbsp;<font color="#800080"><b>{</b></font><font color="#800000">&quot;abcd&quot;</font><font color="#800080"><b>}</b></font><font color="#4b0082">;</font><br />
&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;<font color="#0000ff">return</font>&nbsp;str<font color="#4b0082">;</font><br />
<font color="#800080"><b>}</b></font><br />
&nbsp;<br />
<font color="#0000ff">int</font>&nbsp;main<font color="#800080"><b>(</b></font><font color="#0000ff">int</font>&nbsp;argc<font color="#4b0082">,</font>&nbsp;<font color="#0000ff">char</font><font color="#4b0082">*</font>&nbsp;argv<font color="#800080"><b>[])</b></font><br />
<font color="#800080"><b>{</b></font><br />
&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;<font color="#0000ff">char</font><font color="#4b0082">*</font>&nbsp;p&nbsp;<font color="#4b0082">=</font>&nbsp;get_str<font color="#800080"><b>()</b></font><font color="#4b0082">;</font><br />
&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;printf<font color="#800080"><b>(</b></font><font color="#800000">&quot;%s\n&quot;</font><font color="#4b0082">,</font>&nbsp;p<font color="#800080"><b>)</b></font><font color="#4b0082">;</font><br />
&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;<font color="#0000ff">return</font>&nbsp;<font color="#ff0000">0</font><font color="#4b0082">;</font><br />
<font color="#800080"><b>}</b></font><br />
<br />
o&nbsp;试图修改常量&nbsp;<br />
<br />
在函数参数前加上<font color="#0000ff">const</font>修饰符，只是给编译器做类型检查用的，编译器禁止修改这样的变量。但这并不是强制的，你完全可以用强制类型转换绕过去，一般也不会出什么错。&nbsp;<br />
<br />
而全局常量和字符串，用强制类型转换绕过去，运行时仍然会出错。原因在于它们是放在<font color="#4b0082">.</font>rodata里面的，而<font color="#4b0082">.</font>rodata内存页面是不能修改的。试图对它们修改，会引发内存错误。&nbsp;<br />
<br />
下面这个程序在运行时会出错：&nbsp;<br />
<br />
<font color="#0000ff">int</font>&nbsp;main<font color="#800080"><b>(</b></font><font color="#0000ff">int</font>&nbsp;argc<font color="#4b0082">,</font>&nbsp;<font color="#0000ff">char</font><font color="#4b0082">*</font>&nbsp;argv<font color="#800080"><b>[])</b></font><br />
<font color="#800080"><b>{</b></font><br />
&nbsp;&nbsp;&nbsp;&nbsp;<font color="#0000ff">char</font><font color="#4b0082">*</font>&nbsp;p&nbsp;<font color="#4b0082">=</font>&nbsp;<font color="#800000">&quot;abcd&quot;</font><font color="#4b0082">;</font><br />
&nbsp;&nbsp;&nbsp;&nbsp;<font color="#4b0082">*</font>p&nbsp;<font color="#4b0082">=</font>&nbsp;<font color="#800000">'1'</font><font color="#4b0082">;</font><br />
&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;<font color="#0000ff">return</font>&nbsp;<font color="#ff0000">0</font><font color="#4b0082">;</font><br />
<font color="#800080"><b>}</b></font><br />
<br />
o&nbsp;误解传值与传引用&nbsp;<br />
<br />
在C<font color="#4b0082">/</font>C<font color="#4b0082">++</font>中，参数默认传递方式是传值的，即在参数入栈时被拷贝一份。在函数里修改这些参数，不会影响外面的调用者。如：&nbsp;<br />
<br />
<font color="#0000ff">#include</font>&nbsp;<font color="#4b0082">&lt;</font>stdlib<font color="#4b0082">.</font>h<font color="#4b0082">&gt;</font><br />
<font color="#0000ff">#include</font>&nbsp;<font color="#4b0082">&lt;</font>stdio<font color="#4b0082">.</font>h<font color="#4b0082">&gt;</font><br />
&nbsp;<br />
<font color="#0000ff">void</font>&nbsp;get_str<font color="#800080"><b>(</b></font><font color="#0000ff">char</font><font color="#4b0082">*</font>&nbsp;p<font color="#800080"><b>)</b></font><br />
<font color="#800080"><b>{</b></font><br />
&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;p&nbsp;<font color="#4b0082">=</font>&nbsp;malloc<font color="#800080"><b>(</b></font><font color="#0000ff">sizeof</font><font color="#800080"><b>(</b></font><font color="#800000">&quot;abcd&quot;</font><font color="#800080"><b>))</b></font><font color="#4b0082">;</font><br />
&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;strcpy<font color="#800080"><b>(</b></font>p<font color="#4b0082">,</font>&nbsp;<font color="#800000">&quot;abcd&quot;</font><font color="#800080"><b>)</b></font><font color="#4b0082">;</font><br />
&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;<font color="#0000ff">return</font><font color="#4b0082">;</font><br />
<font color="#800080"><b>}</b></font><br />
&nbsp;<br />
<font color="#0000ff">int</font>&nbsp;main<font color="#800080"><b>(</b></font><font color="#0000ff">int</font>&nbsp;argc<font color="#4b0082">,</font>&nbsp;<font color="#0000ff">char</font><font color="#4b0082">*</font>&nbsp;argv<font color="#800080"><b>[])</b></font><br />
<font color="#800080"><b>{</b></font><br />
&nbsp;&nbsp;&nbsp;&nbsp;<font color="#0000ff">char</font><font color="#4b0082">*</font>&nbsp;p&nbsp;<font color="#4b0082">=</font>&nbsp;NULL<font color="#4b0082">;</font><br />
&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;get_str<font color="#800080"><b>(</b></font>p<font color="#800080"><b>)</b></font><font color="#4b0082">;</font><br />
&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;printf<font color="#800080"><b>(</b></font><font color="#800000">&quot;p=%p\n&quot;</font><font color="#4b0082">,</font>&nbsp;p<font color="#800080"><b>)</b></font><font color="#4b0082">;</font><br />
&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;<font color="#0000ff">return</font>&nbsp;<font color="#ff0000">0</font><font color="#4b0082">;</font><br />
<font color="#800080"><b>}</b></font><br />
在main函数里，p的值仍然是空值。当然在函数里修改指针指向的内容是可以的。&nbsp;<br />
<br />
o&nbsp;重名符号。&nbsp;<br />
<br />
无论是函数名还是变量名，如果在不同的作用范围内重名，自然没有问题。但如果两个符号的作用域有交集，如全局变量和局部变量，全局变量与全局变量之间，重名的现象一定要坚决避免。gcc有一些隐式规则来决定处理同名变量的方式，编译时可能没有任何警告和错误，但结果通常并非你所期望的。&nbsp;<br />
<br />
下面例子编译时就没有警告：&nbsp;<br />
<br />
t<font color="#4b0082">.</font>c<br />
&nbsp;<br />
<font color="#0000ff">#include</font>&nbsp;<font color="#4b0082">&lt;</font>stdlib<font color="#4b0082">.</font>h<font color="#4b0082">&gt;</font><br />
<font color="#0000ff">#include</font>&nbsp;<font color="#4b0082">&lt;</font>stdio<font color="#4b0082">.</font>h<font color="#4b0082">&gt;</font><br />
&nbsp;<br />
<font color="#0000ff">int</font>&nbsp;count&nbsp;<font color="#4b0082">=</font>&nbsp;<font color="#ff0000">0</font><font color="#4b0082">;</font><br />
&nbsp;<br />
<font color="#0000ff">int</font>&nbsp;get_count<font color="#800080"><b>(</b></font><font color="#0000ff">void</font><font color="#800080"><b>)</b></font><br />
&nbsp;<br />
<font color="#800080"><b>{</b></font><br />
&nbsp;&nbsp;&nbsp;&nbsp;<font color="#0000ff">return</font>&nbsp;count<font color="#4b0082">;</font><br />
<font color="#800080"><b>}</b></font><br />
&nbsp;<br />
main<font color="#4b0082">.</font>c<br />
&nbsp;<br />
<font color="#0000ff">#include</font>&nbsp;<font color="#4b0082">&lt;</font>stdio<font color="#4b0082">.</font>h<font color="#4b0082">&gt;</font><br />
&nbsp;<br />
<font color="#0000ff">extern</font>&nbsp;<font color="#0000ff">int</font>&nbsp;get_count<font color="#800080"><b>(</b></font><font color="#0000ff">void</font><font color="#800080"><b>)</b></font><font color="#4b0082">;</font><br />
&nbsp;<br />
<font color="#0000ff">int</font>&nbsp;count<font color="#4b0082">;</font><br />
&nbsp;<br />
<font color="#0000ff">int</font>&nbsp;main<font color="#800080"><b>(</b></font><font color="#0000ff">int</font>&nbsp;argc<font color="#4b0082">,</font>&nbsp;<font color="#0000ff">char</font><font color="#4b0082">*</font>&nbsp;argv<font color="#800080"><b>[])</b></font><br />
<font color="#800080"><b>{</b></font><br />
&nbsp;&nbsp;&nbsp;&nbsp;count&nbsp;<font color="#4b0082">=</font>&nbsp;<font color="#ff0000">10</font><font color="#4b0082">;</font><br />
&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;printf<font color="#800080"><b>(</b></font><font color="#800000">&quot;get_count=%d\n&quot;</font><font color="#4b0082">,</font>&nbsp;get_count<font color="#800080"><b>())</b></font><font color="#4b0082">;</font><br />
&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;<font color="#0000ff">return</font>&nbsp;<font color="#ff0000">0</font><font color="#4b0082">;</font><br />
&nbsp;<br />
<font color="#800080"><b>}</b></font><br />
如果把main<font color="#4b0082">.</font>c中的<font color="#0000ff">int</font>&nbsp;count<font color="#4b0082">;</font>修改为<font color="#0000ff">int</font>&nbsp;count&nbsp;<font color="#4b0082">=</font>&nbsp;<font color="#ff0000">0</font><font color="#4b0082">;</font>，gcc就会编辑出错，说multiple&nbsp;definition&nbsp;of&nbsp;`count&rsquo;。它的隐式规则比较奇妙吧，所以还是不要依赖它为好。&nbsp;<br />
<br />
o&nbsp;栈溢出。&nbsp;<br />
<br />
我们在前面关于堆栈的一节讲过，在PC上，普通线程的栈空间也有十几M，通常够用了，定义大一点的临时变量不会有什么问题。&nbsp;<br />
<br />
而在一些嵌入式中，线程的栈空间可能只5K大小，甚至小到只有<font color="#ff0000">256</font>个字节。在这样的平台中，栈溢出是最常用的错误之一。在编程时应该清楚自己平台的限制，避免栈溢出的可能。&nbsp;<br />
<br />
o&nbsp;误用<font color="#0000ff">sizeof</font>。&nbsp;<br />
<br />
尽管C<font color="#4b0082">/</font>C<font color="#4b0082">++</font>通常是按值传递参数，而数组则是例外，在传递数组参数时，数组退化为指针（即按引用传递），用<font color="#0000ff">sizeof</font>是无法取得数组的大小的。&nbsp;<br />
<br />
从下面这个例子可以看出：&nbsp;<br />
<br />
<font color="#0000ff">void</font>&nbsp;test<font color="#800080"><b>(</b></font><font color="#0000ff">char</font>&nbsp;str<font color="#800080"><b>[</b></font><font color="#ff0000">20</font><font color="#800080"><b>])</b></font><br />
<font color="#800080"><b>{</b></font><br />
&nbsp;&nbsp;&nbsp;&nbsp;printf<font color="#800080"><b>(</b></font><font color="#800000">&quot;%s:size=%d\n&quot;</font><font color="#4b0082">,</font>&nbsp;__func__<font color="#4b0082">,</font>&nbsp;<font color="#0000ff">sizeof</font><font color="#800080"><b>(</b></font>str<font color="#800080"><b>))</b></font><font color="#4b0082">;</font><br />
<font color="#800080"><b>}</b></font>&nbsp;&nbsp;<br />
&nbsp;<br />
<font color="#0000ff">int</font>&nbsp;main<font color="#800080"><b>(</b></font><font color="#0000ff">int</font>&nbsp;argc<font color="#4b0082">,</font>&nbsp;<font color="#0000ff">char</font><font color="#4b0082">*</font>&nbsp;argv<font color="#800080"><b>[])</b></font><br />
<font color="#800080"><b>{</b></font><br />
&nbsp;&nbsp;&nbsp;&nbsp;<font color="#0000ff">char</font>&nbsp;str<font color="#800080"><b>[</b></font><font color="#ff0000">20</font><font color="#800080"><b>]</b></font>&nbsp;&nbsp;<font color="#4b0082">=</font>&nbsp;<font color="#800080"><b>{</b></font><font color="#ff0000">0</font><font color="#800080"><b>}</b></font><font color="#4b0082">;</font><br />
&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;test<font color="#800080"><b>(</b></font>str<font color="#800080"><b>)</b></font><font color="#4b0082">;</font><br />
&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;printf<font color="#800080"><b>(</b></font><font color="#800000">&quot;%s:size=%d\n&quot;</font><font color="#4b0082">,</font>&nbsp;__func__<font color="#4b0082">,</font>&nbsp;<font color="#0000ff">sizeof</font><font color="#800080"><b>(</b></font>str<font color="#800080"><b>))</b></font><font color="#4b0082">;</font><br />
&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;<font color="#0000ff">return</font>&nbsp;<font color="#ff0000">0</font><font color="#4b0082">;</font><br />
<font color="#800080"><b>}</b></font><br />
&nbsp;<br />
<font color="#800080"><b>[</b></font>root@localhost&nbsp;mm<font color="#800080"><b>]</b></font>#&nbsp;<font color="#4b0082">./</font>t<font color="#4b0082">.</font>exe<br />
test<font color="#4b0082">:</font>size<font color="#4b0082">=</font><font color="#ff0000">4</font><br />
main<font color="#4b0082">:</font>size<font color="#4b0082">=</font><font color="#ff0000">20</font><br />
<br />
o&nbsp;字节对齐。&nbsp;<br />
<br />
字节对齐主要目的是提高内存访问的效率。但在有的平台<font color="#800080"><b>(</b></font>如arm7<font color="#800080"><b>)</b></font>上，就不光是效率问题了，如果不对齐，得到的数据是错误的。&nbsp;<br />
<br />
所幸的是，大多数情况下，编译会保证全局变量和临时变量按正确的方式对齐。内存管理器会保证动态内存按正确的方式对齐。要注意的是，在不同类型的变量之间转换时要小心，如把<font color="#0000ff">char</font><font color="#4b0082">*</font>强制转换为<font color="#0000ff">int</font><font color="#4b0082">*</font>时，要格外小心。&nbsp;<br />
<br />
另外，字节对齐也会造成结构大小的变化，在程序内部用<font color="#0000ff">sizeof</font>来取得结构的大小，这就足够了。若数据要在不同的机器间传递时，在通信协议中要规定对齐的方式，避免对齐方式不一致引发的问题。&nbsp;<br />
<br />
o&nbsp;字节顺序。&nbsp;<br />
<br />
字节顺序历来是设计跨平台软件时头疼的问题。字节顺序是关于数据在物理内存中的布局的问题，最常见的字节顺序有两种：大端模式与小端模式。&nbsp;<br />
<br />
大端模式是高位字节数据存放在低地址处，低位字节数据存放在高地址处。&nbsp;<br />
<br />
小端模式指低位字节数据存放在内存低地址处，高位字节数据存放在内存高地址处；&nbsp;<br />
<br />
在普通软件中，字节顺序问题并不引人注目。而在开发与网络通信和数据交换有关的软件时，字节顺序问题就要特殊注意了。&nbsp;<br />
<br />
o&nbsp;多线程共享变量没有用valotile修饰。&nbsp;<br />
<br />
关键字valotile的作用是告诉编译器，不要把变量优化到寄存器里。在开发多线程并发的软件时，如果这些线程共享一些全局变量，这些全局变量最好用valotile修饰。这样可以避免因为编译器优化而引起的错误，这样的错误非常难查。&nbsp;<br />
<br />
o&nbsp;忘记函数的返回值&nbsp;<br />
<br />
函数需要返回值，如果你忘记<font color="#0000ff">return</font>语句，它仍然会返回一个值，因为在i386上，EAX用来保存返回值，如果没有明确返回，EAX最后的内容被返回，所以EAX的内容是随机的。&nbsp;<br />
<br />
<font color="#4b0082">-----------------------------------</font><br />
<br />
自动测试&nbsp;<br />
<br />
手工测试比没有测试强一点，但是它存在的问题让它很难在实践中应用：手工输入数据的过程单调乏味，很难长期坚持。每次都要重新输入数据，浪费大量时间。测试用例不能累积，测试往往不完整。用人脑判断输出的正误，浪费人力也存在误差。要写得好测试自然不能省，要写得快就需要更好的测试方法。&nbsp;<br />
<br />
更好的测试方法当然是自动测试了。幸运的是，刚进入这个行业我就接触了自动的测试&nbsp;<font color="#800080"><b>(</b></font>呵，读本文的初学者就更幸运了<font color="#800080"><b>)</b></font>，我的第一份正式工作是在测试组写测试程序。当时测试组也算是人才济济了，居然有几个北大毕业的，不过她们都不懂Linux，所以我被指派去为移植到Linux上的模块写测试程序。这些模块都有测试程序，但这些测试程序的功能太弱了，我的上司要求开发人员改进，但那些开发人员太自以为是了，根本不理我们，所以我们只好自己重写这些测试程序。模块很多，大概有<font color="#ff0000">50</font>多个模块，熟悉这些模块也需要不少时间，按每两个工作日写一个测试程序，上司给我<font color="#ff0000">5</font>个月时间。&nbsp;<br />
<br />
记得第一个模块是RDFParser，RDF<font color="#800080"><b>(</b></font>资源描述框架<font color="#800080"><b>)</b></font>是XML的一种应用，RDFParser实际上是一个XML解析器，并包装成RDF要求的接口。由于我对C<font color="#4b0082">/</font>C<font color="#4b0082">++</font>还不太熟悉，对RDF更不熟悉了，花了两周时间才写出这个测试程序。运行起来有些不正常，我确信不是测试程序的问题，就去请开发人员帮忙来看一下。负责RDFParser的那个程序员是人大毕业，我没有见过第二个比他更自以为是的程序员了，他刚在我座位上坐下就很大声说，你们QA的人太蠢了！&nbsp;<br />
<br />
当时一听就愣了，不过我是新来的，见上司都没反应，自然就忍了。我列举了一些证据是模块里面的问题，他听也不听，只是不断重复的说，不可能是我程序的问题，你们QA的人太蠢了，总是浪费我的时间。过了一会儿，他终于闭上了嘴巴，又等了一会儿才说，等会儿重新发个版本给你吧。后来又请他过来四五次，结果每次都是他的问题。&nbsp;<br />
<br />
之后我再没有听到他说过你们QA的人太蠢了的话。为了避免让他抓到把柄来嘲笑测试组，我决定请他来查问题之前做更详细的测试。当时我写的测试程序和现在初学者写的测试程序没有两样，都是从教科书上学来的，先通过scanf从终端输入数据，调用被测函数，再把结果printf出来，这花了我太多时间。想到后面还有<font color="#ff0000">50</font>多个模块的测试程序要写，这样下去不行，一定得想个办法。&nbsp;<br />
<br />
后来我把输入的数据和期望的结果都写到一个INI文件中，测试程序从这个文件中读入数据，运行测试，再和预期结果比较，整个过程都自动化了。写了一个INI文件的解析器花了我一周时间，又重写了那个测试程序，整整花了我一个月时间完成RDFParser的测试程序。进度自然大落后了，还好上司知道后并没有责备我，让我慢慢做就好了。&nbsp;<br />
<br />
写第二个测试程序时把INI解析的代码拷贝过去，再加一些调用模块的代码就写好了，第三个也是如此。写了几个之后，我发现了INI解析有个BUG，结果每个测试程序我都要去修改，想到维护起来太麻烦了，就把INI解析器的接口规范化了，编译成一个独立共享库。又写了几个测试程序，我写烦了，原因是测试程序无非就是读入数据，调用被测函数，再检查结果，这个过程太无聊了。想到后面还要把这个过程重复几十遍，郁闷了几天之后，突然灵机一动，我决定写了一个代码产生器来产生这些代码。开始的代码产生器用C写的，用一个简单的规则来描述被测函数，通过这些规则来产生测试程序。我把这些东西和INI解析器放在一个独立的库中，把它叫作TesterFrameWork，经过几个测试程序的验证和完善，后来利用这个TesterFrameWork，只要一两个小时就能完成一个测试程序了。有次请开发人员那边一个高手帮我查一个问题，他看一会儿我的TesterFrameWork之后，盯着我说，你太聪明了。我笑了笑说，刚刚开始写C<font color="#4b0082">/</font>C<font color="#4b0082">++</font>程序。&nbsp;<br />
<br />
一年之后我知道了有个CPPUnit之后，为了赶时髦我把TesterFrameWork改名为CxxUnit，非典的时候放假无聊就把它重写了一遍放在cosoft上了<font color="#800080"><b>(</b></font>之后没有管过它，或许还在吧<font color="#800080"><b>)</b></font>。&nbsp;<br />
<br />
一个大系统很难自动测试，而一个独立的模块则是最佳的自动测试单元。自动测试和单元测试几乎成了等价的概念，很多人都以为自动测试就是利用CPPUnit这样的单元测试框架写个测试程序而已，这完全是错误的，就像有人以为有个设计文档的模板，照着填空就能填出好设计一样。&nbsp;<br />
<br />
我自己实现过单元测试框架，不是像有些人出于模仿去实现，而完全出于实际的需要，后来我也研究其它测试框架，应该说我对测试程序框架的认识比一般程序员要深刻。我认为测试程序框架可以减化一些测试程序的工作，但它与自动测试没有密切关系，用不用测试程序框架完全是个人喜好。用测试程序框架未必能写出好的测试程序，就像用C<font color="#4b0082">++</font>未必能写出好的面向对象的程序一样。&nbsp;<br />
<br />
虽然我顺利的完成了那个写测试程序的任务，但我一直被一个问题困扰：如何写测试用例，如何去检测结果？这是测试程序框架帮不上忙的。写测试用例还好说，通过边界值法，等价类法和路径覆盖法找到最常用的测试用例。检测结果呢？有人说很简单啊，判断返回值就好了。那我问一下dlist_insert返回OK，就真的OK了吗？如果一个函数根本没有返回值，那你怎么判断呢？&nbsp;<br />
<br />
测试程序框架是敏捷论者提倡的，在我看来它根本不够敏捷：你要去学习它，了解它的运行机制，要包含它的头文件，链接它的库，有比不用它更敏捷么？重要的是它根本帮不上什么有用的忙。前面的问题折磨了我一段时间，于是得出一个可能有点偏激的结论：测试程序框架都是愚蠢的，你真正需要的，它根本帮不了你<font color="#800080"><b>(</b></font>我知道这样说会得罪一些用测试程序框架的朋友，如果你想找我讨论的话，请看完本节的附带示例代码再说<font color="#800080"><b>)</b></font>。&nbsp;<br />
<br />
就在那个时候，我看到了孟岩老师翻译的《契约式设计<font color="#800080"><b>(</b></font>Design&nbsp;by&nbsp;Contract<font color="#800080"><b>)</b></font>》，读完之后豁然开朗。或许我还没有明白契约式设计的本质，但我确实知道了写自动测试程序的方法，下面我介绍一下：&nbsp;<br />
<br />
o&nbsp;在设计时，每个函数只完成单一的功能。单一功能的函数容易理解，也容易预测其行为。对测试来说，给定一些输入数据，就知道它的输出和影响，这样函数是最容易测试的。&nbsp;<br />
<br />
o&nbsp;在设计时，把函数分为查询和命令两类。查询函数只查询对象的状态，而不改变对象的状态。命令函数则只修改对象的状态，只返回其操作是否成功的标志，而不返回对象的状态。比如，dlist_length查询双向链表的长度，它不修改双向链表的任何状态。dlist_delete修改对象的状态<font color="#800080"><b>(</b></font>删除结点<font color="#800080"><b>)</b></font>，并返回其操作是否成功，而不返回当前长度或者删除的结点之类的状态。&nbsp;<br />
<br />
o&nbsp;在设计时，把查询分为基本查询和复合查询两类。基本查询函数只查询单一的状态，而复合查询可以同时查询多个状态。比如，window_get_width返回窗口的宽度，这是基本查询函数，widget_get_rect返回窗口的左上角坐标，宽度和高度，这是复合查询函数。&nbsp;<br />
<br />
o在实现时，检验输入数据，确认使用者正确的调用了函数。契约式设计规定了调用者和实现者双方的责任，调用者需要使用正确的参数，才能保证有正确的结果。政治家告诉我们，信任但要检查，所以作为实现者就需要检查输入参数是否违背了契约。那怎么检查呢？有人说，如果检查到无效参数就返回一个错误码。这当然可以，只是不太好，因为大多数人都没有检查返回值的习惯，如果每个地方都检查函数的返回值，也是件很繁琐的事，代码看起来也比较乱。通常我们只检查一些关键的地方，对于无效参数这样的错误，可能就无声无息的隐藏起来了，这样不好，因为隐藏得越深，发现的时间越晚，修改的代价越大。&nbsp;<br />
<br />
在C<font color="#4b0082">++</font>和JAVA里，如果参数不正确，通常是<font color="#0000ff">throw</font>一个无效参数之类的异常，C语言里面没有异常这个概念，我们需要其它办法才行。有人推荐用assert来检查，这是一个好办法，assert只在调试版本中有效<font color="#800080"><b>(</b></font>没有定义NDEBUG<font color="#800080"><b>)</b></font>，这样任何无效调用都在调试版本中暴露出来了。如果配合前面返回错误码的方法，在发布版本中也可能避免程序粗暴的死掉。使用方法如下：&nbsp;<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;assert<font color="#800080"><b>(</b></font>thiz&nbsp;<font color="#4b0082">!=</font>&nbsp;NULL<font color="#800080"><b>)</b></font><font color="#4b0082">;</font><br />
&nbsp;&nbsp;&nbsp;&nbsp;<font color="#0000ff">if</font><font color="#800080"><b>(</b></font>thiz&nbsp;<font color="#4b0082">==</font>&nbsp;NULL<font color="#800080"><b>)</b></font><br />
&nbsp;&nbsp;&nbsp;&nbsp;<font color="#800080"><b>{</b></font><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<font color="#0000ff">return</font>&nbsp;DLIST_RET_INVALID_PARAMS<font color="#4b0082">;</font><br />
&nbsp;&nbsp;&nbsp;&nbsp;<font color="#800080"><b>}</b></font><br />
我一直使用这种方法，但是有个问题：无法用自动测试验证assert是否正常的触发了，当用错误的参数测试时，我期望assert被触发，但如果assert被触发了，自动程序测试就死掉了，自动测试程序死掉了，就无法继续验证下一个assert。这是一个悖论！&nbsp;<br />
<br />
后来我从glib里面学了一招，它检查时不用assert，只是打印出一个警告，代码也简明了，按它的方式，我们这样检查：&nbsp;<br />
<br />
return_val_if_fail<font color="#800080"><b>(</b></font>cursor&nbsp;<font color="#4b0082">!=</font>&nbsp;NULL<font color="#4b0082">,</font>&nbsp;DLIST_RET_INVALID_PARAMS<font color="#800080"><b>)</b></font><font color="#4b0082">;</font><br />
我们需要定义两个宏，一个用于无返回值的函数，一个用于有返回值的函数：&nbsp;<br />
<br />
<font color="#0000ff">#define</font>&nbsp;return_if_fail<font color="#800080"><b>(</b></font>p<font color="#800080"><b>)</b></font>&nbsp;<font color="#0000ff">if</font><font color="#800080"><b>(</b></font><font color="#4b0082">!</font><font color="#800080"><b>(</b></font>p<font color="#800080"><b>))</b></font>&nbsp;\<br />
&nbsp;&nbsp;&nbsp;&nbsp;<font color="#800080"><b>{</b></font>printf<font color="#800080"><b>(</b></font><font color="#800000">&quot;%s:%d&nbsp;Warning:&nbsp;&quot;</font>#p<font color="#800000">&quot;&nbsp;failed.\n&quot;</font><font color="#4b0082">,</font>&nbsp;\<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;__func__<font color="#4b0082">,</font>&nbsp;__LINE__<font color="#800080"><b>)</b></font><font color="#4b0082">;</font>&nbsp;<font color="#0000ff">return</font><font color="#4b0082">;</font><font color="#800080"><b>}</b></font><br />
<font color="#0000ff">#define</font>&nbsp;return_val_if_fail<font color="#800080"><b>(</b></font>p<font color="#4b0082">,</font>&nbsp;ret<font color="#800080"><b>)</b></font>&nbsp;<font color="#0000ff">if</font><font color="#800080"><b>(</b></font><font color="#4b0082">!</font><font color="#800080"><b>(</b></font>p<font color="#800080"><b>))</b></font>&nbsp;\<br />
&nbsp;&nbsp;&nbsp;&nbsp;<font color="#800080"><b>{</b></font>printf<font color="#800080"><b>(</b></font><font color="#800000">&quot;%s:%d&nbsp;Warning:&nbsp;&quot;</font>#p<font color="#800000">&quot;&nbsp;failed.\n&quot;</font><font color="#4b0082">,</font>\<br />
__func__<font color="#4b0082">,</font>&nbsp;__LINE__<font color="#800080"><b>)</b></font><font color="#4b0082">;</font>&nbsp;<font color="#0000ff">return</font>&nbsp;<font color="#800080"><b>(</b></font>ret<font color="#800080"><b>)</b></font><font color="#4b0082">;</font><font color="#800080"><b>}</b></font><br />
这样一来，遇到无效参数时，可以看到一个警告信息，同时又不会影响自动测试。&nbsp;<br />
<br />
o在测试时，用查询来验证命令。命令一般都有返回值，但只检查返回值是不够的。比如dlist_delete返回OK，它真的OK了吗？我们信任它，但还是要检查。怎么检查？很简单，用查询函数来检查对象的状态是不是预期的。&nbsp;<br />
<br />
对于dlist_delete，我们预期：&nbsp;<br />
<br />
<font color="#ff0000">1</font><font color="#4b0082">.</font>输入无效参数，期望返回DLIST_RET_INVALID_PARAMS。&nbsp;<font color="#ff0000">2</font><font color="#4b0082">.</font>输入正确参数，期望：&nbsp;函数返回DLIST_RET_OK&nbsp;双向链表的长度减一。&nbsp;删除的位置的下一个元素被移到删除的位置。在测试程序中检查时，因为任何不符合期望的结果都是BUG，所以我们用assert检查。这样有问题马上暴露出来了，定位错误比较容易，通常都不需要调试器。我们这样来检查：&nbsp;<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;assert<font color="#800080"><b>(</b></font>dlist_length<font color="#800080"><b>(</b></font>dlist<font color="#800080"><b>)</b></font>&nbsp;<font color="#4b0082">==</font>&nbsp;<font color="#800080"><b>(</b></font>n<font color="#4b0082">-</font>i<font color="#800080"><b>))</b></font><font color="#4b0082">;</font><br />
&nbsp;&nbsp;&nbsp;&nbsp;assert<font color="#800080"><b>(</b></font>dlist_delete<font color="#800080"><b>(</b></font>dlist<font color="#4b0082">,</font>&nbsp;<font color="#ff0000">0</font><font color="#800080"><b>)</b></font>&nbsp;<font color="#4b0082">==</font>&nbsp;DLIST_RET_OK<font color="#800080"><b>)</b></font><font color="#4b0082">;</font><br />
&nbsp;&nbsp;&nbsp;&nbsp;assert<font color="#800080"><b>(</b></font>dlist_length<font color="#800080"><b>(</b></font>dlist<font color="#800080"><b>)</b></font>&nbsp;<font color="#4b0082">==</font>&nbsp;<font color="#800080"><b>(</b></font>n<font color="#4b0082">-</font>i<font color="#4b0082">-</font><font color="#ff0000">1</font><font color="#800080"><b>))</b></font><font color="#4b0082">;</font><br />
&nbsp;&nbsp;&nbsp;&nbsp;<font color="#0000ff">if</font><font color="#800080"><b>((</b></font>i&nbsp;<font color="#4b0082">+</font>&nbsp;<font color="#ff0000">1</font><font color="#800080"><b>)</b></font>&nbsp;<font color="#4b0082">&lt;</font>&nbsp;n<font color="#800080"><b>)</b></font><br />
&nbsp;&nbsp;&nbsp;&nbsp;<font color="#800080"><b>{</b></font><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;assert<font color="#800080"><b>(</b></font>dlist_get_by_index<font color="#800080"><b>(</b></font>dlist<font color="#4b0082">,</font>&nbsp;<font color="#ff0000">0</font><font color="#4b0082">,</font>&nbsp;<font color="#800080"><b>(</b></font><font color="#0000ff">void</font><font color="#4b0082">**</font><font color="#800080"><b>)</b></font><font color="#4b0082">&amp;</font>data<font color="#800080"><b>)</b></font>&nbsp;<font color="#4b0082">==</font>&nbsp;DLIST_RET_OK<font color="#800080"><b>)</b></font><font color="#4b0082">;</font><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;assert<font color="#800080"><b>((</b></font><font color="#0000ff">int</font><font color="#800080"><b>)</b></font>data&nbsp;<font color="#4b0082">==</font>&nbsp;<font color="#800080"><b>(</b></font>i<font color="#4b0082">+</font><font color="#ff0000">1</font><font color="#800080"><b>))</b></font><font color="#4b0082">;</font><br />
&nbsp;&nbsp;&nbsp;&nbsp;<font color="#800080"><b>}</b></font><br />
<br />
o在测试时，用基本查询去验证复合查询。基本查询和复合查询返回的应该一致。比如：&nbsp;<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;Rect&nbsp;rect&nbsp;<font color="#4b0082">=</font>&nbsp;<font color="#800080"><b>{</b></font><font color="#ff0000">0</font><font color="#800080"><b>}</b></font><font color="#4b0082">;</font><br />
&nbsp;&nbsp;&nbsp;&nbsp;widget_get_rect<font color="#800080"><b>(</b></font>widget<font color="#4b0082">,</font>&nbsp;<font color="#4b0082">&amp;</font>rect<font color="#800080"><b>)</b></font><font color="#4b0082">;</font><br />
&nbsp;&nbsp;&nbsp;&nbsp;assert<font color="#800080"><b>(</b></font>widget_get_width<font color="#800080"><b>(</b></font>widget<font color="#800080"><b>)</b></font>&nbsp;<font color="#4b0082">==</font>&nbsp;rect<font color="#4b0082">.</font>width<font color="#800080"><b>)</b></font><font color="#4b0082">;</font><br />
&nbsp;&nbsp;&nbsp;&nbsp;assert<font color="#800080"><b>(</b></font>widget_get_height<font color="#800080"><b>(</b></font>widget<font color="#800080"><b>)</b></font><font color="#4b0082">==</font>&nbsp;rect<font color="#4b0082">.</font>height<font color="#800080"><b>)</b></font><font color="#4b0082">;</font><br />
<br />
o在测试时，预期结果依赖其执行上下文，我们要按逻辑组织测试用例。前面调用的函数可能改变了对象的状态，为了简化测试，在每组测试用例开始时，都重置对象到初始状态。&nbsp;<br />
<br />
o在测试时，第一次只写基本的测试用例，以后逐渐累积，每次发现新的BUG就把相应的测试用例加进去。每次修改了代码就运行一遍自动测试，保证修改没有引起其它副作用。&nbsp;<br />
<br />
按着上面的原则，应付正常模块的测试没有问题了，但是下面的情况仍然比较棘手：&nbsp;<br />
<br />
o&nbsp;带有GUI的应用程序。有GUI的程序会给自动的输入数据和检查结果带来困难，有些工具可以部分解决这个问题，特别是针对Win32下的GUI，我很少在Windows下写程序，所以对这方面了解不多。不过最好的办法还是用MVC模型等分离界面和实现，因为界面通常相对比较简单，可以手工测试，而实现的逻辑比较复杂，这部分可以自动测试。后面我们会专门讲解分离界面和实现的方法。&nbsp;<br />
<br />
o&nbsp;有随机数据输入。如果有些输入数据是内部随机产生的，那你根本无法预测它的输出结果和影响。比如游戏随机的步骤和无线网络信号的变化。对于我们可以控制的随机数据，可以提供额外的函数去获取这些数据。对于无法控制的随机输入数据，可以把它们隔离开，在自动测试中，使用固定的数据。&nbsp;<br />
<br />
o&nbsp;多线程运行的程序。多线程的程序也很难自动测试，比如向链表中插入一个元素，当你检查的时候，根本无法知道链表的长度是否增加，也无法知道刚才插入的位置是否是你插入的元素，因为这个时候，可能有另外一个线程已经把它删除了，或者又加入了新的数据。不过在单线程的自动测试通过之后，多线程的问题会大大减少，剩下的问题我们可以通过其它方式加以避免。&nbsp;<br />
<br />
写自动测试程序会花你一些时间，但这个投资能带来最大的回报：减少后面调试时的浪费，提高代码的质量，更重要的是你可以安稳的睡个觉了。&nbsp;<br />
<br />
Save&nbsp;your&nbsp;work&nbsp;<br />
<br />
&ldquo;Ernst和Young所在的小组决定使用正规的开发理论&mdash;他们常用削减法，分阶段进行开发并具有中途交付能力。他们的步骤包括细致的分析和设计&mdash;正如本章描写的基本原则一样。而其他竞争者径直开始了编码，在开始几个小时里，Ernst和Young小组落后了。但到中午时Ernst和Young小组却是遥遥领先了，而到了这一天的最后，他们却失败了。导致失败的原因不是因为他们的正规方法，而是他们偶然错误的把工作文件覆盖了，最终他们比午餐时所做的估计少交付了一些功能，他们是被没有使用有效的源程序版本控制这个典型的错误给打败了。&rdquo;&nbsp;<br />
<br />
&ndash;摘自《快速软件开发》&nbsp;<br />
<br />
<br />
前段时间看探索频道的《荒野求生秘技<font color="#800080"><b>(</b></font>Man&nbsp;<font color="#4b0082">&amp;</font>&nbsp;wild<font color="#800080"><b>)</b></font>》，我很喜欢这个节目也喜欢那个英国佬，甚至连重播都不会放过。他展示在沙漠、丛林、冰河和雪山等各种环境的求生秘技，他吃蜘蛛、白蚁、蝎子和蜥蜴，边吃边说这东西很恶心，但是里面含有非常的维生素，蛋白质和糖份，能够Save&nbsp;your&nbsp;life，所以要吃下去。&nbsp;<br />
<br />
在Man&nbsp;<font color="#4b0082">&amp;</font>&nbsp;Code的世界里，环境好多了，不用面临危险，寻找水源和食物根本不需要什么秘诀。这里我们不需要求生秘技去Save&nbsp;your&nbsp;life，但我们需要一些习惯去Save&nbsp;your&nbsp;work。我说过作为一名高效的程序员，不是因为他打字比别人快，而是因为他省下了别人浪费的时间，有什么比成果被毁，从头再来更浪费时间呢？下面我介绍一些习惯，它们简单有效，根本算不上什么秘技，但它们能够Save&nbsp;your&nbsp;work，让你的工作稳步前进。&nbsp;<br />
<br />
o&nbsp;随时存盘&nbsp;<br />
<br />
每次停电时，我都会听到有人惊呼，完了，我的代码没有保存！补回半小时或一个小时的工作不难，在一个好的工作环境里，这种情况一年也就会遇到几次，浪费的时间完全可以忽略不计。但是那种感觉很难受，会影响你的工作情绪，无缘无故的让你重做你的工作，和因为要改进去重做完全是两回事。在我以前工作过的一个公司，有段时间经常跳闸，每周都要停好几次，怎么也找不到原因，后来请人来查，据说是线路太长，静电引起的跳闸。经过那段时间的折磨，我养成了一个习惯：写代码的时候，平均<font color="#ff0000">30</font>秒钟存盘一次。现在遇到停电，别人惊呼的时候，我开始闭目养神了。&nbsp;<br />
<br />
o&nbsp;使用版本控制系统&nbsp;<br />
<br />
和一些老程序员聊天时<font color="#800080"><b>(</b></font>呵，其实我也老了<font color="#800080"><b>)</b></font>，他们经常问起我们项目有没有使用版本控制系统，我说当然有了，大二的时候就我用Sourcesafe来管理用powerbuilder写的代码了，后来的工作中一直在使用不同的版本控制系统。接着他们开始讲述他们惨痛的经历&hellip;这些经历小则让他们项目延期，大则导致整个项目失败。&nbsp;<br />
<br />
版本控制系统有很多功能，但对我个人来说，它最重要的功能是备份代码。每完成一个小功能，我都会把它提交<font color="#800080"><b>(</b></font>checkin<font color="#800080"><b>)</b></font>进去，如果我不小心删除了本地文件，或者某个做尝试的修改失败了，我可以恢复代码到前一个版本。不同团队有不同的规则，有的团队是不允许这样checkin的，他们只允许checkin经过严格测试的代码。如果是那样，你可以在本地建立自己的版本控制系统，初学者在学习时也可以这样做。现在有很多免费的版本控制系统可用，像CVS、SVN和GIT等等，我个人习惯用CVS，SVN是CVS的改进版，将来肯定会替代CVS的，所以推荐大家使用它。&nbsp;<br />
<br />
o&nbsp;定期备份&nbsp;<br />
<br />
温伯格在《Quality&nbsp;Software&nbsp;Management<font color="#4b0082">:</font>&nbsp;System&nbsp;Thinking》讲了一个有趣的故事，他以前去研究一些失败的案例，发现这些项目的失败都是因为欠佳的运气引起的：比如遭受到洪水、地震、火灾和流行感冒等灾害，项目主管们把自己描述成外部问题的受害者。他又对另外一些成功的项目进行研究，发现其中有些项目同样经历这些自然灾害，但是他们成功的完成了任务。区别只是在于成功项目的主管，采用积极预防措施，定期备份代码，把它们放到不同的地点。&nbsp;<br />
<br />
以前在学校的时候，我有两台电脑，一台赛扬和一台<font color="#ff0000">486</font>。我经常在上面重装系统，一会儿装Linux，一会儿装NT，一会儿又装Netware。虽然我经常把代码备份到不同的分区上，结果还不小心把所有分区全干掉了，让我痛心不已。那只是写的一些小程序，重写一遍问题也不大，但是对于专业程序员或一个软件团队来说，重写整个代码就不能接受了，所以需要更可靠的备份机制。&nbsp;<br />
<br />
使用源代码管理系统还不能保证代码的安全，比如服务器硬盘损坏和办公室发生火灾等都是可能发生。团队里一定要有人负责定期备份源代码管理系统系统上的资料，作为初学者也应该有这种意识。另外，我发现有些朋友把重要的资料放在邮箱里，现在的邮箱容量很大，因为提供商会定期备份，非常安全，这倒是一个不错的主意。&nbsp;<br />
<br />
o&nbsp;状态不好就做点别的&nbsp;<br />
<br />
女同胞有定期状态不佳的时候，男同胞也不是每天状态都很好。感冒了、丢东西了、或者家人争吵了，都会影响你的状态。状态不好的时候做事，往往是进一步退两步，甚至犯下严重的错误。有次我得了重感冒，居然在服务器的根目录下运行rm&nbsp;<font color="#4b0082">*</font>&nbsp;<font color="#4b0082">-</font>rf<font color="#800080"><b>(</b></font>删除全部文件<font color="#800080"><b>)</b></font>，由于删除的时间太长，才让我发现删错地方了，吓得我出了一身冷汗，还好那台服务器不是运行着源代码管理系统，但还是浪费了我两天时间去重建服务器上的环境。&nbsp;<br />
<br />
状态不好的时候编程也会犯一些低级错误，让你花费更多时间去调试。总要言之，状态不好的去做重要的事有害无益，这时你不防去做点别做的，比如看看其它模块的代码之类的，甚至完全放松去休息都比犯下严重的错误强。&nbsp;<br />
&nbsp;</p>]]></description>
		</item>
		
			<item>
			<link>http://www.wscxy.com/shosh/article.asp?id=91</link>
			<title><![CDATA[高中考语法，大学考词汇]]></title>
			<author>shosh.zhu@qisda.com(shosh)</author>
			<category><![CDATA[English Learning]]></category>
			<pubDate>Sun,01 Mar 2009 22:26:24 +0800</pubDate>
			<guid>http://www.wscxy.com/shosh/default.asp?id=91</guid>
		<description><![CDATA[最近喜欢练习英语口语，今日不知为何，总结了一下英语学习的问题：高中考语法，大学考词汇。<br/>高中的时候，我也是在看了一本语法书时候，才发现自己的英语“突飞猛进”的。其实老师也很注重语法，基本上每篇课文，老师都会找出文中的语法现象加以分析，而课文的编排上，基本上每个单元都在展示一种语法现象。英语的语法实在复杂，想要完全掌握，是很困难的。要掌握好英语语法，其实也是考验一个人的记忆能力的。英语单词尚可根据读音来记忆，不必死记硬背，而语法，有的地方，还真得靠死记硬背。（插句题外话，说到记忆，想起我们高中的化学反应中化合价变化的记忆口诀来，什么“生虱痒”，记的实际上是“升失氧”，靠记住“生了虱子会痒”来记忆“化合价上升，失去电子，是氧化反应”，所以即使大学4年加上工作2年我毫不接触化学，对它也都还能记忆犹新。）<br/><br/>大一的课程，主要有两门，分别是高等数学和大学英语。大一的上课，给我的印象就是每天有上不完的英语课。大一结束前，我们就参加了全国大学英语四级的考试。最后以76.5的中等成绩过关。其实我对等级考试并不在意的，只是当时学校毕业是要求英语四级和计算机二级的，所以我在大一过了英语四级，大二过了计算机二级（当时C语言学得很好，全国的C语言二级收费较高且难度较低，没有挑战性，所以我过的是江苏省Visual C++的二级）。<br/><br/>虽然我对其他的等级或认证考试不感兴趣，不过其实除了参加了全国英语四级和江苏省计算机二级的考试，我在大三的时候还参加了一次英语六级的考试，这是我至今参加的唯一一次不必参加的考试。当时是寒假前英语学习热情高涨，经常去学校御花园和一些朋友一起学习英语（口语为主），寒假后为了保持或提高对英语的学习热情，通过报名参加英语六级的方式来给自己的学习增添压力。不过这只是在报名当时起了点作用，过了两天，便没有了效力。且后来去御花园学习英语的次数也减少了，最后就随随便便毫无压力地进了考场。结果可想而知，自然通过不了。不过其实六级的试题的听力、作文、阅读、改错等做得都还不错的，挂就挂在单项选择上。单项选择考的其实就是词汇量的大小，而且所占的比重很大（记忆中有110分左右，当时英语等级考试已经改革，不再是100分数制，而是几百分数制，具体几分制，我现在也忘记了，反正超过400分）。当时做单选的时候，感觉自己完全是四选一瞎蒙的，最多确定去掉一个错误答案，按照概率论的方法来计算，单选的分数只能得到1/4的分数，最多也就1/3.<br/><br/>所以确是如此，高中考语法（词汇就这么多，一单元都上一星期，一单元的新单词也就20个左右，一个星期记住20个，在当时的学习强度下，应该不再话下），大学考词汇（一篇课文的词汇就铺天盖地；所以有的人看着平时英语很好，考起等级来也不咋的，因为词汇量不大；而有的人虽然让他读篇英语文章都结结巴巴的，但是考起等级来却牛逼哄哄的，因为对他来说没多少新词，看懂试卷如履平地）。]]></description>
		</item>
		
			<item>
			<link>http://www.wscxy.com/shosh/article.asp?id=90</link>
			<title><![CDATA[inline的一种特殊用法]]></title>
			<author>shosh.zhu@qisda.com(shosh)</author>
			<category><![CDATA[brew开发]]></category>
			<pubDate>Tue,24 Feb 2009 10:44:52 +0800</pubDate>
			<guid>http://www.wscxy.com/shosh/default.asp?id=90</guid>
		<description><![CDATA[<p>曾经我在一个c文件里用几个函数实现了一个比较Common的Functions，并提供一个对应的h文件让使用者<font color="#0000ff">#include</font>。接口如下：<br />
<br />
<font color="#008000">//please&nbsp;do&nbsp;not&nbsp;call&nbsp;these&nbsp;functions&nbsp;directly</font><br />
<font color="#0000ff">int</font>&nbsp;WriteTrace<font color="#800080"><b>(</b></font><font color="#0000ff">int</font>&nbsp;nLevel<font color="#4b0082">,</font>&nbsp;<font color="#0000ff">char</font><font color="#4b0082">*</font>&nbsp;pFile<font color="#4b0082">,</font>&nbsp;<font color="#0000ff">int</font>&nbsp;nLine<font color="#4b0082">,</font>&nbsp;<font color="#0000ff">char</font><font color="#4b0082">*</font>&nbsp;format<font color="#4b0082">,</font>&nbsp;<font color="#4b0082">...</font><font color="#800080"><b>)</b></font><font color="#4b0082">;</font><br />
<font color="#0000ff">void</font>&nbsp;EndWriteTrace<font color="#800080"><b>(</b></font><font color="#0000ff">void</font><font color="#800080"><b>)</b></font><font color="#4b0082">;</font><br />
<br />
这个Common的功能会在很多地方被人调用，但是它只在target端（真实运行环境）运行，而在simulator端（模拟运行环境，为方便调试加快编程速度而引入的模拟的运行环境）不需要<font color="#4b0082">/</font>无法运行该功能。所以我定义了下面一些宏，让用户通过宏来调用这些提供出来的接口：</p>
<p><font color="#008000">//please&nbsp;use&nbsp;these&nbsp;macros&nbsp;instead</font><br />
<font color="#0000ff">#ifdef</font>&nbsp;AEE_SIMULATOR&nbsp;<font color="#008000">//don't&nbsp;let&nbsp;it&nbsp;run&nbsp;in&nbsp;simulator</font><br />
&nbsp;&nbsp;&nbsp;&nbsp;<font color="#0000ff">#define</font>&nbsp;TRACE_FILE<br />
&nbsp;&nbsp;&nbsp;&nbsp;<font color="#0000ff">#define</font>&nbsp;END_TRACE<br />
<font color="#0000ff">#else</font><br />
&nbsp;&nbsp;&nbsp;&nbsp;<font color="#0000ff">#define</font>&nbsp;TRACE_FILE&nbsp;WriteTrace<br />
&nbsp;&nbsp;&nbsp;&nbsp;<font color="#0000ff">#define</font>&nbsp;END_TRACE&nbsp;&nbsp;EndWriteTrace<br />
<font color="#0000ff">#endif</font><br />
<br />
为了方便描述问题，模拟地写了一段代码：</p>
<p>&nbsp;</p>
<div id="shoCodeAreaWscxy">
<ol id="shoCodeMain107ID" style="border-right: gray 1px solid; padding-right: 2px; border-top: gray 1px solid; padding-left: 2px; padding-bottom: 2px; margin: 0px; border-left: gray 1px solid; color: #2f4f4f; word-break: break-all; padding-top: 2px; border-bottom: gray 1px solid; font-family: Courier New; list-style-type: decimal; background-color: #dcf5dc">
    <li style="padding-left: 6px; margin: 0px 0px 0px 50px; border-left: silver 2px ridge; line-height: 18px; background-color: #f5fffa"><span style="color: black"><font color="#0000ff">int</font>&nbsp;WriteTrace<font color="#800080"><b>(</b></font><font color="#0000ff">int</font>&nbsp;nLevel<font color="#4b0082">,</font>&nbsp;<font color="#0000ff">int</font>&nbsp;add<font color="#800080"><b>)</b></font></span></li>
    <li style="padding-left: 6px; margin: 0px 0px 0px 50px; border-left: silver 2px ridge; line-height: 18px; background-color: #f5fffa"><span style="color: black"><font color="#800080"><b>{</b></font></span></li>
    <li style="padding-left: 6px; margin: 0px 0px 0px 50px; border-left: silver 2px ridge; line-height: 18px; background-color: #f5fffa"><span style="color: black">&nbsp;&nbsp;&nbsp;&nbsp;<font color="#0000ff">return</font>&nbsp;nLevel&nbsp;<font color="#4b0082">+</font>&nbsp;add<font color="#4b0082">;</font></span></li>
    <li style="padding-left: 6px; margin: 0px 0px 0px 50px; border-left: silver 2px ridge; line-height: 18px; background-color: #f5fffa"><span style="color: black"><font color="#800080"><b>}</b></font></span></li>
    <li style="padding-left: 6px; margin: 0px 0px 0px 50px; border-left: silver 2px ridge; line-height: 18px; background-color: #f5fffa"><span style="color: black"><font color="#0000ff">void</font>&nbsp;EndWriteTrace<font color="#800080"><b>(</b></font><font color="#0000ff">void</font><font color="#800080"><b>)</b></font></span></li>
    <li style="padding-left: 6px; margin: 0px 0px 0px 50px; border-left: silver 2px ridge; line-height: 18px; background-color: #f5fffa"><span style="color: black"><font color="#800080"><b>{</b></font></span></li>
    <li style="padding-left: 6px; margin: 0px 0px 0px 50px; border-left: silver 2px ridge; line-height: 18px; background-color: #f5fffa"><span style="color: black">&nbsp;&nbsp;&nbsp;&nbsp;<font color="#0000ff">return</font><font color="#4b0082">;</font></span></li>
    <li style="padding-left: 6px; margin: 0px 0px 0px 50px; border-left: silver 2px ridge; line-height: 18px; background-color: #f5fffa"><span style="color: black"><font color="#800080"><b>}</b></font></span></li>
    <li style="padding-left: 6px; margin: 0px 0px 0px 50px; border-left: silver 2px ridge; line-height: 18px; background-color: #f5fffa"><span style="color: black"><font color="#008000">//#define&nbsp;AEE_SIMULATOR</font></span></li>
    <li style="padding-left: 6px; margin: 0px 0px 0px 50px; border-left: silver 2px ridge; line-height: 18px; background-color: #f5fffa"><span style="color: black"><font color="#0000ff">#ifdef</font>&nbsp;AEE_SIMULATOR&nbsp;<font color="#008000">//don't&nbsp;let&nbsp;it&nbsp;run&nbsp;in&nbsp;simulator</font></span></li>
    <li style="padding-left: 6px; margin: 0px 0px 0px 50px; border-left: silver 2px ridge; line-height: 18px; background-color: #f5fffa"><span style="color: black">&nbsp;&nbsp;&nbsp;&nbsp;<font color="#0000ff">#define</font>&nbsp;TRACE_FILE</span></li>
    <li style="padding-left: 6px; margin: 0px 0px 0px 50px; border-left: silver 2px ridge; line-height: 18px; background-color: #f5fffa"><span style="color: black">&nbsp;&nbsp;&nbsp;&nbsp;<font color="#0000ff">#define</font>&nbsp;END_TRACE</span></li>
    <li style="padding-left: 6px; margin: 0px 0px 0px 50px; border-left: silver 2px ridge; line-height: 18px; background-color: #f5fffa"><span style="color: black"><font color="#0000ff">#else</font></span></li>
    <li style="padding-left: 6px; margin: 0px 0px 0px 50px; border-left: silver 2px ridge; line-height: 18px; background-color: #f5fffa"><span style="color: black">&nbsp;&nbsp;&nbsp;&nbsp;<font color="#0000ff">#define</font>&nbsp;TRACE_FILE&nbsp;WriteTrace</span></li>
    <li style="padding-left: 6px; margin: 0px 0px 0px 50px; border-left: silver 2px ridge; line-height: 18px; background-color: #f5fffa"><span style="color: black">&nbsp;&nbsp;&nbsp;&nbsp;<font color="#0000ff">#define</font>&nbsp;END_TRACE&nbsp;&nbsp;EndWriteTrace</span></li>
    <li style="padding-left: 6px; margin: 0px 0px 0px 50px; border-left: silver 2px ridge; line-height: 18px; background-color: #f5fffa"><span style="color: black"><font color="#0000ff">#endif</font></span></li>
    <li style="padding-left: 6px; margin: 0px 0px 0px 50px; border-left: silver 2px ridge; line-height: 18px; background-color: #f5fffa">&nbsp;</li>
    <li style="padding-left: 6px; margin: 0px 0px 0px 50px; border-left: silver 2px ridge; line-height: 18px; background-color: #f5fffa"><span style="color: black"><font color="#0000ff">int</font>&nbsp;main<font color="#800080"><b>(</b></font><font color="#0000ff">void</font><font color="#800080"><b>)</b></font></span></li>
    <li style="padding-left: 6px; margin: 0px 0px 0px 50px; border-left: silver 2px ridge; line-height: 18px; background-color: #f5fffa"><span style="color: black"><font color="#800080"><b>{</b></font></span></li>
    <li style="padding-left: 6px; margin: 0px 0px 0px 50px; border-left: silver 2px ridge; line-height: 18px; background-color: #f5fffa"><span style="color: black">&nbsp;&nbsp;&nbsp;&nbsp;TRACE_FILE<font color="#800080"><b>(</b></font><font color="#ff0000">1</font><font color="#4b0082">,</font>&nbsp;<font color="#ff0000">3</font><font color="#800080"><b>)</b></font><font color="#4b0082">;</font></span></li>
    <li style="padding-left: 6px; margin: 0px 0px 0px 50px; border-left: silver 2px ridge; line-height: 18px; background-color: #f5fffa"><span style="color: black">&nbsp;&nbsp;&nbsp;&nbsp;END_TRACE<font color="#800080"><b>()</b></font><font color="#4b0082">;</font></span></li>
    <li style="padding-left: 6px; margin: 0px 0px 0px 50px; border-left: silver 2px ridge; line-height: 18px; background-color: #f5fffa"><span style="color: black">&nbsp;&nbsp;&nbsp;&nbsp;<font color="#0000ff">return</font>&nbsp;<font color="#ff0000">0</font><font color="#4b0082">;</font></span></li>
    <li style="padding-left: 6px; margin: 0px 0px 0px 50px; border-left: silver 2px ridge; line-height: 18px; background-color: #f5fffa"><span style="color: black"><font color="#800080"><b>}</b></font>&nbsp;</span></li>
</ol>
</div>
<p><br />
当AEE_SIMULATOR未被定义时，那么TRACE_FILE就是WriteTrace，END_TRACE就是EndWriteTrace，也就是前文提到的在target端运行，这样不存在任何问题，可以无错误无警告通过编译。但是如果定义了AEE_SIMULATOR，也就是想编译出在simulator上运行的代码，会发现会有一个错误。错误原因很容易发现，因为main函数体内第二行语句END_TRACE<font color="#800080"><b>()</b></font><font color="#4b0082">;</font>宏展开后的代码是<font color="#800080"><b>()</b></font><font color="#4b0082">;</font>，这句代码自然不可以编译通过了。那第一行TRACE_FILE<font color="#800080"><b>(</b></font><font color="#ff0000">1</font><font color="#4b0082">,</font>&nbsp;<font color="#ff0000">3</font><font color="#800080"><b>)</b></font><font color="#4b0082">;</font>宏展开后是<font color="#800080"><b>(</b></font><font color="#ff0000">1</font><font color="#4b0082">,</font>&nbsp;<font color="#ff0000">3</font><font color="#800080"><b>)</b></font><font color="#4b0082">;</font>为什么没有错误也没有警告呢？因为<font color="#800080"><b>(</b></font><font color="#ff0000">1</font><font color="#4b0082">,</font>&nbsp;<font color="#ff0000">3</font><font color="#800080"><b>)</b></font><font color="#4b0082">;</font>是C语言中的表达式（逗号运算，表达式的值是<font color="#ff0000">3</font>），就算是<font color="#800080"><b>(</b></font><font color="#ff0000">1</font><font color="#800080"><b>)</b></font><font color="#4b0082">;</font>&nbsp;也是C语言中的表达式（表达式的值是<font color="#ff0000">1</font>），没有错误。因为<font color="#800080"><b>(</b></font><font color="#0000ff">void</font><font color="#800080"><b>)</b></font><font color="#4b0082">;</font>不是表达式（<font color="#0000ff">void</font>不是值），所以就算将END_TRACE<font color="#800080"><b>()</b></font>改成END_TRACE<font color="#800080"><b>(</b></font><font color="#0000ff">void</font><font color="#800080"><b>)</b></font>也是不能通过编译的。<br />
<br />
要解决这样的问题，我最先想到了两种解决方案：<br />
<br />
<font color="#ff0000">1</font>、给END_TRACE添加一个无效的参数。对于target端，多了一个无效参数，有一定的资源浪费，不过影响小到可以忽略。<br />
<br />
<font color="#ff0000">2</font>、在simulator上实现一个空函数<font color="#0000ff">void</font>&nbsp;funNum<font color="#800080"><b>(</b></font><font color="#0000ff">void</font><font color="#800080"><b>)</b></font>，将END_TRACE定义为该函数的函数名funNum，这样做对target端无任何影响，但是simulator端多了一个空函数，加大了编译出来的文件，不过影响同样很小。关键是要添加空函数<font color="#0000ff">void</font>&nbsp;funNum<font color="#800080"><b>(</b></font><font color="#0000ff">void</font><font color="#800080"><b>)</b></font>的实现，当然最适合放在我提供的那个c文件里。不过这样需要增加工作量，在simulator的编译工程中也需要将该c文件编译进来，需要修改到dsp文件，而实际上我们不希望这样（在simulator上只需要包含h文件即可编译通过了，那c文件不需要编译，也不需要加到工程中）。<br />
<br />
后来想到了一个完美的方法，既解决了问题，又没有上面的问题。那就是在h文件中实现空函数<font color="#0000ff">void</font>&nbsp;funNum<font color="#800080"><b>(</b></font><font color="#0000ff">void</font><font color="#800080"><b>)</b></font>，不过得加关键字<font color="#0000ff">__inline</font>。 <br />
<br />
&nbsp;</p>
<ol id="shoCodeMain353ID" style="border-right: gray 1px solid; padding-right: 2px; border-top: gray 1px solid; padding-left: 2px; padding-bottom: 2px; margin: 0px; border-left: gray 1px solid; color: #2f4f4f; word-break: break-all; padding-top: 2px; border-bottom: gray 1px solid; font-family: Courier New; list-style-type: decimal; background-color: #dcf5dc">
    <li style="padding-left: 6px; margin: 0px 0px 0px 50px; border-left: silver 2px ridge; line-height: 18px; background-color: #f5fffa"><span style="color: black"><font color="#008000">//-------------in&nbsp;.c&nbsp;file------------------------//</font></span></li>
    <li style="padding-left: 6px; margin: 0px 0px 0px 50px; border-left: silver 2px ridge; line-height: 18px; background-color: #f5fffa"><span style="color: black"><font color="#0000ff">int</font>&nbsp;WriteTrace<font color="#800080"><b>(</b></font><font color="#0000ff">int</font>&nbsp;nLevel<font color="#4b0082">,</font>&nbsp;<font color="#0000ff">int</font>&nbsp;add<font color="#800080"><b>)</b></font>&nbsp;</span></li>
    <li style="padding-left: 6px; margin: 0px 0px 0px 50px; border-left: silver 2px ridge; line-height: 18px; background-color: #f5fffa"><span style="color: black"><font color="#800080"><b>{</b></font>&nbsp;</span></li>
    <li style="padding-left: 6px; margin: 0px 0px 0px 50px; border-left: silver 2px ridge; line-height: 18px; background-color: #f5fffa"><span style="color: black">&nbsp;&nbsp;&nbsp;&nbsp;<font color="#0000ff">return</font>&nbsp;nLevel&nbsp;<font color="#4b0082">+</font>&nbsp;add<font color="#4b0082">;</font>&nbsp;</span></li>
    <li style="padding-left: 6px; margin: 0px 0px 0px 50px; border-left: silver 2px ridge; line-height: 18px; background-color: #f5fffa"><span style="color: black"><font color="#800080"><b>}</b></font>&nbsp;</span></li>
    <li style="padding-left: 6px; margin: 0px 0px 0px 50px; border-left: silver 2px ridge; line-height: 18px; background-color: #f5fffa"><span style="color: black"><font color="#0000ff">void</font>&nbsp;EndWriteTrace<font color="#800080"><b>(</b></font><font color="#0000ff">void</font><font color="#800080"><b>)</b></font>&nbsp;</span></li>
    <li style="padding-left: 6px; margin: 0px 0px 0px 50px; border-left: silver 2px ridge; line-height: 18px; background-color: #f5fffa"><span style="color: black"><font color="#800080"><b>{</b></font>&nbsp;</span></li>
    <li style="padding-left: 6px; margin: 0px 0px 0px 50px; border-left: silver 2px ridge; line-height: 18px; background-color: #f5fffa"><span style="color: black">&nbsp;&nbsp;&nbsp;&nbsp;<font color="#0000ff">return</font><font color="#4b0082">;</font>&nbsp;</span></li>
    <li style="padding-left: 6px; margin: 0px 0px 0px 50px; border-left: silver 2px ridge; line-height: 18px; background-color: #f5fffa"><span style="color: black"><font color="#800080"><b>}</b></font></span></li>
    <li style="padding-left: 6px; margin: 0px 0px 0px 50px; border-left: silver 2px ridge; line-height: 18px; background-color: #f5fffa"><span style="color: black">&nbsp;</span></li>
    <li style="padding-left: 6px; margin: 0px 0px 0px 50px; border-left: silver 2px ridge; line-height: 18px; background-color: #f5fffa"><span style="color: black"><font color="#008000">//---------in&nbsp;.h&nbsp;file&nbsp;that&nbsp;for&nbsp;users&nbsp;to&nbsp;include------//</font></span></li>
    <li style="padding-left: 6px; margin: 0px 0px 0px 50px; border-left: silver 2px ridge; line-height: 18px; background-color: #f5fffa"><span style="color: black"><font color="#0000ff">__inline</font>&nbsp;<font color="#0000ff">void</font>&nbsp;FuncName<font color="#800080"><b>(</b></font><font color="#0000ff">void</font><font color="#800080"><b>)</b></font></span></li>
    <li style="padding-left: 6px; margin: 0px 0px 0px 50px; border-left: silver 2px ridge; line-height: 18px; background-color: #f5fffa"><span style="color: black"><font color="#800080"><b>{</b></font></span></li>
    <li style="padding-left: 6px; margin: 0px 0px 0px 50px; border-left: silver 2px ridge; line-height: 18px; background-color: #f5fffa"><span style="color: black">&nbsp;&nbsp;&nbsp;&nbsp;<font color="#008000">//this&nbsp;function's&nbsp;just&nbsp;empty&nbsp;for&nbsp;special&nbsp;usage</font></span></li>
    <li style="padding-left: 6px; margin: 0px 0px 0px 50px; border-left: silver 2px ridge; line-height: 18px; background-color: #f5fffa"><span style="color: black"><font color="#800080"><b>}</b></font></span></li>
    <li style="padding-left: 6px; margin: 0px 0px 0px 50px; border-left: silver 2px ridge; line-height: 18px; background-color: #f5fffa"><span style="color: black"><font color="#008000">//#define&nbsp;AEE_SIMULATOR&nbsp;</font></span></li>
    <li style="padding-left: 6px; margin: 0px 0px 0px 50px; border-left: silver 2px ridge; line-height: 18px; background-color: #f5fffa"><span style="color: black"><font color="#0000ff">#ifdef</font>&nbsp;AEE_SIMULATOR&nbsp;<font color="#008000">//don't&nbsp;let&nbsp;it&nbsp;run&nbsp;in&nbsp;simulator&nbsp;</font></span></li>
    <li style="padding-left: 6px; margin: 0px 0px 0px 50px; border-left: silver 2px ridge; line-height: 18px; background-color: #f5fffa"><span style="color: black">&nbsp;&nbsp;&nbsp;&nbsp;<font color="#0000ff">#define</font>&nbsp;TRACE_FILE&nbsp;</span></li>
    <li style="padding-left: 6px; margin: 0px 0px 0px 50px; border-left: silver 2px ridge; line-height: 18px; background-color: #f5fffa"><span style="color: black">&nbsp;&nbsp;&nbsp;&nbsp;<font color="#0000ff">#define</font>&nbsp;END_TRACE&nbsp;FuncName</span></li>
    <li style="padding-left: 6px; margin: 0px 0px 0px 50px; border-left: silver 2px ridge; line-height: 18px; background-color: #f5fffa"><span style="color: black"><font color="#0000ff">#else</font>&nbsp;</span></li>
    <li style="padding-left: 6px; margin: 0px 0px 0px 50px; border-left: silver 2px ridge; line-height: 18px; background-color: #f5fffa"><span style="color: black">&nbsp;&nbsp;&nbsp;&nbsp;<font color="#0000ff">#define</font>&nbsp;TRACE_FILE&nbsp;WriteTrace&nbsp;</span></li>
    <li style="padding-left: 6px; margin: 0px 0px 0px 50px; border-left: silver 2px ridge; line-height: 18px; background-color: #f5fffa"><span style="color: black">&nbsp;&nbsp;&nbsp;&nbsp;<font color="#0000ff">#define</font>&nbsp;END_TRACE&nbsp;&nbsp;EndWriteTrace&nbsp;</span></li>
    <li style="padding-left: 6px; margin: 0px 0px 0px 50px; border-left: silver 2px ridge; line-height: 18px; background-color: #f5fffa"><span style="color: black"><font color="#0000ff">#endif</font>&nbsp;</span></li>
    <li style="padding-left: 6px; margin: 0px 0px 0px 50px; border-left: silver 2px ridge; line-height: 18px; background-color: #f5fffa"><span style="color: black">&nbsp;</span></li>
    <li style="padding-left: 6px; margin: 0px 0px 0px 50px; border-left: silver 2px ridge; line-height: 18px; background-color: #f5fffa"><span style="color: black"><font color="#008000">//------------in&nbsp;test.c&nbsp;file------------//</font></span></li>
    <li style="padding-left: 6px; margin: 0px 0px 0px 50px; border-left: silver 2px ridge; line-height: 18px; background-color: #f5fffa"><span style="color: black"><font color="#0000ff">int</font>&nbsp;main<font color="#800080"><b>(</b></font><font color="#0000ff">void</font><font color="#800080"><b>)</b></font>&nbsp;</span></li>
    <li style="padding-left: 6px; margin: 0px 0px 0px 50px; border-left: silver 2px ridge; line-height: 18px; background-color: #f5fffa"><span style="color: black"><font color="#800080"><b>{</b></font>&nbsp;</span></li>
    <li style="padding-left: 6px; margin: 0px 0px 0px 50px; border-left: silver 2px ridge; line-height: 18px; background-color: #f5fffa"><span style="color: black">&nbsp;&nbsp;&nbsp;&nbsp;TRACE_FILE<font color="#800080"><b>(</b></font><font color="#ff0000">1</font><font color="#4b0082">,</font>&nbsp;<font color="#ff0000">3</font><font color="#800080"><b>)</b></font><font color="#4b0082">;</font>&nbsp;</span></li>
    <li style="padding-left: 6px; margin: 0px 0px 0px 50px; border-left: silver 2px ridge; line-height: 18px; background-color: #f5fffa"><span style="color: black">&nbsp;&nbsp;&nbsp;&nbsp;END_TRACE<font color="#800080"><b>()</b></font><font color="#4b0082">;</font>&nbsp;</span></li>
    <li style="padding-left: 6px; margin: 0px 0px 0px 50px; border-left: silver 2px ridge; line-height: 18px; background-color: #f5fffa"><span style="color: black">&nbsp;&nbsp;&nbsp;&nbsp;<font color="#0000ff">return</font>&nbsp;<font color="#ff0000">0</font><font color="#4b0082">;</font>&nbsp;</span></li>
    <li style="padding-left: 6px; margin: 0px 0px 0px 50px; border-left: silver 2px ridge; line-height: 18px; background-color: #f5fffa"><span style="color: black"><font color="#800080"><b>}</b></font>&nbsp;&nbsp;</span></li>
</ol>
<p>&nbsp;</p>]]></description>
		</item>
		
			<item>
			<link>http://www.wscxy.com/shosh/article.asp?id=87</link>
			<title><![CDATA[Shosh谈手机的发展]]></title>
			<author>shosh.zhu@qisda.com(shosh)</author>
			<category><![CDATA[brew开发]]></category>
			<pubDate>Thu,19 Feb 2009 13:47:50 +0800</pubDate>
			<guid>http://www.wscxy.com/shosh/default.asp?id=87</guid>
		<description><![CDATA[<p>昨晚有朋友问我：&ldquo;手机行业已入夕阳，我们这些做手机开发的将何去何从？&rdquo;<br />
之前尚未细想，现在来谈谈敝人的一点看法。如需转载，请保留作者信息：Shosh，http://www.wscxy.com，2009年2月19日。</p>
<p>在手机之前，是固定电话。固定电话需要放在固定的位置，如家里或办公室。而人是需要经常移动的，这样就会存在找不到人或错过一些电话的缺点。手机正是通过其移动性来解决该问题的产物。最开始的时候，手机很大，携带相当不便，且功能很少（当时设计的需求估计focus在能够接听拨打电话上），价格却相当昂贵。不过那个时候能够拥有这样一部手机的人是相当酷的，所以名字也很酷，叫&ldquo;大哥大&rdquo;，颇有黑社会老大的味道在里面。<br />
新事物一旦诞生，如果它具有生命力，它的发展就会相当迅速。人们开始不满足于黑白屏幕，开始不满足于光打电话光发短信。随着液晶屏幕技术上的发展，彩屏慢慢进入了人们的视野。而在手机功能上，电子书、音乐、广播、视频、相机、摄像以及蓝牙红外等短距离通信等功能和其他一些辅助功能（如日历、闹钟等）也日趋丰富，曾经就有很多款手机主打音乐旗帜拉动消费；而最近手机又融入了GPS导航、信息同步、计步感应器、手机上网等功能。我们老大曾经说过一句话，一点也不错：电脑的今天就是手机的明天。手机的发展确实紧随电脑的发展，而智能手机则让手机更加接近电脑。电脑上有鼠标，而手机没有鼠标，于是随着触摸技术的发展，很多手机配上了触摸屏，而Apple的手机（iPhone）更是使用了多点触控技术，凭借其独到的设计和绚丽的屏幕以及Apple自身所具有的品牌影响力，赢得了不少顾客和广大的追随者、赞赏者（触控技术是一项让手机赢过桌面电脑率先使用的技术，其实它在多功能查询机、ATM存/取款机等设备上早有应用）。可以说，目前手机上该有的功能都已经具备，只剩下各家运营商想破脑袋想出来的各种用来诱惑消费者来使用他们的网络成为他们的客户的功能还可以推陈出新了。</p>
<p>总体来说，以本人的理解和概括，随着高速网络的普及和高性能手机硬件的支持，手机最近及未来几年内的发展主要有以下几个方面：<br />
1、动画效果应用将更加广泛，手机的UI将变得更加绚丽。<br />
最近在评估一些新的案子，要求里的动画效果相当绚丽，绝对超过Apple手机（iPhone）的。不过如果不用flash，不用3D引擎，即使对BUIW再熟悉，纯粹用BUIW，纯粹用C Code，除了代码冗长、维护困难，恐怕效果也会不理想。不过这肯定是一个方向，最近接到的案子，动画效果都很炫。工程开发的时候，应该将这部分架构好，而不应该碰到一个效果，从头开始设计一个。<br />
2、随着3G网络的普及，视频通话、流媒体在线收看、手机电视、在线游戏等将得到应用。<br />
其实几年前，就算在电脑上想在线收看一部电影，也是相当困难的，尤其是免费的。两三年前，将Flash视频嵌入到网页中才使越来越多的视频资源在网络上免费呈现，随后各种在线点播系统、各种现在播放软件纷纷出现，就连一向在媒体播放领域大名顶顶的暴风影音也开始将在其播放器中加入在线播放的功能。其实目前，很多手机已经具备视频通话的功能，只是需要网络的支持。高速的3G网络普及后，如果这些功能的价位低廉，它们将得到广泛应用。所以程序员往这个方向发展，应该是一条不错的路子。<br />
3、3D游戏也将会在手机中出现。<br />
3D游戏在电脑上倒很早就已出现，而手机上的游戏到目前为止基本上都是简单的2D游戏。但本人觉得，等到时机成熟，3D游戏战火必将延伸到手机，而且很可能以网络游戏的形式出现。不过受其屏幕大小和操作不便的限制，手机游戏还是会很简单，不可能像电脑上的某些需要高操作性的游戏那么复杂。<br />
4、声控技术有可能在几年后得到应用。<br />
声控技术，就是使用声音来控制一定的设备的一种技术，如使用声音来控制个人电脑或机器人等。这个似乎还很遥远，因为桌面电脑上都还不成熟（有在尝试）。不过微软正在努力将他的操作系统开发成可以脱离鼠标键盘但仍能方便操作的系统，比如采用触摸屏，而另一种方法就是使用声控（两种方法并不冲突，应该会同步发展）。用户可以通过嘴巴说出口令来操作计算机，而打字，也将会变得只是动动嘴巴而已。不过这涉及到声音识别的准确性问题，桌面系统上都还不成熟，要在手机上应用，还需要较长的一段时间。</p>
<p>总之一句话：手机的今天就是电脑的昨天，手机的明天就是电脑的今天。发展还在继续，就看会发展成什么样子了。以上看法尚不成熟，如若有不对的地方，望勿见笑。</p>]]></description>
		</item>
		
</channel>
</rss>
