﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>语源科技BlogJava-faintbear</title><link>http://www.blogjava.net/faintbear/</link><description>小风嗖嗖的刮着......</description><language>zh-cn</language><lastBuildDate>Sat, 14 Mar 2026 17:35:40 GMT</lastBuildDate><pubDate>Sat, 14 Mar 2026 17:35:40 GMT</pubDate><ttl>60</ttl><item><title>静态局部变量</title><link>http://www.blogjava.net/faintbear/archive/2010/01/06/308408.html</link><dc:creator>小力力力</dc:creator><author>小力力力</author><pubDate>Wed, 06 Jan 2010 03:37:00 GMT</pubDate><guid>http://www.blogjava.net/faintbear/archive/2010/01/06/308408.html</guid><wfw:comment>http://www.blogjava.net/faintbear/comments/308408.html</wfw:comment><comments>http://www.blogjava.net/faintbear/archive/2010/01/06/308408.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/faintbear/comments/commentRss/308408.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/faintbear/services/trackbacks/308408.html</trackback:ping><description><![CDATA[在局部变量前加上&#8220;static&#8221;关键字，就成了静态局部变量。静态局部变量存放在内存的全局数据区。函数结束时，静态局部变量不会消失，每次该函数调用
时，也不会为其重新分配空间。它始终驻留在全局数据区，直到程序运行结束。静态局部变量的初始化与全局变量类似．如果不为其显式初始化，则C++自动为其
初始化为0。<br />
静态局部变量与全局变量共享全局数据区，但静态局部变量只在定义它的函数中可见。静态局部变量与局部变量在存储位置上不同，使得其存在的时限也不同，导致对这两者操作 的运行结果也不同。<br />
例如，下面的程序定义了全局变量、静态局部变量和局部变量：<br />
<span class="ff0000"><font color="#ff0000">　//*********************<br />
//**　　 ch5_2.cpp　 **<br />
//********************* </font></span>
<p class="ff0000"><font color="#ff0000">　　　　#include &lt;iostream.h&gt;</font></p>
<p class="ff0000"><font color="#ff0000">　　　　void func();<br />
int n=1; //全局变量</font></p>
<p class="ff0000"><font color="#ff0000">　　　　void main()<br />
{<br />
<em><strong>static</strong></em> int a; // 静态局部变量<br />
int b= -10; // 局部变量<br />
cout &lt;&lt;"a:" &lt;&lt;a<br />
&lt;&lt;" b:" &lt;&lt;b<br />
&lt;&lt;" n:" &lt;&lt;n &lt;&lt;endl;<br />
b+=4;<br />
func();<br />
cout &lt;&lt;"a:" &lt;&lt;a<br />
&lt;&lt;" b:" &lt;&lt;b<br />
&lt;&lt;" n:" &lt;&lt;n &lt;&lt;endl;<br />
n+=10;<br />
func();<br />
}</font></p>
<p class="ff0000"><font color="#ff0000">　　　　void func()<br />
{<br />
<em><strong>static</strong></em> int a=2; // 静态局部变量<br />
int b=5; // 局部变量<br />
a+=2;<br />
n+=12;<br />
b+=5;<br />
cout &lt;&lt;"a:" &lt;&lt;a<br />
&lt;&lt;" b:" &lt;&lt;b<br />
&lt;&lt;" n:" &lt;&lt;n &lt;&lt;endl;<br />
}</font></p>
<p class="p14">　　运行结果为：<br />
<span class="ff0000">　　　<font color="#ff0000">　a:0 b:-10 n:l<br />
a:4 b:10 n:13<br />
a:0 b:-6 n:13<br />
a:6 b:10 n:35</font></span><br />
程序中主函数main()两次调用了func()函数，从运行结果可以看出，程序控制每次进入func()函数时，局部变量b都被初始化。而静态局部
变量a仅在第一次调用时被初始化，第二次进入该函数时，不再进行初始化，这时它的值是第一次调用后的结果值4。
main()函数中的变量a和b与func()函数中的变量a和b空间位置是不一样的，所以相应的值也不一样。关于变量作用域和可见性的进一步讨论见第6
章。<br />
静态局部变量的用途有许多：可以使用它确定某函数是否被调用过。使用它保留多次调用的值。</p>
<p class="p14"><strong><font size="5">对静态局部变量的说明：</font></strong> <br />
(1) 静态局部变量在静态存储区内分配存储单元。在程序整个运行期间都不释放。而自动变量（即动态局部变量）属于动态存储类别，存储在动态存储区空间(而不是静态存储区空间)，函数调用结束后即释放。 <br />
(2)
为静态局部变量赋初值是在编译时进行值的，即只赋初值一次，在程序运行时它已有初值。以后每次调用函数时不再重新赋初值而只是保留上次函数调用结束时的
值。而为自动变量赋初值，不是在编译时进行的，而是在函数调用时进行，每调用一次函数重新给一次初值，相当于执行一次赋值语句。 <br />
<br />
(3)
如果在定义局部变量时不赋初值的话，对静态局部变量来说，编译时自动赋初值0(对数值型变量)或空字符(对字符型变量)。而对自动变量来说，如果不赋初
值，则它的值是一个不确定的值。这是由于每次函数调用结束后存储单元已释放，下次调用时又重新另分配存储单元，而所分配的单元中的值是不确定的。 <br />
(4) 虽然静态局部变量在函数调用结束后仍然存在，但其他函数是不能引用它的，也就是说，在其他函数中它是&#8220;不可见&#8221;的。</p>
<img src ="http://www.blogjava.net/faintbear/aggbug/308408.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/faintbear/" target="_blank">小力力力</a> 2010-01-06 11:37 <a href="http://www.blogjava.net/faintbear/archive/2010/01/06/308408.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>走近GPRS</title><link>http://www.blogjava.net/faintbear/archive/2006/09/07/68215.html</link><dc:creator>小力力力</dc:creator><author>小力力力</author><pubDate>Thu, 07 Sep 2006 03:45:00 GMT</pubDate><guid>http://www.blogjava.net/faintbear/archive/2006/09/07/68215.html</guid><wfw:comment>http://www.blogjava.net/faintbear/comments/68215.html</wfw:comment><comments>http://www.blogjava.net/faintbear/archive/2006/09/07/68215.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/faintbear/comments/commentRss/68215.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/faintbear/services/trackbacks/68215.html</trackback:ping><description><![CDATA[
		<table width="680" align="center">
				<tbody>
						<tr>
								<td align="middle">
										<h2>
												<font color="#0f3ccd">走近GPRS</font>
										</h2>
										<br />
										<b>崔绘</b>
										<br />
										<hr width="660" color="#f46240" size="1" />
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">
								</td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">
								</td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">对于一个技术人员而言，GPRS是一个美丽而又复杂的系统，它继承了无线领域几代技术的精华，又吸收了数据网络数十年成功的经验。如此种种，决非一篇文章可以尽数。本文试图从原理角度介绍GPRS网络元素、数据传输协议和操作流程，希望能揭开冰山的一角，为大家了解GPRS技术提供帮助。 </td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">
								</td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">作为一个用户，他所见到GPRS网络是一个承载网，与以太网，帧中继网无异。而从系统工程师的角度看，这个网络就复杂多了，为了提供二层承载，GPRS网络引入了许多新的网络元素和数据处理方法。 </td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">
								</td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">数据由用户的手机到因特网要经过四个设备，MS（Mobile Station，手机）、BSS（Base Station System，基站系统）、SGSN（Serving GPRS Support Node，服务GPRS节点）和GGSN（Gateway GPRS Support Node，网关GPRS节点）。其中，SGSN和GGSN是新增设备，而MS和BSS需要进行设备的软硬件升级。 </td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">
								</td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">它们的主要功能如下： </td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">
								</td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">MS：一方面，处理空中接口的上下行传输；另一方面，将数据信息发给与之连接的计算机。 </td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">
								</td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">BSS：通常包括一系列设备。负责分配空中的信道资源，并在手机和SGSN之间转发信息。 </td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">
								</td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">SGSN：是无线部分和数据网部分的分界线，负责管理手机的移动，并与GGSN协作完成用户数据在Gn网络上的传输。 </td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">
								</td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">GGSN：是GPRS网络与外网的分界线，对内负责Gn网络的传输，对外是一台因特网路由器。其中的BGGSN（Border GGSN）负责连接不同运营商之间的Gn网络，实现网间漫游。上述四个设备将用户的计算机和因特网连接起来，完成了无线上网的数据传输工作。这之间用到的接口有： </td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">
								</td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">R：计算机与GPRS手机之间的接口。可以基于多种传输方法，如RS232、红外等等。 </td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">
								</td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">Um：空中接口，在手机与BSS之间。接口上的传输格式与GSM无异，但逻辑信道的定义和分配有很大差别。 </td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">
								</td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">Gb：BSS和SGSN之间的接口。可以是租用的帧中继网络或者是运行帧中继封装协议的专线。 </td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">
								</td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">Gn：SGSN和GGSN之间的接口。所有的SGSN和GGSN构成了GPRS的骨干网，这个网络是运营商的私有网络，与外网之间没有路由。GGSN将外网的信息封装后在Gn网络上传输。 </td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">
								</td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">Gi：GGSN和外网之间的接口。在这个接口上可以设置隧道（Tunnel），以保证用户到企业内部网之间的安全性。 除此之外，还有Gp接口，在BGGSN和其他运营商的网络之间，以便实现网间漫游。所有这些设备和接口如图所示（双线标出的是数据通路）：</td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">
										<p align="center">
												<img src="http://www.ccw.com.cn/htm/net/comm/01_3_23_4a.gif" border="0" />
										</p>
								</td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">图：GPRS网络的设备与接口</td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">在上图中，还包括一些传统的GSM设备，如MSC（Mobile Switch Center，移动交换中心）、HLR（Home Location Register，用户位置寄存器）、SMS（Short Message Service，短消息服务中心）等等。这些设备与GPRS网络元素之间的接口主要是信令接口。比如： </td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">
								</td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">Gr接口，在SGSN和HLR之间。用户向SGSN登录时，SGSN在HLR中获得用户的登记信息和鉴权信息。 </td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">Gs接口，在SGSN和MSC之间。主要为了便于一些组合的操作，例如，一个用户要同时登录GPRS和GSM两个网络，SGSN就要通过这个接口将对GSM网络的登录请求转发给MSC。 </td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">Gd接口，在SGSN和SMS之间。有了这个接口，短消息就可以通过GPRS网络传送，而不必占用GSM的资源。 </td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">Gc接口，在GGSN和HLR之间。设置它的目的是为了支持外网主机发起连接。通常连接是由手机发起的，SGSN在对用户鉴权之后，找到指定的GGSN，并与之建立数据通路。如果联接是由外网主机发起的，信息到达GGSN之后，GGSN就需要通过Gc接口查询HLR：（1）目的IP对应哪一台手机；（2）这台手机现在由哪一个SGSN控制。这个接口目前还没有实现。 </td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">上面的所有这些设备和接口是从网络功能上讲的。在现实的GPRS网络中，还需要一些其它的辅助设备。例如网络管理设备、Gn网络的路由器、交换机和应用支持设备（DNS和DHCP服务所在的主机）等等。 </td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">
								</td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">了解一个系统，除了它的组成之外，我们最关心的莫过于系统中所使用的协议。GPRS引入了许多新的协议，它们大多出现在数据传输通路上，如下图所示： </td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">
								</td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">
										<p align="center">
												<img src="http://www.ccw.com.cn/htm/net/comm/01_3_23_4b.gif" border="0" />
										</p>
								</td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">
								</td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">图 GPRS所使用的数据传输协议</td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">数据包从外网传到手机要经过四个接口，Gi、Gn、Gb和Um。下面我们就顺序介绍在这个过程中所使用到的传输协议。 </td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">
								</td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">
										<b>Gi接口，外网与GGSN之间的接口</b>
								</td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">
								</td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">在这个接口上没有新的协议。与普通的路由器一样，GGSN利用现有的传输方法，接收二层数据帧。之后，做帧处理，得到IP数据包。分析该IP包的目的地址，恰为本地PDP Context所标示的某一手机地址，则将此数据包送至Gn接口的软件模块，做进一步处理。 </td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">
								</td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">
										<b>Gn接口，GGSN与SGSN之间的接口</b>
								</td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">
								</td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">最先对数据包做处理的GTP（GPRS Tunneling Protocol）协议，它实现了从GGSN到SGSN的虚拟传输通路，即隧道。隧道的优点有二，一是便于手机的移动，当手机由一个SGSN转移到另外一个SGSN的控制下时，只需改变GTP的配置，使隧道的末端发生变化即可，对于被承载的IP数据包来说是透明的；二来，在Gi和Gn两个网络之间，即外网和运营商网络之间不存在路由，只有封装关系，安全性得到了保障。 </td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">
								</td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">Gn网络本身是一个TCP/IP网络，Gn网络中的元素都是靠IP来寻址的。GTP协议数据包对于Gn网络来说是高层的应用数据，需要由TCP或UDP承载，对应GTP应用的四层端口号是3386。之后， TCP或UDP的数据包进一步封装成IP包，此包的目的地址即为目标SGSN的地址。 </td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">
								</td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">Gn网络中的IP包传送也是靠一系列的路由器和交换机来完成的。注意，这时传送的是运营商内网IP（或者内层IP、下层IP），与此相对应，封装在GTP协议内部的IP叫做外网IP（或者外层IP、上层IP）。 </td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">
								</td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">数据到达SGSN之后，层层解封，最终还原出用户的IP数据包，交给IP Relay软件模块。 </td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">
								</td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">
										<b>Gb接口，SGSN和BSS之间的接口</b>
								</td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">
								</td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">首先，SNDCP（Sub-Network Dependent Convergence Protocol）对IP数据包做同一化处理，这一步的目的是提高GPRS的可扩展性，未来，只需改变SNDCP就可以适应新的三层协议，比如IPv6。除此之外，SNDCP还负责数据的压缩和分段，压缩的目的是节约空中接口带宽，分段的目的是适应下层LLC的MTU（Maximum Transmission Unit，最大传输单元）。 </td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">
								</td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">LLC（Logical Link Control）协议负责从SGSN到手机的数据传输。它的服务对象有三，SNDCP数据包（即用户数据）、用户信令和短消息。服务的类型有面向连接和无连接两种，用户可以根据QoS要求选择。 </td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">
								</td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">BSSGP（Base Station System GPRS Protocol）是SGSN与BSS通讯的最上层协议，故而它不但发送上层的LLC数据，还传输SGSN对BSS的控制信息，如对手机的呼叫（Paging）等等。 </td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">
								</td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">NS（Network Service）提供网络传输服务，目前，这个服务是基于帧中继PVC的。也就是说，Gb接口是帧中继接口，可以租用帧中继服务商的线路，也可以在专线上运行帧中继协议。 </td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">
								</td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">数据到达BSS之后，同样是层层解封，最终得到的是LLC数据帧，BSS并不对LLC帧做处理，而只是透明转发。 </td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">
								</td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">
										<b>Um接口，BSS和手机之间的接口</b>
								</td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">
								</td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">任何由空中接口传输的数据，必须先经过两个协议的处理，RLC（Radio Link Control）和MAC（Media Access Control）。 RLC将LLC数据帧拆分成便于空中传输的数据块，并负责空中接口的可靠性保障。数据块的大小依Coding Scheme的不同，可能是181bits、268bits、312bits和428bits。 </td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">
								</td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">MAC的功能是控制空中资源的使用，由于一个用户可以使用多个信道，多个用户也可以使用一个信道，而且，资源的分配是动态的，所以下行传输时，MAC必须标识当前的数据块是给哪一个手机的，上行传输时，必须指定当前资源由谁使用。 </td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">
								</td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">加上LLC和MAC头之后，数据块被卷积（Convolutional Coding）和交织（Interleaving）。卷积指在数据中添加冗余信息，以减少非连续比特错误。卷积处理之后的数据块统一为456比特。交织是将数据块交叉分散到四个突发序列（Normal Burst）中，可以抵抗连续比特错误。 </td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">
								</td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">经过上述处理，最终得到的突发序列与GSM无异，每个包含114比特的数据信息。它们采用和GSM同样的方式，通过空中接口，到达手机。手机做如图的解封之后，还原出IP数据包。至此，IP包通过了GPRS网络，承载的功能完成了。 </td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">
								</td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">控制GPRS操作的有两个重要的Context，在SGSN之前（靠近手机一侧）有MM Context，SGSN之后（靠近网络一侧）有PDP Context。 </td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">
								</td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">MM Context负责移动管理，它有三种状态，空闲（Idle）、就绪（Ready）和守候（Standby）。手机开机，执行登录网络操作，手机的状态由空闲转至就绪。在就绪状态下，手机可以收发数据。一段时间没有数据收发，手机将转入守候状态。这时，网络中有手机的注册信息，但手机基本不占用网络资源。在守候状态下，手机可以申请资源进行上行传输，位置更新；网络方也可以通过Paging发起下行传输，传输一旦开始，手机就返回就序状态。在正常状态下，手机的MM Context在就绪和守候两者之间。也就是说，无论网络还是手机都随时可以发起数据传输，这就是GPRS宣称的一直在线（Always Online）功能，与传统的上网方法相比，效率着实提高不少。 </td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">
								</td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">PDP Context负责数据的传输，任何数据传送之前，手机要建立PDP Context，必备的参数包括手机的IP地址，APN（Access Point Name，访问点名字）等等。有了这些参数，SGSN可以发起建立一条到GGSN的数据传输通路。IP地址的动态分配也是在这个过程中完成的。 </td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">
								</td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">GPRS服务的Qos服务质量可以从五个方面规定。 </td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">
								</td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">
										<ul>
												<li>优先级。分为高中低三种。</li>
										</ul>
								</td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">
								</td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">
										<ul>
												<li>可靠性。GPRS从四个方面评价可靠性，GTP是否重传，LLC是否重传，LLC是否带数据校验和空中接口是否重传。目前的实现大多是GTP、LLC不重传，LLC带数据校验，空中接口有重传。</li>
										</ul>
								</td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">
								</td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">
										<ul>
												<li>延迟。协议规定了三种量化的延迟标准和尽最大可能传输。目前实现的是尽最大可能传输。所有的延迟测量都是在Gi和R接口之间进行的。</li>
										</ul>
								</td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">
								</td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">
										<ul>
												<li>平均速率。分为十九级，目前实现的尽最大可能传输。</li>
										</ul>
								</td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">
								</td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">
										<ul>
												<li>突发速率。分为九级，从8Kbps到未来的2Mbps。</li>
										</ul>
								</td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">合理的数据传输计费方法应该综合考虑数据量与服务质量。不过，计费并不是GPRS协议规定的内容，它的方式将由系统运营商决定。 </td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">
								</td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">正如本文开篇所言，GPRS是一个非常复杂的系统，决非一篇文章可以尽述。如果你想更加系统地了解GPRS，最好的方法莫过于阅读ETSI的GSM 03.60协议 GPRS Service Description。 </td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<table width="620" align="center">
				<tbody>
						<tr>
								<td align="right">（网页编辑：<a href="mailto:network@ccw.com.cn">嘉佳</a>）</td>
						</tr>
				</tbody>
		</table>
<img src ="http://www.blogjava.net/faintbear/aggbug/68215.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/faintbear/" target="_blank">小力力力</a> 2006-09-07 11:45 <a href="http://www.blogjava.net/faintbear/archive/2006/09/07/68215.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>C/C++ 程序设计员应聘常见面试试题深入剖析 </title><link>http://www.blogjava.net/faintbear/archive/2006/07/27/60321.html</link><dc:creator>小力力力</dc:creator><author>小力力力</author><pubDate>Thu, 27 Jul 2006 06:08:00 GMT</pubDate><guid>http://www.blogjava.net/faintbear/archive/2006/07/27/60321.html</guid><wfw:comment>http://www.blogjava.net/faintbear/comments/60321.html</wfw:comment><comments>http://www.blogjava.net/faintbear/archive/2006/07/27/60321.html#Feedback</comments><slash:comments>2</slash:comments><wfw:commentRss>http://www.blogjava.net/faintbear/comments/commentRss/60321.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/faintbear/services/trackbacks/60321.html</trackback:ping><description><![CDATA[
		<table class="Caption_Big" width="95%" align="center" border="0">
				<tbody>
						<tr>
								<td height="30">
										<font style="FONT-SIZE: 15px">
												<b>C/C++ 程序设计员应聘常见面试试题深入剖析 </b>
										</font>
								</td>
						</tr>
				</tbody>
		</table>
		<table class="Border_Blue" cellpadding="5" width="95%" align="center" bgcolor="#f2f9ff" border="0">
				<tbody>
						<tr>
								<td>时间：2006-5-30 17:06:00</td>
								<td>
										<a href="http://edu.cnzz.cn/NewsComment/28170.aspx">
										</a>
								</td>
								<td> </td>
						</tr>
				</tbody>
		</table>
		<div class="Content_T">.引言 <br /><br />　　本文的写作目的并不在于提供C/C++程序员求职面试指导，而旨在从技术上分析面试题的内涵。文中的大多数面试题来自各大论坛，部分试题解答也参考了网友的意见。<br /><br />　　许多面试题看似简单，却需要深厚的基本功才能给出完美的解答。企业要求面试者写一个最简单的strcpy函数都可看出面试者在技术上究竟达到了怎样的程度，我们能真正写好一个strcpy函数吗？我们都觉得自己能，可是我们写出的strcpy很可能只能拿到10分中的2分。读者可从本文看到strcpy 函数从2分到10分解答的例子，看看自己属于什么样的层次。此外，还有一些面试题考查面试者敏捷的思维能力。 <br /><br />　　分析这些面试题，本身包含很强的趣味性；而作为一名研发人员，通过对这些面试题的深入剖析则可进一步增强自身的内功。<br /><br />　　2.找错题<br /><br />　　试题1：<br /><br /><table bordercolor="#cccccc" width="90%" align="center" bgcolor="#e3e3e3" border="1"><tbody><tr><td>void test1()<br />{<br />　char string[10];<br />　char* str1 = "0123456789";<br />　strcpy( string, str1 );<br />}</td></tr></tbody></table><br />　　试题2：<br /><br /><table bordercolor="#cccccc" width="90%" align="center" bgcolor="#e3e3e3" border="1"><tbody><tr><td>void test2()<br />{<br />　char string[10], str1[10];<br />　int i;<br />　for(i=0; i&lt;10; i++)<br />　{<br />　　str1[i] = 'a';<br />　}<br />　strcpy( string, str1 );<br />}</td></tr></tbody></table><br />　　试题3：<br /><br /><table bordercolor="#cccccc" width="90%" align="center" bgcolor="#e3e3e3" border="1"><tbody><tr><td>void test3(char* str1)<br />{<br />　char string[10];<br />　if( strlen( str1 ) &lt;= 10 )<br />　{<br />　　strcpy( string, str1 );<br />　}<br />}</td></tr></tbody></table><br />　　解答：<br /><br />　　试题1字符串str1需要11个字节才能存放下（包括末尾的’\0’），而string只有10个字节的空间，strcpy会导致数组越界；<br /><br />　　对试题2，如果面试者指出字符数组str1不能在数组内结束可以给3分；如果面试者指出strcpy(string, str1)调用使得从str1内存起复制到string内存起所复制的字节数具有不确定性可以给7分，在此基础上指出库函数strcpy工作方式的给10 分；<br /><br />　　对试题3，if(strlen(str1) &lt;= 10)应改为if(strlen(str1) &lt; 10)，因为strlen的结果未统计’\0’所占用的1个字节。<br /><br />　　剖析：<br /><br />　　考查对基本功的掌握：<br /><br />　　(1)字符串以’\0’结尾；<br /><br />　　(2)对数组越界把握的敏感度；<br /><br />　　(3)库函数strcpy的工作方式，如果编写一个标准strcpy函数的总分值为10，下面给出几个不同得分的答案：<br /><br />　　2分<br /><br /><table bordercolor="#cccccc" width="90%" align="center" bgcolor="#e3e3e3" border="1"><tbody><tr><td>void strcpy( char *strDest, char *strSrc )<br />{<br />　 while( (*strDest++ = * strSrc++) != ‘\0’ );<br />}</td></tr></tbody></table><br />　　4分<br /><br /><table bordercolor="#cccccc" width="90%" align="center" bgcolor="#e3e3e3" border="1"><tbody><tr><td>void strcpy( char *strDest, const char *strSrc ) <br />//将源字符串加const，表明其为输入参数，加2分<br />{<br />　 while( (*strDest++ = * strSrc++) != ‘\0’ );<br />}</td></tr></tbody></table><br />　　7分<br /><br /><table bordercolor="#cccccc" width="90%" align="center" bgcolor="#e3e3e3" border="1"><tbody><tr><td>void strcpy(char *strDest, const char *strSrc) <br />{<br />　//对源地址和目的地址加非0断言，加3分<br />　assert( (strDest != NULL) &amp;&amp; (strSrc != NULL) );<br />　while( (*strDest++ = * strSrc++) != ‘\0’ );<br />}</td></tr></tbody></table><br />　　10分<br /><br /><table bordercolor="#cccccc" width="90%" align="center" bgcolor="#e3e3e3" border="1"><tbody><tr><td>//为了实现链式操作，将目的地址返回，加3分！<br /><br />char * strcpy( char *strDest, const char *strSrc ) <br />{<br />　assert( (strDest != NULL) &amp;&amp; (strSrc != NULL) );<br />　char *address = strDest; <br />　while( (*strDest++ = * strSrc++) != ‘\0’ ); <br />　　return address;<br />}</td></tr></tbody></table><br />　　从2分到10分的几个答案我们可以清楚的看到，小小的strcpy竟然暗藏着这么多玄机，真不是盖的！需要多么扎实的基本功才能写一个完美的strcpy啊！<br /><br />　　(4)对strlen的掌握，它没有包括字符串末尾的'\0'。<br /><br />　　读者看了不同分值的strcpy版本，应该也可以写出一个10分的strlen函数了，完美的版本为： int strlen( const char *str ) //输入参数const<br /><br /><table bordercolor="#cccccc" width="90%" align="center" bgcolor="#e3e3e3" border="1"><tbody><tr><td>{<br />　assert( strt != NULL ); //断言字符串地址非0<br />　int len;<br />　while( (*str++) != '\0' ) <br />　{ <br />　　len++; <br />　} <br />　return len;<br />}</td></tr></tbody></table><br />　　试题4：<br /><br /><table bordercolor="#cccccc" width="90%" align="center" bgcolor="#e3e3e3" border="1"><tbody><tr><td>void GetMemory( char *p )<br />{<br />　p = (char *) malloc( 100 );<br />}<br /><br />void Test( void ) <br />{<br />　char *str = NULL;<br />　GetMemory( str ); <br />　strcpy( str, "hello world" );<br />　printf( str );<br />}</td></tr></tbody></table><br />　　试题5：<br /><br /><table bordercolor="#cccccc" width="90%" align="center" bgcolor="#e3e3e3" border="1"><tbody><tr><td>char *GetMemory( void )<br />{ <br />　char p[] = "hello world"; <br />　return p; <br />}<br /><br />void Test( void )<br />{ <br />　char *str = NULL; <br />　str = GetMemory(); <br />　printf( str ); <br />}</td></tr></tbody></table><br />　　试题6：<br /><br /><table bordercolor="#cccccc" width="90%" align="center" bgcolor="#e3e3e3" border="1"><tbody><tr><td>void GetMemory( char **p, int num )<br />{<br />　*p = (char *) malloc( num );<br />}<br /><br />void Test( void )<br />{<br />　char *str = NULL;<br />　GetMemory( &amp;str, 100 );<br />　strcpy( str, "hello" ); <br />　printf( str ); <br />}</td></tr></tbody></table><br />　　试题7：<br /><br /><table bordercolor="#cccccc" width="90%" align="center" bgcolor="#e3e3e3" border="1"><tbody><tr><td>void Test( void )<br />{<br />　char *str = (char *) malloc( 100 );<br />　strcpy( str, "hello" );<br />　free( str ); <br />　... //省略的其它语句<br />}</td></tr></tbody></table><br />　　解答：<br /><br />　　试题4传入中GetMemory( char *p )函数的形参为字符串指针，在函数内部修改形参并不能真正的改变传入形参的值，执行完<br /><br /><table bordercolor="#cccccc" width="90%" align="center" bgcolor="#e3e3e3" border="1"><tbody><tr><td>char *str = NULL;<br />GetMemory( str ); </td></tr></tbody></table><br />　　后的str仍然为NULL；<br /><br />　　试题5中<br /><br /><table bordercolor="#cccccc" width="90%" align="center" bgcolor="#e3e3e3" border="1"><tbody><tr><td>char p[] = "hello world"; <br />return p; </td></tr></tbody></table><br />　　的p[]数组为函数内的局部自动变量，在函数返回后，内存已经被释放。这是许多程序员常犯的错误，其根源在于不理解变量的生存期。<br /><br />　　试题6的GetMemory避免了试题4的问题，传入GetMemory的参数为字符串指针的指针，但是在GetMemory中执行申请内存及赋值语句<br /><br /><table bordercolor="#cccccc" width="90%" align="center" bgcolor="#e3e3e3" border="1"><tbody><tr><td>*p = (char *) malloc( num );</td></tr></tbody></table><br />　　后未判断内存是否申请成功，应加上：<br /><br /><table bordercolor="#cccccc" width="90%" align="center" bgcolor="#e3e3e3" border="1"><tbody><tr><td>if ( *p == NULL )<br />{<br />　...//进行申请内存失败处理<br />}</td></tr></tbody></table><br />　　试题7存在与试题6同样的问题，在执行<br /><br /><table bordercolor="#cccccc" width="90%" align="center" bgcolor="#e3e3e3" border="1"><tbody><tr><td>char *str = (char *) malloc(100);</td></tr></tbody></table><br />　　后未进行内存是否申请成功的判断；另外，在free(str)后未置str为空，导致可能变成一个“野”指针，应加上：<br /><br /><table bordercolor="#cccccc" width="90%" align="center" bgcolor="#e3e3e3" border="1"><tbody><tr><td>str = NULL;</td></tr></tbody></table><br />　　试题6的Test函数中也未对malloc的内存进行释放。<br /><br />　　剖析：<br /><br />　　试题4～7考查面试者对内存操作的理解程度，基本功扎实的面试者一般都能正确的回答其中50~60的错误。但是要完全解答正确，却也绝非易事。<br /><br />　　对内存操作的考查主要集中在：<br /><br />　　（1）指针的理解；<br /><br />　　（2）变量的生存期及作用范围；<br /><br />　　（3）良好的动态内存申请和释放习惯。<br /><br />　　再看看下面的一段程序有什么错误：<br /><br /><table bordercolor="#cccccc" width="90%" align="center" bgcolor="#e3e3e3" border="1"><tbody><tr><td>swap( int* p1,int* p2 )<br />{<br />　int *p;<br />　*p = *p1;<br />　*p1 = *p2;<br />　*p2 = *p;<br />}</td></tr></tbody></table><br />　　在swap函数中，p是一个“野”指针，有可能指向系统区，导致程序运行的崩溃。在VC++中DEBUG运行时提示错误“Access Violation”。该程序应该改为：<br /><br /><table bordercolor="#cccccc" width="90%" align="center" bgcolor="#e3e3e3" border="1"><tbody><tr><td>swap( int* p1,int* p2 )<br />{<br />　int p;<br />　p = *p1;<br />　*p1 = *p2;<br />　*p2 = p;<br />}</td></tr></tbody></table><br />　3.内功题<br /><br />　　试题1：分别给出BOOL，int，float，指针变量 与“零值”比较的 if 语句（假设变量名为var）<br /><br />　　解答：<br /><br />　　　BOOL型变量：if(!var)<br /><br />　　　int型变量： if(var==0)<br /><br />　　　float型变量：<br /><br />　　　const float EPSINON = 0.00001;<br /><br />　　　if ((x &gt;= - EPSINON) &amp;&amp; (x &lt;= EPSINON)<br /><br />　　　指针变量：　　if(var==NULL)<br /><br />　　剖析：<br /><br />　　考查对0值判断的“内功”，BOOL型变量的0判断完全可以写成if(var==0)，而int型变量也可以写成if(!var)，指针变量的判断也可以写成if(!var)，上述写法虽然程序都能正确运行，但是未能清晰地表达程序的意思。 
<table cellspacing="0" cellpadding="0" align="left" border="0"><tbody><tr><td valign="top"><table cellspacing="4" cellpadding="0" width="350" align="left" border="0"><tbody><tr><td><iframe marginwidth="0" marginheight="0" src="http://interbjafa.allyes.com/main/adfshow?user=Interbjafa%7Csohu_dual_core2%7CDigital_inner_page_PIP_350_250&amp;db=interbjafa&amp;border=0&amp;local=yes" frameborder="0" width="350" scrolling="no" height="250">
&amp;ltnoscript&amp;gt&amp;lta
HREF="http://interbjafa.allyes.com/main/adfclick?user=Interbjafa|sohu_dual_core2|Digital_inner_page_PIP_350_250&amp;ampdb=interbjafa"&gt;ltimg
SRC="/newsfile/28000-28999/28170/060530170836850.gif" WIDTH=350
HEIGHT=250 BORDER=0&gt;lt/a&gt;lt/noscript&gt;</iframe></td></tr></tbody></table></td></tr><tr><td></td></tr></tbody></table><br /><br />　　const关键字至少有下列n个作用：<br /><br />　　（1）欲阻止一个变量被改变，可以使用const关键字。在定义该const变量时，通常需要对它进行初始化，因为以后就没有机会再去改变它了；<br /><br />　　（2）对指针来说，可以指定指针本身为const，也可以指定指针所指的数据为const，或二者同时指定为const；<br /><br />　　（3）在一个函数声明中，const可以修饰形参，表明它是一个输入参数，在函数内部不能改变其值；<br /><br />　　（4）对于类的成员函数，若指定其为const类型，则表明其是一个常函数，不能修改类的成员变量；<br /><br />　　（5）对于类的成员函数，有时候必须指定其返回值为const类型，以使得其返回值不为“左值”。例如：<br /><br /><table bordercolor="#cccccc" width="90%" align="center" bgcolor="#e3e3e3" border="1"><tbody><tr><td>const classA operator*(const classA&amp; a1,const classA&amp; a2);</td></tr></tbody></table><br />　　operator*的返回结果必须是一个const对象。如果不是，这样的变态代码也不会编译出错：<br /><br /><table bordercolor="#cccccc" width="90%" align="center" bgcolor="#e3e3e3" border="1"><tbody><tr><td>classA a, b, c;<br />(a * b) = c; // 对a*b的结果赋值</td></tr></tbody></table><br />　　操作(a * b) = c显然不符合编程者的初衷，也没有任何意义。<br /><br />　　剖析：<br /><br />　　惊讶吗？小小的static和const居然有这么多功能，我们能回答几个？如果只能回答1~2个，那还真得闭关再好好修炼修炼。<br /><br />　　这个题可以考查面试者对程序设计知识的掌握程度是初级、中级还是比较深入，没有一定的知识广度和深度，不可能对这个问题给出全面的解答。大多数人只能回答出static和const关键字的部分功能。<br /><br />　　4.技巧题<br /><br />　　试题1：请写一个C函数，若处理器是Big_endian的，则返回0；若是Little_endian的，则返回1<br /><br />　　解答：<br /><br /><table bordercolor="#cccccc" width="90%" align="center" bgcolor="#e3e3e3" border="1"><tbody><tr><td>int checkCPU()<br />{<br />　{<br />　　union w<br />　　{ <br />　　　int a;<br />　　　char b;<br />　　} c;<br />　　c.a = 1;<br />　　return (c.b == 1);<br />　}<br />}</td></tr></tbody></table><br />　　剖析：<br /><br />　　嵌入式系统开发者应该对Little-endian和Big-endian模式非常了解。采用Little-endian模式的CPU对操作数的存放方式是从低字节到高字节，而Big-endian模式对操作数的存放方式是从高字节到低字节。例如，16bit宽的数0x1234在Little- endian模式CPU内存中的存放方式（假设从地址0x4000开始存放）为：<br /><br /><table cellspacing="0" cellpadding="0" width="72%" align="center" border="1"><tbody><tr><td>内存地址</td><td>存放内容</td></tr><tr><td>0x4000</td><td>0x34</td></tr><tr><td>0x4001</td><td>0x12</td></tr></tbody></table><br />　　而在Big-endian模式CPU内存中的存放方式则为：<br /><br /><table cellspacing="0" cellpadding="0" width="72%" align="center" border="1"><tbody><tr><td>内存地址</td><td>存放内容</td></tr><tr><td>0x4000</td><td>0x12</td></tr><tr><td>0x4001</td><td>0x34</td></tr></tbody></table><br />　　32bit宽的数0x12345678在Little-endian模式CPU内存中的存放方式（假设从地址0x4000开始存放）为：<br /><br /><table cellspacing="0" cellpadding="0" width="72%" align="center" border="1"><tbody><tr><td>内存地址</td><td>存放内容</td></tr><tr><td>0x4000</td><td>0x78</td></tr><tr><td>0x4001</td><td>0x56</td></tr><tr><td>0x4002</td><td>0x34</td></tr><tr><td>0x4003</td><td>0x12</td></tr></tbody></table><br />　　而在Big-endian模式CPU内存中的存放方式则为：<br /><br /><table cellspacing="0" cellpadding="0" width="72%" align="center" border="1"><tbody><tr><td>内存地址</td><td>存放内容</td></tr><tr><td>0x4000</td><td>0x12</td></tr><tr><td>0x4001</td><td>0x34</td></tr><tr><td>0x4002</td><td>0x56</td></tr><tr><td>0x4003</td><td>0x78</td></tr></tbody></table><br />　　联合体union的存放顺序是所有成员都从低地址开始存放，面试者的解答利用该特性，轻松地获得了CPU对内存采用Little-endian还是Big-endian模式读写。如果谁能当场给出这个解答，那简直就是一个天才的程序员。<br /><br />　　试题2：写一个函数返回1+2+3+…+n的值（假定结果不会超过长整型变量的范围） <br /><br />　　解答：<br /><br /><table bordercolor="#cccccc" width="90%" align="center" bgcolor="#e3e3e3" border="1"><tbody><tr><td>int Sum( int n )<br />{ <br />　return ( (long)1 + n) * n / 2;　　//或return (1l + n) * n / 2;<br />}</td></tr></tbody></table><br />　　剖析：<br />　<br />　　对于这个题，只能说，也许最简单的答案就是最好的答案。下面的解答，或者基于下面的解答思路去优化，不管怎么“折腾”，其效率也不可能与直接return ( 1 l + n ) * n / 2相比！ <br /><br /><table bordercolor="#cccccc" width="90%" align="center" bgcolor="#e3e3e3" border="1"><tbody><tr><td>int Sum( int n )<br />{<br />　long sum = 0;<br />　for( int i=1; i&lt;=n; i++ )<br />　{<br />　　sum += i;<br />　}<br />　return sum;<br />} </td></tr></tbody></table><br />　　所以程序员们需要敏感地将数学等知识用在程序设计中。 </div>
<img src ="http://www.blogjava.net/faintbear/aggbug/60321.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/faintbear/" target="_blank">小力力力</a> 2006-07-27 14:08 <a href="http://www.blogjava.net/faintbear/archive/2006/07/27/60321.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title> c 语言难点分析整理 (转)</title><link>http://www.blogjava.net/faintbear/archive/2006/07/18/58871.html</link><dc:creator>小力力力</dc:creator><author>小力力力</author><pubDate>Tue, 18 Jul 2006 15:29:00 GMT</pubDate><guid>http://www.blogjava.net/faintbear/archive/2006/07/18/58871.html</guid><wfw:comment>http://www.blogjava.net/faintbear/comments/58871.html</wfw:comment><comments>http://www.blogjava.net/faintbear/archive/2006/07/18/58871.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/faintbear/comments/commentRss/58871.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/faintbear/services/trackbacks/58871.html</trackback:ping><description><![CDATA[
		<table height="100%" cellspacing="0" cellpadding="0" width="100%" border="0">
				<tbody>
						<tr>
								<td style="PADDING-RIGHT: 10px" width="74">
								</td>
								<td valign="top" width="100%">
										<table height="100%" cellspacing="0" cellpadding="0" width="100%" border="0">
												<tbody>
														<tr>
																<td class="artitle" valign="top" colspan="2">c 语言难点分析整理</td>
														</tr>
														<tr class="text" valign="top">
																<td align="left">原创：imy </td>
																<td class="text" valign="top" align="right">2003年9月10日 </td>
														</tr>
												</tbody>
										</table>
								</td>
						</tr>
						<tr>
								<td class="arcontent" colspan="3">
										<br />
								</td>
						</tr>
				</tbody>
				<style type="text/css">
						<!--
.text {
	padding: 7px;
}
-->
				</style>
这篇文章主要是介绍一些在复习C语言的过程中笔者个人认为比较重点的地方，较好的掌握这些重点会使对C的运用更加得心应手。此外会包括一些细节、易错的地方。涉及的主要内容包括：变量的作用域和存储类别、函数、数组、字符串、指针、文件、链表等。一些最基本的概念在此就不多作解释了，仅希望能有只言片语给同是C语言初学者的学习和上机过程提供一点点的帮助。<br /><p><strong>变量作用域和存储类别：</strong></p><p>了解了基本的变量类型后，我们要进一步了解它的存储类别和变量作用域问题。</p><table cellspacing="1" width="100%" bgcolor="#000000" border="0"><tbody><tr class="text"><td class="text" width="21%" bgcolor="#cccccc" height="22">变量类别</td><td class="text" width="79%" bgcolor="#cccccc">子类别</td></tr><tr class="text"><td class="text" bgcolor="#e0e0e0" rowspan="3">局部变量</td><td class="text" bgcolor="#ffffff">静态变量（离开函数，变量值仍保留）</td></tr><tr bordercolor="#000000" bgcolor="#e0e0e0"><td class="text" bgcolor="#ffffff">自动变量</td></tr><tr bordercolor="#000000" bgcolor="#e0e0e0"><td class="text" bgcolor="#ffffff">寄存器变量</td></tr><tr class="text"><td class="text" bgcolor="#e0e0e0" rowspan="2">全局变量</td><td class="text" bgcolor="#ffffff">静态变量（只能在本文件中用）</td></tr><tr bordercolor="#000000" bgcolor="#e0e0e0"><td class="text" bgcolor="#ffffff">非静态变量（允许其他文件使用）</td></tr></tbody></table><p>换一个角度</p><table cellspacing="1" width="100%" bgcolor="#000000" border="0"><tbody><tr class="text"><td class="text" bgcolor="#cccccc" height="19">变量类别</td><td class="text" bgcolor="#cccccc">子类别</td></tr><tr class="text"><td class="text" bgcolor="#e0e0e0" rowspan="3"><font color="#000000">静态存储变量</font></td><td class="text" bgcolor="#ffffff"><font color="#000000">静态局部变量（函数）</font></td></tr><tr><td class="text" bgcolor="#ffffff"><font color="#000000">静态全局变量（本文件）</font></td></tr><tr><td class="text" bgcolor="#ffffff"><font color="#000000">非静态全局/外部变量（其他文件引用）</font></td></tr><tr class="text"><td class="text" bgcolor="#e0e0e0" rowspan="3">动态存储变量</td><td class="text" bgcolor="#ffffff" height="19">自动变量</td></tr><tr><td class="text" bgcolor="#ffffff" height="19">寄存器变量</td></tr><tr><td class="text" bgcolor="#ffffff" height="20">形式参数</td></tr></tbody></table><p>extern型的存储变量在处理多文件问题时常能用到，在一个文件中定义extern型的变量即说明这个变量用的是其他文件的。顺便说一下，笔者在做课设时遇到out of memory的错误，于是改成做多文件，再把它include进来（注意自己写的*.h要用“”不用&lt;&gt;），能起到一定的效用。static型的在读程序写结果的试题中是个考点。多数时候整个程序会出现多个定义的变量在不同的函数中，考查在不同位置同一变量的值是多少。主要是遵循一个原则，只要本函数内没有定义的变量就用全局变量（而不是main里的），全局变量和局部变量重名时局部变量起作用，当然还要注意静态与自动变量的区别。 </p><p><font size="+1"><strong>函数：</strong></font></p><p>对于函数最基本的理解是从那个叫main的单词开始的，一开始总会觉得把语句一并写在main里不是挺好的么，为什么偏择出去。其实这是因为对函数还不够熟练，否则函数的运用会给我们编程带来极大的便利。我们要知道函数的返回值类型，参数的类型，以及调用函数时的形式。事先的函数说明也能起到一个提醒的好作用。所谓形参和实参，即在调用函数时写在括号里的就是实参，函数本身用的就是形参，在画流程图时用平行四边形表示传参。</p><p>函数的另一个应用例子就是递归了，笔者开始比较头疼的问题，反应总是比较迟钝，按照老师的方法，把递归的过程耐心准确的逐级画出来，学习的效果还是比较好的，会觉得这种递归的运用是挺巧的，事实上，著名的八皇后、汉诺塔等问题都用到了递归。</p><table class="code" height="214" width="102%" bgcolor="#e0e0e0" border="0"><tbody><tr><td height="210">例子：<br /><font color="#3333ff">long</font> fun(<font color="#3333ff">int</font> n)<br />{<br /><font color="#3333ff">long</font> s;<br /><font color="#3333ff">if</font>(n==1||n==2) s=2;<br />   <font color="#3333ff">else</font> s=n<font color="#000000">-fun(n-1)</font>;<br /><font color="#3333ff">return</font><font color="#000000"> s</font>;<br />}<br />main()<br />{<br />printf("<font color="#ff33cc">%ld</font>",fun(4));<br />}</td></tr></tbody></table><p><img height="169" src="http://beastie.frontfree.net/articles/pages/0000000779/pic.gif" width="193" /></p><p><font size="+1"><strong>数组：</strong></font></p><p>分为一维数组和多维数组，其存储方式画为表格的话就会一目了然，其实就是把相同类型的变量有序的放在一起。因此，在处理比较多的数据时（这也是大多数的情况）数组的应用范围是非常广的。</p><p>具体的实际应用不便举例，而且绝大多数是与指针相结合的，笔者个人认为学习数组在更大程度上是为学习指针做一个铺垫。作为基础的基础要明白几种基本操作：即数组赋值、打印、排序（冒泡排序法和选择排序法）、查找。这些都不可避免的用到循环，如果觉得反应不过来，可以先一点点的把循环展开，就会越来越熟悉，以后自己编写一个功能的时候就会先找出内在规律，较好的运用了。另外数组做参数时，一维的[]里可以是空的，二维的第一个[]里可以是空的但是第二个[]中必须规定大小。</p><table class="code" width="100%" border="0"><tbody><tr><td bgcolor="#e0e0e0">冒泡法排序函数：<br /><font color="#3333ff">void</font> bubble(<font color="#3333ff">int</font><font color="#000000">a[]</font>,<font color="#3333ff">int</font> n)<br />{<br /><font color="#3333ff">int</font> i,j,k;<br /><font color="#3333ff">for</font>(i=1,i&lt;n;i++)<br />   <font color="#3333ff">for</font>(j=0;j&lt;<font color="#000000">n-i-1;</font>j++)<br />   <font color="#3333ff">if</font>(a[j]&gt;a[j+1])<br />    {<font color="#000000"><br />    k=a[j];<br />       a[j]=a[j+1];<br />       a[j+1]=k;<br />       }</font><br />}<br /><br />选择法排序函数：<br /><font color="#3333ff">void</font> sort(<font color="#3333ff">int</font><font color="#000000">a[]</font>,<font color="#3333ff">int</font> n)<br />{<br /><font color="#3333ff">int</font> i,j,k,t;<br /><font color="#3333ff">for</font>(i=0,i&lt;<font color="#000000">n-1</font>;i++)<br />   {<br /><font color="#ff0000">  <font color="#000000"> k=i</font></font><font color="#000000">;</font><br />   <font color="#3333ff">for</font>(<font color="#000000">j=i+1</font>;j&lt;n;j++)<br />      <font color="#3333ff">if</font>(a[k]&lt;a[j]) <font color="#000000">k=j</font>;<br />      <font color="#3333ff">if</font>(<font color="#000000">k!=i</font>)<br />         {<br />         t=a[i];<br />         a[i]=a[k];<br />         a[k]=t;<br />         }<br />   } <br />}<br /><br />折半查找函数（原数组有序）：<br /><font color="#3333ff">void</font> search(<font color="#3333ff">int</font><font color="#000000">a[]</font>,<font color="#3333ff">int</font> n,<font color="#3333ff">int</font> x)<br />{<br /><font color="#3333ff">int</font> left=0,right=n-1,mid,flag=0;<br /><font color="#3333ff">while</font>((flag==0)&amp;&amp;(left&lt;=right))<br />   {<br /><font color="#ff0000">   <font color="#000000">mid=(left+right)/2</font></font><font color="#000000">;</font><br />   <font color="#3333ff">if</font>(x==a[mid])<br />      {<br />      printf("<font color="#ff33cc">%d%d</font>",x,mid);<br />      flag =1;<br />      }<br />      <font color="#3333ff">else</font><font color="#3333ff">if</font>(x&lt;a[mid]) <font color="#000000">right=mid-1;</font><br />                   <font color="#3333ff">else</font><font color="#000000">left=mid+1</font>;<br />   }<br />}</td></tr></tbody></table><p>相关常用的算法还有<strong>判断回文，求阶乘，Fibanacci数列，任意进制转换，杨辉三角形计算</strong>等等<strong>。</strong></p><p><font size="+1"><strong>字符串：</strong></font></p><p>字符串其实就是一个数组（指针），在scanf的输入列中是不需要在前面加“&amp;”符号的，因为字符数组名本身即代表地址。值得注意的是字符串末尾的‘\0’，如果没有的话，字符串很有可能会不正常的打印。另外就是字符串的定义和赋值问题了，笔者有一次的比较综合的上机作业就是字符串打印老是乱码，上上下下找了一圈问题，最后发现是因为</p><table class="code" width="100%" bgcolor="#e0e0e0" border="0"><tbody><tr><td><font color="#3333ff">char</font> *name;</td></tr></tbody></table><p>而不是</p><table class="code" width="100%" bgcolor="#e0e0e0" border="0"><tbody><tr><td><font color="#3333ff">char</font> name[10];</td></tr></tbody></table><p>前者没有说明指向哪儿，更没有确定大小，导致了乱码的错误，印象挺深刻的。 </p><p>另外，字符串的赋值也是需要注意的，如果是用字符指针的话，既可以定义的时候赋初值，即</p><table class="code" width="100%"><tbody><tr><td class="text" bgcolor="#e0e0e0"><font color="#3333ff">char</font> *a="Abcdefg";</td></tr></tbody></table><p>也可以在赋值语句中赋值，即</p><table class="code" width="100%" bgcolor="#e0e0e0" border="0"><tbody><tr><td><font color="#3333ff">char</font> *a;<br />a="<font color="#ff33cc">Abcdefg</font>"; </td></tr></tbody></table><p>但如果是用字符数组的话，就只能在定义时整体赋初值，即char a[5]={"abcd"};而不能在赋值语句中整体赋值。 </p><p>常用字符串函数列表如下，要会自己实现：</p><table cellspacing="1" width="100%" bgcolor="#000000" border="0"><tbody><tr><td class="text" width="21%" bgcolor="#cccccc"><strong>函数作用</strong></td><td class="text" width="30%" bgcolor="#cccccc"><strong>函数调用形式</strong></td><td class="text" width="49%" bgcolor="#cccccc"><strong>备注</strong></td></tr><tr><td class="text" bgcolor="#ffffff">字符串拷贝函数</td><td class="text" bgcolor="#ffffff">strcpy(char*,char *)</td><td class="text" bgcolor="#ffffff">后者拷贝到前者</td></tr><tr><td class="text" bgcolor="#e0e0e0">字符串追加函数</td><td class="text" bgcolor="#e0e0e0">strcat(char*,char *)</td><td class="text" bgcolor="#e0e0e0">后者追加到前者后，返回前者，因此前者空间要足够大</td></tr><tr><td class="text" bgcolor="#ffffff">字符串比较函数</td><td class="text" bgcolor="#ffffff">strcmp(char*,char *)</td><td class="text" bgcolor="#ffffff">前者等于、小于、大于后者时，返回0、正值、负值。注意，不是比较长度，是比较字符ASCII码的大小，可用于按姓名字母排序等。</td></tr><tr><td class="text" bgcolor="#e0e0e0">字符串长度</td><td class="text" bgcolor="#e0e0e0">strlen(char *)</td><td class="text" bgcolor="#e0e0e0">返回字符串的长度，不包括'\0'.转义字符算一个字符。</td></tr><tr><td class="text" bgcolor="#ffffff">字符串型-&gt;整型</td><td class="text" bgcolor="#ffffff" colspan="2">atoi(char *)</td></tr><tr><td class="text" bgcolor="#e0e0e0" rowspan="2">整型-&gt;字符串型</td><td class="text" bgcolor="#e0e0e0">itoa(int,char *,int)</td><td class="text" bgcolor="#e0e0e0">做课设时挺有用的</td></tr><tr><td class="text" bgcolor="#e0e0e0">sprintf(char *,格式化输入）</td><td class="text" bgcolor="#e0e0e0">赋给字符串，而不打印出来。课设时用也比较方便</td></tr></tbody></table><p><strong>注：</strong>对字符串是不允许做==或！=的运算的，只能用字符串比较函数</p><p><font size="+1"><strong>指针：</strong></font></p><p>指针可以说是C语言中最关键的地方了，其实这个“指针”的名字对于这个概念的理解是十分形象的。首先要知道，指针变量的值（即指针变量中存放的值）是指针（即地址）。指针变量定义形式中：基本类型 *指针变量名 中的“*”代表的是这是一个指向该基本类型的指针变量，而不是内容的意思。在以后使用的时候，如*ptr=a时，“*”才表示ptr所指向的地址里放的内容是a。</p><p>指针比较典型又简单的一应用例子是两数互换，看下面的程序，</p><table class="code" width="100%" bgcolor="#e0e0e0" border="0"><tbody><tr><td>swap(<font color="#3333ff">int</font><font color="#ff0000"><font color="#000000">c,</font><font color="#3333ff">int</font><font color="#000000">d</font></font>)<br />{<br /><font color="#3333ff">int</font> t;<br />t=c;<br />c=d;<br />d=t;<br />}<br />main()<br />{<br /><font color="#3333ff">int</font> a=2,b=3;<br />swap(<font color="#000000">a,b</font>);<br />printf(“%d,%d”,a,b);<br />}</td></tr></tbody></table><p>这是不能实现a和b的数值互换的，实际上只是形参在这个函数中换来换去，对实参没什么影响。现在，用指针类型的数据做为参数的话，更改如下：</p><table class="code" width="100%" bgcolor="#e0e0e0" border="0"><tbody><tr><td><font color="#000000">swap(#3333FF *p1,<font color="#3333ff">int</font> *p2)<br />{<br /><font color="#3333ff">int</font> t;<br />t=*p1;<br />*p1=*p2;<br />*p2=t;<br />}<br />main()<br />{<br /><font color="#3333ff">int</font> a=2,b=3;<br /><font color="#3333ff">int</font> *ptr1,*ptr2;<br />ptr1=&amp;a;<br />ptr2=&amp;b;<br />swap(prt1,ptr2);<br />printf(“%d,%d”,a,b);<br />}</font></td></tr></tbody></table><p>这样在swap中就把p1,p2 的内容给换了，即把a，b的值互换了。</p><p>指针可以执行<strong>增、减运算</strong>，结合++运算符的法则，我们可以看到:</p><table cellspacing="1" cellpadding="0" width="100%" bgcolor="#000000" border="0"><tbody><tr class="text" bgcolor="#cccccc"><td width="6%" bgcolor="#e0e0e0"><strong>*++s</strong></td><td width="94%" bgcolor="#ffffff"><p>取指针变量加1以后的内容</p></td></tr><tr class="text" bgcolor="#cccccc"><td bgcolor="#e0e0e0"><strong>*s++</strong></td><td bgcolor="#ffffff">取指针变量所指内容后s再加1</td></tr><tr class="text" bgcolor="#cccccc"><td bgcolor="#e0e0e0"><strong>(*s)++</strong></td><td bgcolor="#ffffff">指针变量指的内容加1</td></tr></tbody></table><p><strong>指针和数组</strong>实际上几乎是一样的，数组名可以看成是一个常量指针，一维数组中ptr=&amp;b[0]则下面的表示法是等价的：</p><p><strong>a[3]等价于*(a+3)<br />ptr[3]等价于*(ptr+3) </strong></p><p>下面看一个用指针来自己实现atoi（字符串型-&gt;整型）函数：</p><table class="code" width="100%" bgcolor="#e0e0e0" border="0"><tbody><tr><td><font color="#3333ff">int</font> atoi(<font color="#3333ff">char</font> *s)<br />{<br /><font color="#3333ff">int</font> sign=1,m=0;<br /><font color="#3333ff">if</font>(*s=='+'||*s=='-') <font color="#006600">/*判断是否有符号*/</font><br />sign=(*s++=='+'<font color="#000000">)?1:-1;</font><font color="#006600">/*用到三目运算符*/</font><br /><font color="#3333ff">while</font>(<font color="#000000">*s!='\0'</font>) <font color="#006600">/*对每一个字符进行操作*/</font><br />   {<br />   <font color="#000000">m=m*10+(*s-'0');<br />   s++;</font><font color="#006600">/*指向下一个字符*/</font><br />   }<br /><font color="#3333ff">return</font> m*sign;<br />}</td></tr></tbody></table><p>指向多维数组的指针变量也是一个比较广泛的运用。例如数组a[3][4]，a代表的实际是整个二维数组的首地址，即第0行的首地址，也就是一个指针变量。而a+1就不是简单的在数值上加上1了，它代表的不是a[0][1]，而是第1行的首地址，&amp;a[1][0]。</p><p>指针变量常用的用途还有把指针作为参数传递给其他函数，即<strong>指向函数的指针</strong>。<br />看下面的几行代码：</p><table class="code" width="100%" bgcolor="#e0e0e0" border="0"><tbody><tr><td><font color="#3333ff">void</font> Input(ST *);<br /><font color="#3333ff">void</font> Output(ST *);<br /><font color="#3333ff">void</font> Bubble(ST *);<br /><font color="#3333ff">void</font> Find(ST *);<br /><font color="#3333ff">void</font> Failure(ST *);<br /><font color="#006600">/*函数声明：这五个函数都是以一个指向ST型（事先定义过）结构的指针变量作为参数，无返回值。*/</font><font color="#009900"><br /></font><br /><font color="#3333ff">void</font><font color="#000000">(*process[5])(ST *)</font>={Input,Output,Bubble,Find,Failure};<br /><font color="#006600">/*process被调用时提供5种功能不同的函数共选择(指向函数的指针数组）*/</font><br /><br />printf(<font color="#ff33cc">"\nChoose:\n?"</font>);<br />scanf(<font color="#ff33cc">"%d"</font>,&amp;choice);<br /><font color="#3333ff">if</font>(choice&gt;=0&amp;&amp;choice&lt;=4)<br />(*process[<font color="#000000">choice</font>])(a); <font color="#006600">/*调用相应的函数实现不同功能*;/ </font></td></tr></tbody></table><p>总之，指针的应用是非常灵活和广泛的，不是三言两语能说完的，上面几个小例子只是个引子，实际编程中，会逐渐发现运用指针所能带来的便利和高效率。 </p><p><font size="+1"><strong>文件：</strong></font></p><table class="unnamed1" cellspacing="1" cellpadding="0" width="100%" bgcolor="#000000" border="0"><!--DWLayoutTable--><tbody><tr bgcolor="#cccccc"><td class="text" width="40%"><strong>函数调用形式</strong></td><td class="text"><strong>说明</strong></td></tr><tr bgcolor="#ffffff"><td class="text">fopen("路径","打开方式")</td><td class="text">打开文件</td></tr><tr bgcolor="#e0e0e0"><td class="text">fclose(FILE *)</td><td class="text">防止之后被误用</td></tr><tr bgcolor="#ffffff"><td class="text">fgetc(FILE *)</td><td class="text">从文件中读取一个字符</td></tr><tr bgcolor="#e0e0e0"><td class="text">fputc(ch,FILE *)</td><td class="text">把ch代表的字符写入这个文件里</td></tr><tr bgcolor="#ffffff"><td class="text">fgets(FILE *)</td><td class="text">从文件中读取一行</td></tr><tr bgcolor="#e0e0e0"><td class="text">fputs(FILE *)</td><td class="text">把一行写入文件中</td></tr><tr bgcolor="#ffffff"><td class="text">fprintf(FILE *,"格式字符串",输出表列）</td><td class="text">把数据写入文件</td></tr><tr bgcolor="#e0e0e0"><td class="text">fscanf(FILE *,"格式字符串",输入表列）</td><td class="text">从文件中读取</td></tr><tr bgcolor="#ffffff"><td class="text">fwrite（地址，sizeof（），n，FILE *）</td><td class="text">把地址中n个sizeof大的数据写入文件里</td></tr><tr bgcolor="#e0e0e0"><td class="text">fread（地址，sizeof（），n，FILE *）</td><td class="text">把文件中n个sizeof大的数据读到地址里</td></tr><tr bgcolor="#ffffff"><td class="text">rewind（FILE *）</td><td class="text">把文件指针拨回到文件头</td></tr><tr bgcolor="#e0e0e0"><td class="text">fseek（FILE *，x，0/1/2）</td><td class="text">移动文件指针。第二个参数是位移量，0代表从头移，1代表从当前位置移，2代表从文件尾移。</td></tr><tr bgcolor="#ffffff"><td class="text">feof(FILE *)</td><td class="text">判断是否到了文件末尾</td></tr></tbody></table><br /><table cellspacing="1" cellpadding="0" width="100%" bgcolor="#000000" border="0"><!--DWLayoutTable--><tbody><tr bgcolor="#e0e0e0"><td class="text" width="22%"><strong>文件打开方式</strong></td><td class="text" width="78%"><strong>说明</strong></td></tr><tr bgcolor="#ffffff"><td class="text"><strong>r</strong></td><td class="text">打开只能读的文件</td></tr><tr bgcolor="#ffffff"><td class="text"><strong>w</strong></td><td class="text">建立供写入的文件，如果已存在就抹去原有数据</td></tr><tr bgcolor="#ffffff"><td class="text"><strong>a</strong></td><td class="text">打开或建立一个把数据追加到文件尾的文件</td></tr><tr bgcolor="#ffffff"><td class="text"><strong>r+</strong></td><td class="text">打开用于更新数据的文件</td></tr><tr bgcolor="#ffffff"><td class="text"><strong>w+</strong></td><td class="text">建立用于更新数据的文件，如果已存在就抹去原有数据</td></tr><tr bgcolor="#ffffff"><td class="text"><strong>a+</strong></td><td class="text">打开或建立用于更新数据的文件，数据追加到文件尾</td></tr></tbody></table><p><strong>注：</strong>以上用于文本文件的操作，如果是二进制文件就在上述字母后加“b”。</p><p>我们用文件最大的目的就是能让数据保存下来。因此在要用文件中数据的时候，就是要把数据读到一个结构（一般保存数据多用结构，便于管理）中去，再对结构进行操作即可。例如，文件aa.data中存储的是30个学生的成绩等信息，要遍历这些信息，对其进行成绩输出、排序、查找等工作时，我们就把这些信息先读入到一个结构数组中，再对这个数组进行操作。如下例：</p><table class="code" width="100%" border="0"><tbody><tr><td bgcolor="#e0e0e0"><font color="#ff0000">#include</font>&lt;stdio.h&gt;<br /><font color="#ff0000">#include</font>&lt;stdlib.h&gt;<br /><font color="#ff0000">#define</font> N 30 <p><font color="#3333ff">typedef struct</font> student <font color="#006600">/*定义储存学生成绩信息的数组*/</font><br />{<br /><font color="#3333ff">char</font> *name;<br /><font color="#3333ff">int</font> chinese;<br /><font color="#3333ff">int</font> maths;<br /><font color="#3333ff">int</font> phy;<br /><font color="#3333ff">int</font> total;<br />}<font color="#ff00ff">ST</font>;</p><p>main()<br />{<br /><font color="#ff00ff">ST a[N]</font>; <font color="#006600">/*存储N个学生信息的数组*/</font><br /><font color="#ff00ff">FILE *fp</font>;<br /><font color="#3333ff">void</font><font color="#000000">(*process[3])(ST *)</font>={Output,Bubble,Find}; <font color="#006600">/*实现相关功能的三个函数*/</font><br /><font color="#3333ff">int</font> choice,i=0;<br />Show();<br />printf(<font color="#ff00ff">"\nChoose:\n?"</font>);<br />scanf(<font color="#ff00ff">"%d"</font>,&amp;choice);<br /><font color="#3333ff">while</font>(choice&gt;=0&amp;&amp;choice&lt;=2)<br />   {<br />   <font color="#000000">fp=fopen(</font><font color="#ff0000"><font color="#ff00ff">"aa.dat"</font><font color="#000000">,</font><font color="#ff00ff">"rb"</font><font color="#000000">)</font></font>; <br />   <font color="#3333ff">for</font>(i=0;<font color="#000000">i&lt;N</font>;i++)<br />  <font color="#ff0000">    <font color="#000000">fread(&amp;a[i],</font><font color="#3333ff">sizeof</font><font color="#000000">(ST),1,fp);</font></font><font color="#006600">/*把文件中储存的信息逐个读到数组中去*/</font><br />   fclose(fp);<br />   (*process[choice])<font color="#000000">(a</font>); <font color="#006600">/*前面提到的指向函数的指针，选择操作*/</font><br />   printf(<font color="#ff00ff">"\n"</font>);<br />   Show();<br />   printf(<font color="#ff00ff">"\n?"</font>);<br />   scanf(<font color="#ff00ff">"%d"</font>,&amp;choice);<br />   }<br />}</p><p><font color="#3333ff">void</font> Show()<br />{<br />printf(<font color="#ff00ff">"\n****Choices:****\n0.Display the data form\n1.Bubble it according to the total score\n2.Search\n3.Quit!\n"</font>);<br />}</p><p><font color="#3333ff">void</font> Output(<font color="#000000">ST *a</font>) <font color="#006600">/*将文件中存储的信息逐个输出*/</font><br />{<br /><font color="#3333ff">int</font> i,t=0;<br />printf(<font color="#ff00ff">"Name Chinese Maths Physics Total\n"</font>);<br /><font color="#3333ff">for</font>(i=0;i&lt;N;i++)<br />   {<br />   t=a[i].chinese+a[i].maths+a[i].phy;<br />   a[i].total=t;<br />   printf(<font color="#ff00ff">"%4s%8d%8d%8d%8d\n"</font>,a[i].name,a[i].chinese,a[i].maths,a[i].phy,a[i].total);<br />   }<br />}</p><p><font color="#3333ff">void</font> Bubble(<font color="#000000">ST *a)</font><font color="#006600">/*对数组进行排序，并输出结果*/</font><br />{<br /><font color="#3333ff">int</font> i,pass;<br /><font color="#000000">ST m;</font><br /><font color="#3333ff">for</font>(pass=0;pass<font color="#000000">&lt;N-1</font>;pass++)<br />   <font color="#3333ff">for</font>(i=0;i<font color="#000000">&lt;N-1</font>;i++) <br />      <font color="#3333ff">if</font>(a[i].total&lt;a[i+1].total)<br />         {<br />         m=a[i]; <font color="#006600">/*结构互换*/</font><br />         a[i]=a[i+1];<br />         a[i+1]=m; <br />         }<br />Output(<font color="#000000">a</font>);<br />}</p><p><font color="#3333ff">void</font> Find<font color="#000000">(ST *a)</font><br />{<br /><font color="#3333ff">int</font> i,t=1;<br /><font color="#3333ff">char</font> m[20];<br />printf(<font color="#ff00ff">"\nEnter the name you want:"</font>);<br />scanf(<font color="#ff00ff">"%s"</font>,m);<br /><font color="#3333ff">for</font>(i=0;i&lt;N;i++)<br />   <font color="#3333ff">if</font>(<font color="#000000">!strcmp(m,a[i].name))</font><font color="#006600">/*根据姓名匹配情况输出查找结果*/</font><br />   {<br />   printf(<font color="#ff00ff">"\nThe result is:\n%s, Chinese:%d, Maths:%d,     Physics:%d,Total:%d\n"</font>,m,a[i].chinese,a[i].maths,a[i].phy,a[i].total);<br />  <font color="#ff0000"> <font color="#000000">t=0</font></font><font color="#000000">;</font><br />   }<br /><font color="#3333ff">if</font>(<font color="#000000">t</font>)<br />   printf(<font color="#ff00ff">"\nThe name is not in the list!\n"</font>);<br />}</p></td></tr></tbody></table><p><font size="+1"><strong>链表：</strong></font><br />链表是C语言中另外一个难点。牵扯到结点、动态分配空间等等。用结构作为链表的结点是非常适合的，例如：</p><table class="code" cellspacing="4" cellpadding="0" width="100%" border="0"><tbody><tr><td bgcolor="#e0e0e0"><font color="#0000ff">struct</font> node <br />{<br /><font color="#0000ff">int</font> data;<br /><font color="#0000ff">struct</font> node *next;<br />};</td></tr></tbody></table><p>其中next是指向自身所在结构类型的指针，这样就可以把一个个结点相连，构成链表。</p><p>链表结构的一大优势就是动态分配存储，不会像数组一样必须在定义时确定大小，造成不必要的浪费。用malloc和free函数即可实现开辟和释放存储单元。其中，malloc的参数多用sizeof运算符计算得到。</p><p>链表的基本操作有：<strong>正、反向建立链表；输出链表；删除链表中结点；在链表中插入结点</strong>等等，都是要熟练掌握的，初学者通过<strong>画图</strong>的方式能比较形象地理解建立、插入等实现的过程。</p><table class="code" width="100%" border="0"><tbody><tr><td bgcolor="#e0e0e0"><font color="#3333ff">typedef struct</font> node<br />{<br /><font color="#3333ff">char</font> data;<br /><font color="#3333ff">struct</font> node *next;<br />}<font color="#000000">NODE</font>; <font color="#006600">/*结点*/</font><br /><br />正向建立链表：<br />NODE *create()<br />{<br /><font color="#3333ff">char</font> ch=<font color="#ff00ff">'a'</font>;<br />NODE *p,*h=NULL,*q=NULL;<br /><font color="#3333ff">while</font>(ch&lt;<font color="#ff00ff">'z'</font>)<br />   {<br />   p=<font color="#000000">(NODE *)malloc(</font><font color="#ff0000"><font color="#3333ff">sizeof</font><font color="#000000">(NODE))</font></font>; <font color="#006600">/*强制类型转换为指针*/</font><br />   p-&gt;data=ch;<br />   <font color="#3333ff">if</font>(h==NULL) h=p;<br />      <font color="#3333ff">else</font><font color="#000000">q-&gt;next=p</font>;<br />   ch++;<br /><font color="#000000">   q=p;</font><br />   }<br />q-&gt;next=NULL; <font color="#006600">/*链表结束*/</font><br /><font color="#3333ff">return</font> h;<br />}</td></tr></tbody></table><p><img height="201" src="http://beastie.frontfree.net/articles/pages/0000000779/pic2.gif" width="287" /></p><p>逆向建立：</p><table class="code" width="100%" border="0"><tbody><tr><td bgcolor="#e0e0e0">NODE *create()<br />{<br /><font color="#3333ff">char</font> ch=<font color="#ff00ff">'a'</font>;<br />NODE *p,*h=NULL;<br /><font color="#3333ff">while</font>(ch&lt;=<font color="#ff00ff">'z'</font>)<br />   {<br />   p=<font color="#000000">(NODE *)malloc(</font><font color="#ff0000"><font color="#3333ff">sizeof</font><font color="#000000">(NODE))</font></font>;<br />   p-&gt;data=ch;<br />   p-&gt;next=h; <font color="#006600">/*不断地把head往前挪*/</font><br />   h=p;<br />   ch++;<br />   }<br /><font color="#3333ff">return</font> h;<br />}</td></tr></tbody></table><p><img height="166" src="http://beastie.frontfree.net/articles/pages/0000000779/pic3.gif" width="146" /></p><p>用递归实现链表逆序输出：</p><table class="code" width="100%" border="0"><tbody><tr><td bgcolor="#e0e0e0" c=""><font color="#3333ff">void</font> output(NODE *h)<br />{<br /><font color="#3333ff">if</font>(h!=NULL)<br />   {<br /><font color="#ff0000">   <font color="#000000">output(h-&gt;next)</font></font><font color="#000000">;</font><br />   printf(<font color="#ff00ff">"%c"</font>,h-&gt;data);<br />   }<br />}</td></tr></tbody></table><p>插入结点（已有升序的链表）：</p><table class="code" width="100%" border="0"><tbody><tr><td bgcolor="#e0e0e0">NODE *insert(NODE *h,<font color="#3333ff">int</font> x)<br />{<br />NODE *<font color="#3333ff">new</font>,*front,*current=h;<br /><font color="#3333ff">while</font>(current!=NULL&amp;&amp;(current-&gt;data&lt;x)) <font color="#006600">/*查找插入的位置*/</font><br />   {<br />   front=current;<br />   current=current-&gt;next;<br />   }<br /><font color="#3333ff">new</font>=<font color="#000000">(NODE *)malloc(</font><font color="#ff0000"><font color="#3333ff">sizeof</font><font color="#000000">(NODE))</font></font>;<br /><font color="#3333ff">new</font>-&gt;data=x;<br /><font color="#3333ff">new</font>-&gt;next=current;<br /><font color="#3333ff">if</font>(current==h) <font color="#006600">/*判断是否是要插在表头*/</font><br />   h=<font color="#3333ff">new</font>;<br /><font color="#3333ff">else</font> front-&gt;next=<font color="#3333ff">new</font>;<br /><font color="#3333ff">return</font> h;<br />}</td></tr></tbody></table><p><img height="131" src="http://beastie.frontfree.net/articles/pages/0000000779/pic4.gif" width="196" /></p><p>删除结点：</p><table class="code" width="100%" border="0"><tbody><tr><td bgcolor="#e0e0e0">NODE *<font color="#3333ff">delete</font>(NODE *h,<font color="#3333ff">int</font> x)<br />{<br />NODE *q,*p=h;<br /><font color="#3333ff">while</font>(p!=NULL&amp;&amp;(p-&gt;data!=x))<br />   {<br />   q=p;<br />   p=p-&gt;next;<br />   }<br /><font color="#3333ff">if</font>(p-&gt;data==x) <font color="#006600">/*找到了要删的结点*/</font><br />   {<br />   <font color="#3333ff">if</font>(p==h) <font color="#006600">/*判断是否要删表头*/</font><br />   h=h-&gt;next;<br />      <font color="#3333ff">else</font><font color="#000000">q-&gt;next=p-&gt;next;<br />   free(p); </font><font color="#006600">/*释放掉已删掉的结点*/</font><br />   }<br /><font color="#3333ff">return</font> h;<br />}</td></tr></tbody></table><p><img height="100" src="http://beastie.frontfree.net/articles/pages/0000000779/pic5.gif" width="227" /></p><p>经常有链表相关的程序填空题，做这样的题要注意看下面提到的变量是否定义了，用到的变量是否赋初值了，是否有给分配空间的没有分配空间，最后看看返回值是否正确。</p><p>笔者水平有限，难免有疏漏、错误的地方，浅显之处，还望指正见谅。上述内容仅是个提示作用，并不包括C语言的全部内容。 </p></table>
<img src ="http://www.blogjava.net/faintbear/aggbug/58871.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/faintbear/" target="_blank">小力力力</a> 2006-07-18 23:29 <a href="http://www.blogjava.net/faintbear/archive/2006/07/18/58871.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>链表3</title><link>http://www.blogjava.net/faintbear/archive/2006/07/17/58657.html</link><dc:creator>小力力力</dc:creator><author>小力力力</author><pubDate>Mon, 17 Jul 2006 14:56:00 GMT</pubDate><guid>http://www.blogjava.net/faintbear/archive/2006/07/17/58657.html</guid><wfw:comment>http://www.blogjava.net/faintbear/comments/58657.html</wfw:comment><comments>http://www.blogjava.net/faintbear/archive/2006/07/17/58657.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/faintbear/comments/commentRss/58657.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/faintbear/services/trackbacks/58657.html</trackback:ping><description><![CDATA[
		<table cellspacing="0" cellpadding="0" width="100%" align="center" border="0">
				<tbody>
						<tr>
								<td style="FONT-SIZE: 18px" valign="bottom" align="middle" width="85%" bgcolor="#dddddd" height="20">
										<strong>
												<font color="#003399" size="4">
														<b>链表的运算(02) </b>
												</font>
										</strong>
								</td>
								<br />
						</tr>
						<tr>
								<td align="middle" width="100%">
										<br />
								</td>
						</tr>
						<tr>
								<td style="FONT-SIZE: 9pt" align="middle" width="100%">发表日期：2003年4月8日    作者：C语言之家搜集整理  已经有2462位读者读过此文</td>
						</tr>
						<tr>
								<td align="middle" width="100%">
										<!--下面的这一句是设置阅读文本区的宽度-->
										<table style="TABLE-LAYOUT: fixed" cellspacing="0" cellpadding="0" width="90%" align="center" border="0">
												<tbody>
														<tr>
																<td align="middle" width="100%">
																</td>
														</tr>
														<tr>
																<td style="WORD-WRAP: break-word">
																		<font class="news">
																				<br />
																				<font color="#0000ff">3.链表节点的插入<br />4.链表节点的删除</font>
																				<br />
																				<br />
																				<br />
																				<br />
																				<font color="#0000ff">3.链表节点的插入</font>
																				<br />解：<br />    1) 首先声明一个新节点供输入要插入节点的内容<br />    2) 由用户输入一个节点内容(Key)，表示欲插入在哪一个节点之后<br />    3) 持续往下一个节点，直到节点内容Key或节点指针为NULL为止(即找不到该节点)<br />    4) 如果该节点不存在，则插入在节点前<br />        New-&gt;Next=Head<br />        Head=New<br />    5) 如果找到该节点，则<br />        New-&gt;Next=Pointer-&gt;Next<br />        Pointer-&gt;Next=New<br />*程序代码如下：<br />#include&lt;stdlib.h&gt;<br />#include&lt;stdio.h&gt;<br />#define Max 10<br />struct List            /*节点结构声明*/<br />{<br />    int Number;<br />    int Total;<br />    struct List *Next;<br />};<br />typedef struct List Node;<br />typedef Node *Link;<br />int Data[2][Max]={1,3,5,7,2,4,6,8,9,0,15,35,10,67,25,65,38,70,30,20};<br />/*插入节点至链表内*/<br />Link Insert_List(Link Head,Link New,int Key)<br />{<br />    Link Pointer;        /*声明节点*/<br />    Pointer=Head;        /*Pointer指针设为首节点*/<br />    while(1)<br />    {<br />        if(Pointer==NULL)    /*插入在首节点前*/<br />        {<br />            New-&gt;Next=Head;<br />            Head=New;<br />            break;<br />        }<br />        if(Pointer-&gt;Number==Key)    /*插入在链表中间或尾端*/<br />        {<br />            New-&gt;Next=Pointer-&gt;Next;<br />            Pointer-&gt;Next=New;<br />            break;<br />        }<br />        Pointer=Pointer-&gt;Next;    /*指向下一个节点*/<br />    }<br />    return Head;<br />}<br />/*输出链表数据*/<br />void Print_List(Link Head)<br />{<br />    Link Pointer;        /*节点声明*/<br />    Pointer=Head;        /*Pointer指针设为首节点*/<br />    while(Pointer!=NULL)    /*当节点为NULL结束循环*/<br />    {<br />        printf("[%d,%d]",Pointer-&gt;Number,Pointer-&gt;Total);<br />        Pointer=Pointer-&gt;Next;    /*指向下一个节点*/<br />    }<br />    printf("\n");<br />}<br />/*释放链表*/<br />void Free_List(Link Head)<br />{<br />    Link Pointer;        /*节点声明*/<br />    while(Head!=NULL)    /*当节点为NULL结束循环*/<br />    {<br />        Pointer=Head;<br />        Head=Head-&gt;Next;<br />        free(Pointer);<br />    }<br />}<br />/*建立链表*/<br />Link Create_List(Link Head)<br />{<br />    Link New;        /*节点声明*/<br />    Link Pointer;    /*节点声明*/<br />    int i;<br />    Head=(Link)malloc(sizeof(Node));    /*分配内存*/<br />    if(Head==NULL)<br />        printf("Memory allocate Failure!\n");    /*内存分配失败*/<br />    else<br />    {<br />        Head-&gt;Number=Data[0][0];        /*定义首节点数据编号*/<br />        Head-&gt;Total=Data[1][0];<br />        Head-&gt;Next=NULL;<br />        Pointer=Head;        /*Pointer指针设为首节点*/<br />        for(i=1;i&lt;Max;i++)<br />        {<br />            New=(Link)malloc(sizeof(Node));    /*分配内存*/<br />            New-&gt;Number=Data[0][i];<br />            New-&gt;Total=Data[1][i];<br />            New-&gt;Next=NULL;<br />            Pointer-&gt;Next=New;        /*将新节点串连在原列表尾端*/<br />            Pointer=New;            /*列表尾端节点为新节点*/<br />        }<br />    }<br />    return Head;<br />}<br />/*主程序*/<br />void main()<br />{<br />    Link Head;        /*节点声明*/<br />    Link New;<br />    int Key;<br />    Head=Create_List(Head);    /*建立链表*/<br />    if(Head!=NULL)<br />    {<br />        Print_List(Head);    <br />        while(1)<br />        {<br />            printf("Input 0 to Exit\n");    /*数据输入提示*/<br />            New=(Link)malloc(sizeof(Node));    /*分配内存*/<br />            printf("Please input Data number:");<br />            scanf("%d",&amp;New-&gt;Number);<br />            if(New-&gt;Number==0)        /*输入0时结束循环*/<br />                break;<br />            printf("Please input the data total:");<br />            scanf("%d",&amp;New-&gt;Total);<br />            printf("Please input the data number for Insert:");<br />            scanf("%d",&amp;Key);<br />            Head=Insert_List(Head,New,Key);    /*插入节点*/<br />            Print_List(Head);                /*输出链表数据*/<br />        }<br />        Free_List(Head);        /*释放链表*/<br />    }<br />}<br />*程序运行结果如下：<br /><p align="left"></p><p align="center"><img src="http://www.cstudyhome.com/wenzhang06/uploadpic/20034814224081056.gif" onload="javascript:if(this.width&gt;screen.width-333)this.width=screen.width-333" /></p>------------------------------------------------------------------<br /><br /><font color="#0000ff">4.链表节点的删除</font><br />解：<br />    持续往下一个节点查找要删除的节点，直到节点内容找到或节点指针为NULL(即找不到该节点)。<br />    在删除时，必须记录前一个节点的位置(Back)<br />    如果该节点不存在，输出一个节点不存在的提示<br />    如果该节点存在，且是首节点：<br />        Head=Pointer-&gt;Next<br />        free(Pointer)<br />    如果该节点存在，但不是首节点(即链表内节点或尾端节点)，则：<br />        Back-&gt;Next=Pointer-&gt;Next<br />        free(Pointer)<br />*程序代码如下：<br />#include&lt;stdio.h&gt;<br />#include&lt;stdlib.h&gt;<br />#define Max 10<br />struct List            /*节点结构声明 */<br />{<br />    int Number;<br />    int Total;<br />    struct List *Next;<br />};<br />typedef struct List Node;<br />typedef Node *Link;<br />int Data[2][Max]={1,3,5,7,2,4,6,8,9,10,15,35,10,67,25,65,38,70,30,20};<br />/*删除链表内节点*/<br />Link Delete_List(Link Head,int Key)<br />{<br />    Link Pointer;        /*节点声明*/<br />    Link Back;<br />    Pointer=Head;        /*Pointer 指针设为首节点*/<br />    while(1)<br />    {<br />        if(Pointer-&gt;Next==NULL)<br />        {<br />            printf("Not Found!\n");<br />            break;<br />        }<br />        if(Head-&gt;Number==Key)        /*删除首节点*/<br />        {<br />            Head=Pointer-&gt;Next;<br />            free(Pointer);<br />            break;<br />        }<br />        Back=Pointer;<br />        Pointer=Pointer-&gt;Next;    /*指向下一个节点*/<br />        if(Pointer-&gt;Number==Key)    /*插入在链表中间或尾端*/<br />        {<br />            Back-&gt;Next=Pointer-&gt;Next;<br />            free(Pointer);<br />            break;<br />        }<br />    }<br />    return Head;<br />}<br />/*输出链表数据*/<br />void Print_List(Link Head)<br />{<br />    Link Pointer;        /*节点声明*/<br />    Pointer=Head;        /*Pointer指针设为首节点*/<br />    while(Pointer!=NULL)    /*当节点为NULL结束循环*/<br />    {<br />        printf("[%d,%d]",Pointer-&gt;Number,Pointer-&gt;Total);<br />        Pointer=Pointer-&gt;Next;    /*指向下一个节点*/<br />    }<br />    printf("\n");<br />}<br />/*释放链表*/<br />void Free_List(Link Head)<br />{<br />    Link Pointer;        /*节点声明*/<br />    while(Head!=NULL)    /*当节点为NULL结束循环*/<br />    {<br />        Pointer=Head;<br />        Head=Head-&gt;Next;        /*指向下一个节点*/<br />        free(Pointer);<br />    }<br />}<br />/*建立链表*/<br />Link Create_List(Link Head)<br />{<br />    Link New;        /*节点声明*/<br />    Link Pointer;<br />    int i;<br />    Head=(Link)malloc(sizeof(Node));        /*分本内存*/<br />    if(Head==NULL)<br />        printf("Memory allocate Failure!\n");    /*内存分配失败*/<br />    else<br />    {<br />        Head-&gt;Number=Data[0][0];        /*定义首节点数据编号*/<br />        Head-&gt;Total=Data[1][0];<br />        Head-&gt;Next=NULL;<br />        Pointer=Head;        /*Pointer指针设为首节点*/<br />        for(i=1;i&lt;Max;i++)<br />        {<br />            New=(Link)malloc(sizeof(Node));        /*分配内存*/<br />            New-&gt;Number=Data[0][i];<br />            New-&gt;Total=Data[1][i];<br />            New-&gt;Next=NULL;<br />            Pointer-&gt;Next=New;        /*将新节点串连在原列表尾端*/<br />            Pointer=New;            /*列表尾端节点为新节点*/<br />        }<br />    }<br />    return Head;<br />}<br />/*主程序*/<br />void main()<br />{<br />    Link Head=NULL;        /*节点声明*/<br />    int Key;<br />    Head=Create_List(Head);        /*建立链表*/<br />    if(Head!=NULL)<br />    {<br />        Print_List(Head);<br />        while(1)<br />        {<br />            printf("Input 0 to exit\n");    /*数据输入提示*/<br />            printf("Please input the data number for Delete:");<br />            scanf("%d",&amp;Key);<br />            if(Key==0)        /*时结束循环*/<br />                break;<br />            Head=Delete_List(Head,Key);    /*删除节点*/<br />            Print_List(Head);              /*输出链表*/<br />        }<br />        Free_List(Head);        /*释放链表*/<br />    }<br />}<font color="#0000ff"><br /><br /><font color="#000000">*程序运行结果如下：</font><br /><p align="left"></p><p align="center"><img src="http://www.cstudyhome.com/wenzhang06/uploadpic/2003481518569133.gif" onload="javascript:if(this.width&gt;screen.width-333)this.width=screen.width-333" /></p>   </font></font>
																</td>
														</tr>
												</tbody>
										</table>
								</td>
						</tr>
				</tbody>
		</table>
<img src ="http://www.blogjava.net/faintbear/aggbug/58657.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/faintbear/" target="_blank">小力力力</a> 2006-07-17 22:56 <a href="http://www.blogjava.net/faintbear/archive/2006/07/17/58657.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>链表2</title><link>http://www.blogjava.net/faintbear/archive/2006/07/17/58656.html</link><dc:creator>小力力力</dc:creator><author>小力力力</author><pubDate>Mon, 17 Jul 2006 14:54:00 GMT</pubDate><guid>http://www.blogjava.net/faintbear/archive/2006/07/17/58656.html</guid><wfw:comment>http://www.blogjava.net/faintbear/comments/58656.html</wfw:comment><comments>http://www.blogjava.net/faintbear/archive/2006/07/17/58656.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/faintbear/comments/commentRss/58656.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/faintbear/services/trackbacks/58656.html</trackback:ping><description><![CDATA[
		<font color="#0000ff">2.设计一个查找链表中的数据的程序<br /></font>解：<br />    单链表中的数据查找，只能采用线性查找法往下一个节点查找。采用线性查找法查找链表中的数据时与数组不同的是，原来数组是用递增数组索引来查找数据，在链表中是往下一个节点查找。<br />程序代码如下：<br />#include&lt;stdio.h&gt;<br />#include&lt;stdlib.h&gt;<br />#define Max 10<br />struct List        /*结节点结构声明*/    <br />{<br />    int Number;<br />    int Total;<br />    struct List *Next;<br />};<br />typedef struct List Node;<br />typedef Node *Link;<br />int Data[2][Max]=              /*初始化数据*/<br />        {3,9,25,5,7,26,65,80,2,6,1050,3850,1000,5670,2250,9650,2380,<br />            1700,3000,2000};<br />int SearchTime=0;        /*查找次数*/<br />/*链表查找*/<br />int List_Search(int Key,Link Head)<br />{<br />    Link Pointer;<br />    Pointer=Head;        /*Pointer指针设为首节点*/<br />    while(Pointer!=NULL)    /*当节点为NULL结束循环*/<br />    {<br />        SearchTime++;<br />        if(Pointer-&gt;Number==Key)<br />        {<br />            printf("Data Number: %d\n",Pointer-&gt;Number);<br />            printf("Data Total: %d\n",Pointer-&gt;Total);<br />            return 1;<br />        }<br />        Pointer=Pointer-&gt;Next;      /*指向下一个节点*/<br />    }<br />    return 0;<br />}<br />/*释放链表*/<br />void Free_List(Link Head)<br />{<br />    Link Pointer;        /*节点声明*/<br />    while(Head!=NULL)    /*当节点为NULL结束循环*/<br />    {<br />        Pointer=Head;<br />        Head=Head-&gt;Next;    /*指向下一个节点*/<br />        free(Pointer);<br />    }<br />}<br />/*建立链表*/<br />Link Create_List(Link Head)<br />{<br />    Link New;        /*节点声明*/<br />    Link Pointer;    /*节点声明*/<br />    int i;<br />    Head=(Link)malloc(sizeof(Node));    /*分配内存*/<br />    if(Head==NULL)<br />        printf("Memory allocate Failure!\n");    /*内存分配失败*/<br />    else<br />    {<br />        Head-&gt;Number=Data[0][0];        /*定义首节点数据编号*/<br />        Head-&gt;Total=Data[1][0];<br />        Head-&gt;Next=NULL;<br />        Pointer=Head;            /*Pointer指针设为首节点*/<br />        for(i=1;i&lt;Max;i++)<br />        {<br />            New=(Link)malloc(sizeof(Node));    /*分配内存*/<br />            New-&gt;Number=Data[0][i];<br />            New-&gt;Total=Data[1][i];<br />            New-&gt;Next=NULL;<br />            Pointer-&gt;Next=New;        /*将新节点串连在原列表尾端*/<br />            Pointer=New;              /*列表尾端节点为新节点*/<br />        }<br />    }<br />    return Head;<br />}<br />/*主程序*/<br />void main()<br />{<br />    Link Head=NULL;        /*节点声明*/<br />    int Num;          /*欲查找数据编号*/<br />    Head=Create_List(Head);    /*建立链表*/<br />    if(Head!=NULL)<br />    {<br />        printf("Please input the data number:");<br />        scanf("%d",&amp;Num);<br />        if(List_Search(Num,Head))<br />            printf("Search Time=%d\n",SearchTime);<br />        else<br />            printf("Not Found!\n");<br />        Free_List(Head);        /*释放链表*/<br />    }<br />}<br />*运行结果如下：<br /><p align="left"></p><p align="center"><img src="http://www.cstudyhome.com/wenzhang06/uploadpic/20034813101264722.gif" onload="javascript:if(this.width&gt;screen.width-333)this.width=screen.width-333" /></p><img src ="http://www.blogjava.net/faintbear/aggbug/58656.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/faintbear/" target="_blank">小力力力</a> 2006-07-17 22:54 <a href="http://www.blogjava.net/faintbear/archive/2006/07/17/58656.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>链表</title><link>http://www.blogjava.net/faintbear/archive/2006/07/17/58655.html</link><dc:creator>小力力力</dc:creator><author>小力力力</author><pubDate>Mon, 17 Jul 2006 14:53:00 GMT</pubDate><guid>http://www.blogjava.net/faintbear/archive/2006/07/17/58655.html</guid><wfw:comment>http://www.blogjava.net/faintbear/comments/58655.html</wfw:comment><comments>http://www.blogjava.net/faintbear/archive/2006/07/17/58655.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/faintbear/comments/commentRss/58655.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/faintbear/services/trackbacks/58655.html</trackback:ping><description><![CDATA[
		<table cellspacing="0" cellpadding="0" width="100%" align="center" border="0">
				<tbody>
						<tr>
								<td style="FONT-SIZE: 18px" valign="bottom" align="middle" width="85%" bgcolor="#dddddd" height="20">
										<strong>
												<font color="#003399" size="4">
														<b>链表的运算(01) </b>
												</font>
										</strong>
								</td>
								<br />
						</tr>
						<tr>
								<td align="middle" width="100%">
										<br />
								</td>
						</tr>
						<tr>
								<td style="FONT-SIZE: 9pt" align="middle" width="100%">发表日期：2003年4月8日    作者：C语言之家搜集整理  已经有3901位读者读过此文</td>
						</tr>
						<tr>
								<td align="middle" width="100%">
										<!--下面的这一句是设置阅读文本区的宽度-->
										<table style="TABLE-LAYOUT: fixed" cellspacing="0" cellpadding="0" width="90%" align="center" border="0">
												<tbody>
														<tr>
																<td align="middle" width="100%">
																</td>
														</tr>
														<tr>
																<td style="WORD-WRAP: break-word">
																		<font class="news">
																				<br />
																				<font color="#0000ff">1.设计一个程序将输入的数据建立成链表、输出链表数据并在程序结束后释放。<br /><font color="#0000ff">2.设计一个查找链表中的数据的程序</font><br /></font>
																				<br />
																				<br />
																				<br />
																				<font color="#0000ff">1.设计一个程序将输入的数据建立成链表、输出链表数据并在程序结束后释放。<br /></font>解：<br />    1)链表的建立：先声明一个首节点Head，并将Head-&gt;Next设为NULL。每输入一个数据就声明一个新节点New，把New-&gt;Next设为NULL，并且链接到之前列表的尾端。<br />    2)链表数据的输出：先将Pointer节点的指针指向第一个节点，将Pointer节点(即第一个节点)的数据输出。然后再将Pointer节点的指针指向Pointer指针的的指针(即下一节点)，将pointer节点(即第一节点)的数据输出。重复执行此步聚直到Pointer指针指向NULL为止。<br />    3)链表的释放：先将Pointer节点的指针指向第一个节点，然后再将首节点设为首节点的指针(即下一节点)。将Pointer节点(即第一节点)释放。重复执行此步聚直到首节点的指针指向NULL为止。<br />程序代码如下：<br />#include&lt;stdlib.h&gt;<br />#include&lt;stdio.h&gt;<br />#define Max 10<br />struct List             /*节点结构声明*/<br />{<br />    int Number;<br />    char Name[Max];<br />    struct List *Next;<br />};<br />typedef struct List Node;<br />typedef Node *Link;<br />/*释放链表*/<br />void Free_List(Link Head)<br />{<br />    Link Pointer;      /*节点声明*/<br />    while(Head!=NULL)      /*当节点为NULL，结束循环*/<br />    {<br />        Pointer=Head;<br />        Head=Head-&gt;Next;   /*指向下一个节点*/<br />        free(Pointer);<br />    }<br />}<br />/*输出链表*/<br />void Print_List(Link Head)<br />{<br />    Link Pointer;          /*节点声明*/<br />    Pointer=Head;          /*Pointer指针设为首节点*/<br />    while(Pointer!=NULL)   /*当节点为NULL结束循环*/<br />    {<br />        printf("##Input Data##\n");<br />        printf("Data Number: %d\n",Pointer-&gt;Number);<br />        printf("Data Name: %s\n",Pointer-&gt;Name);<br />        Pointer=Pointer-&gt;Next;     /*指向下一个节点*/<br />    }<br />}<br />/*建立链表*/<br />Link Create_List(Link Head)<br />{<br />    int DataNum;         /*数据编号*/<br />    char DataName[Max];        /*数据名称*/<br />    Link New;            /*节点声明*/<br />    Link Pointer;        /*节点声明*/<br />    int i;<br />    Head=(Link)malloc(sizeof(Node));     /*分配内存*/<br />    if(Head==NULL)<br />        printf("Memory allocate Failure!\n");    /*内存分配夫败*/<br />    else<br />    {<br />        DataNum=1;      /*初始数据编号*/<br />        printf("Please input the data name:");<br />        scanf("%s",DataName);<br />        Head-&gt;Number=DataNum;     /*定义首节点数据编号*/<br />        for(i=0;i&lt;=Max;i++)<br />            Head-&gt;Name[i]=DataName[i];<br />        Head-&gt;Next=NULL;<br />        Pointer=Head;          /*Pointer指针设为首节点*/<br />        while(1)<br />        {<br />            DataNum++;         /*数据编号递增*/<br />            New=(Link)malloc(sizeof(Node));     /*分配内存*/<br />            printf("Please input the data Name:");<br />            scanf("%s",DataName);<br />            if(DataName[0]=='0')    /*输入0则结束*/<br />                break;<br />            New-&gt;Number=DataNum;<br />            for(i=0;i&lt;Max;i++)<br />            {<br />                New-&gt;Name[i]=DataName[i];<br />            }<br />            New-&gt;Next=NULL;<br />            Pointer-&gt;Next=New;     /*将新节点串连在原列表尾端*/<br />            Pointer=New;         /*列表尾端节点为新节点*/<br />        }<br />    }<br />    return Head;<br />}<br />/*主程序*/<br />void main()<br />{<br />    Link Head;       /*节点声明*/<br />    Head=Create_List(Head);     /*调用建立链表函数*/<br />    if(Head!=NULL)<br />    {<br />        Print_List(Head);      /*调用输出链表数据函数*/<br />        Free_List(Head);       /*调用释放链表函数*/<br />    }<br />}<br /><br />运行结果如下：<br /><p align="left"></p><p align="center"><img src="http://www.cstudyhome.com/wenzhang06/uploadpic/2003481243969783.gif" onload="javascript:if(this.width&gt;screen.width-333)this.width=screen.width-333" /></p></font>
																</td>
														</tr>
												</tbody>
										</table>
								</td>
						</tr>
				</tbody>
		</table>
<img src ="http://www.blogjava.net/faintbear/aggbug/58655.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/faintbear/" target="_blank">小力力力</a> 2006-07-17 22:53 <a href="http://www.blogjava.net/faintbear/archive/2006/07/17/58655.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>MIDP2.0及其移植技术分析(转)</title><link>http://www.blogjava.net/faintbear/archive/2006/07/12/57798.html</link><dc:creator>小力力力</dc:creator><author>小力力力</author><pubDate>Wed, 12 Jul 2006 06:32:00 GMT</pubDate><guid>http://www.blogjava.net/faintbear/archive/2006/07/12/57798.html</guid><wfw:comment>http://www.blogjava.net/faintbear/comments/57798.html</wfw:comment><comments>http://www.blogjava.net/faintbear/archive/2006/07/12/57798.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/faintbear/comments/commentRss/57798.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/faintbear/services/trackbacks/57798.html</trackback:ping><description><![CDATA[
		<p>
				<b>摘要：</b>MIDP即移动信息设备规范，是专门基于资源和网络连接有限的移动设备之上的J2ME应用规范。本文在分析MIDP2.0的基础上，详细阐述MIDP的事件处理、文件系统、用户图形接口和网络等主要部分在不同平台间移植的实现过程。 
</p>
		<p class="MsoNormal">
				<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">    <b>关键词：</b></span>J2ME MIDP 移植 平台无关 本地代码</p>
		<p style="TEXT-INDENT: 0px">
				<b>1 MIDP2.0简介</b>
		</p>
		<p style="TEXT-INDENT: 30px">随着现代信息化社会的发展，小型移动通信设备已经从最初的一种单纯的通信工具转变成如今集通信、工作、娱乐等功能为一体的综合设备；但仅有这些还不能满足用户的要求。个性永远是千变万化的，时尚也不会始终为一种模式。因此，在移动终端上开发通用的、丰富的应用已成为必然的趋势。这些应用可以按用户的意愿随时安装和删除。</p>
		<p style="TEXT-INDENT: 30px">J2ME（JAVA2 Micro Edition）正是这样一种JAVA应用开发平台。实际上，JAVA语言从其诞生起就以其运行的平台无关性这一强大的优势而成为网络应用的宠儿。J2ME是JAVA2标准版本的微型版本，专门为小型移动设备所设计。这些设备处理器的处理能力都不强，可使用的资源也有限。因此，J2ME只包含了J2SE中在移动通信设备上所必需的功能和组件，使其能够在移动设备及其有限的资源上开发出丰富多彩且平台无关的应用。J2ME在结构上分为CDC（Connecte Device Configuration）和基于其上，以Foundation Profile为主的规范，以及CLDC（Connecte Limited Device Configuration）和基于其上，以MIDP为主的规范。</p>
		<p style="TEXT-INDENT: 30px">MIDP（Mobile Information Device Profile）是移信息设备规范的简称。规范具体定义了J2ME适用的硬件和软件框架，并提供了这个框架要实现的基本功能及其标准接口；而应用开发者就可以基于这个框架开发出各种应用。2000年9月，SUN公司发布了MIDP的第一个正式版本MIDP1.0。它将J2ME适用的设备定位在至少拥有数百KB RAM和ROM，并具有基本网络和显示功能的移动通信设备上；在该基础上定义了一系列软件接的移动通信设备上；在该上基础上定义了一系列软件接口，其中包括基本输入输出、图形化用户接口（GUI）、网络、事件机制、文件系统、应用管理系统（AMS）等之后，随着JAVA技术的不断发展和用户需求的不断提高，SUN公司又于2002年11月发布了MIDP2.0。它对设备的内存资源和处理能力的要求较1.0要高，但MIDP2.0也为应用开发者提供了更方便、更丰富多彩的软件包，主要增加了游戏接口的实现、声音输出接口的实现安全网络机制的实现。MIDP2.0的这些特性将使基于移动设备的JAVA应用具有更加广阔的前景，也必将使新一代的移动设备发生革命性的变化并领导时尚潮流。MIDP2.0接口包如表1所列。<br /><br /><b>表1 MIDP2.0接口包及其功能</b></p>
		<table bordercolordark="#ffffff" width="100%" bordercolorlight="#000000" border="1">
				<tbody>
						<tr class="main">
								<td align="middle" width="45%">包</td>
								<td align="middle" width="55%">功      能</td>
						</tr>
						<tr class="main">
								<td width="45%">javax.microedition.lcdui</td>
								<td align="middle" width="55%">提供一系列用户界面接口</td>
						</tr>
						<tr class="main">
								<td width="45%">javax.microedition.lcdui.game</td>
								<td align="middle" width="55%">专门用于游戏设计的接口</td>
						</tr>
						<tr class="main">
								<td width="45%">javax.microedition.rms</td>
								<td align="middle" width="55%">数据管理，用于保存数据记录</td>
						</tr>
						<tr class="main">
								<td width="45%">javax.microedition.midlet</td>
								<td align="middle" width="55%">JAVA应用管理接口</td>
						</tr>
						<tr class="main">
								<td width="45%">javax.microedition.io</td>
								<td align="middle" width="55%">基本网络连接接口</td>
						</tr>
						<tr class="main">
								<td width="45%">javax.microedition.media</td>
								<td align="middle" width="55%">媒体接口规范（JSR-135）的实现包</td>
						</tr>
						<tr class="main">
								<td width="45%">javax.microedition.media.control</td>
								<td align="middle" width="55%">媒体播放器的控制类</td>
						</tr>
						<tr class="main">
								<td width="45%">javax.microedition.pki</td>
								<td align="middle" width="55%">数字签名规范的实现接口（用于安全网络）</td>
						</tr>
						<tr class="main">
								<td width="45%">java.io</td>
								<td align="middle" width="55%">JAVA基本输入输出接口</td>
						</tr>
						<tr class="main">
								<td width="45%">java.lang</td>
								<td align="middle" width="55%">JAVA基本数据类型接口</td>
						</tr>
						<tr class="main">
								<td width="45%">java.util</td>
								<td align="middle" width="55%">JAVA基本应用接口</td>
						</tr>
				</tbody>
		</table>
		<p style="TEXT-INDENT: 0px">
				<b>2 MIDP2.0的移植</b>
		</p>
		<p style="TEXT-INDENT: 30px">既然MIDP2.0是定位在移动通信设备之上的一系列JAVA应用开发接口，我们就必须考虑如何将整个MIDP系统嵌入到特定的硬件设备和其上的操作系统中。只有这样，JAVA应用程序才能运行在该设备上，并利用MIDP提供的强大功能将用户带入一个全新的JAVA世界。在一个完全不同的操作系统平台上，用该平台上对应的系统API调用（或一系列操作），替换MIDP参考实现中所有与操作平台相关的调用（或操作），使MIDP能在目标平台上正确地执行所有要求的功能；同时，调用该平台上特有的能充分发挥目标设备硬件特性的接口，替换参考实现中相应的接口，使MIDP能在目标平台上更高效地运行，这个过程就称为MIDP的移植。</p>
		<p style="TEXT-INDENT: 30px">MIDP由许多不同的部分组成，每一部分完成MIDP一个特定的功能接口。其中需要移植的部分主要包括事件处理、文件系统、用户图形化接口、网络、AMS、多媒体。它们都分为高端的JAVA层和低端的本地方法层。JAVA层是用JAVA语言实现的，由KVM解释执行；因此没有涉及到与操作系统平台相关的调用和操作，可以不经修改就在任何操作平台上运行，是平台无关的（PlateForm Independent）。它的移植主要是为满足用户的特殊要求而进行的个性化工作。本地方法层（NativeCode）是指为提高代码的执行效率，保持JAVA语言的平台无关性而使用C语言实现的部分MIDP功能的代码。本地即是指它是与当前的操作平台相关的，它的移植才是涉及到具体平台和执行效率而进行的具体调用和操作的替换过程，其结构如图1所示。</p>
		<p style="TEXT-INDENT: 30px">下面，我们就具体到MIDP的每一个部分的移植进行讨论。</p>
		<p style="TEXT-INDENT: 30px">2.1 事件处理</p>
		<p style="TEXT-INDENT: 30px">MIDP的事件处理部分要处理的事件主要来自两个方面：①来自虚拟机底层的事件，如虚拟机的异常消息；②来自MIDP内部的事件，如屏幕刷新、按键消息、触控笔点击消息、时钟消息等。由于不同的平台可能使用不同的事件消息获取和传递机制，因此MIDP事件处理的移植也集中在这上面，其实现被放在本地方法层的文件nativeGUI.c中的函数GetAndStoreKVMEvent中。我们只需根据目标平台获取和传递事件的要求修改该文件中的相应函数即可。</p>
		<p style="TEXT-INDENT: 30px">例如，Windows采用消息响应机制来处理各种事件，所有消息都可以通过系统API调用GetMessage获取，系统会调用消息处理函数WndProc(HWND hwnd、UINTiMsg、WPARAM wParam、LRARAM 1Param)，在其中处理和传递不同的事件。其大致实现过程如下：</p>
		<p style="TEXT-INDENT: 30px">void</p>
		<p style="TEXT-INDENT: 30px">GetAndStoreNextKVMEvent(bool_t forever,ulong64 waitUntil){</p>
		<p style="TEXT-INDENT: 30px">MSG msg；</p>
		<p style="TEXT-INDENT: 30px">……</p>
		<p style="TEXT-INDENT: 30px">while(GetMessage(&amp;msg,NULL,0,0)){</p>
		<p style="TEXT-INDENT: 30px">……</p>
		<p style="TEXT-INDENT: 30px">TranslateMessage(&amp;msg);</p>
		<p style="TEXT-INDENT: 30px">DispatchMessage(&amp;msg);</p>
		<p style="TEXT-INDENT: 30px">if(gotEvent){</p>
		<p style="TEXT-INDENT: 30px">StoreMIDPEvent(&amp;kvmEvent);</p>
		<p style="TEXT-INDENT: 30px">Return;</p>
		<p style="TEXT-INDENT: 30px">}</p>
		<p style="TEXT-INDENT: 30px">}</p>
		<p style="TEXT-INDENT: 30px">return;</p>
		<p style="TEXT-INDENT: 30px">}</p>
		<p style="TEXT-INDENT: 30px">static LRESULT CALLBACK</p>
		<p style="TEXT-INDENT: 30px">WndProc (HWND hwnd,UINT iMsg,WPARAM wParam,LPARAM lParam){</p>
		<p style="TEXT-INDENT: 30px">……</p>
		<p style="TEXT-INDENT: 30px">switch(iMsg){</p>
		<p style="TEXT-INDENT: 30px">case WM_CREATE:</p>
		<p style="TEXT-INDENT: 30px">……</p>
		<p style="TEXT-INDENT: 30px">case WM_KEYDOWN:</p>
		<p style="TEXT-INDENT: 30px">case WM_KEYUP:</p>
		<p style="TEXT-INDENT: 30px">……</p>
		<p style="TEXT-INDENT: 30px">}</p>
		<p style="TEXT-INDENT: 30px">}</p>
		<p style="TEXT-INDENT: 30px">MIDP在GetAndStoreNextKVMEvent中获取事件后，就完全独立地传递和处理事件消息，与平台无关，因此无需移植。<br /><img height="249" hspace="10" src="http://www.embed-dsp.com/Files/image/2006-4/18/0641822021626193.gif" width="549" vspace="10" border="0" /><br /><font size="3">    </font><span class="main1">2.2 文件系统</span></p>
		<p style="TEXT-INDENT: 30px">基于MIDP的JAVA应用以及MIDP本身在某些时候要求数据能够长期保存，即使在应用退出或系统掉电的情况下，数据也不能丢失。这就必须借助于MIDP的文件系统。MIDP的文件系统同样分为JAVA层（称为RMS，即Record Manage System）和本地方法层。一般情况下，文件系统的JAVA层不用移植就可以在任何平台上运行，但如果目标平台的文件系统较为特殊，例如采用数据库的记录方式保存数据，甚至根本就没有提供高效的数据存取接口，我们就必须自己实现数据存取接口。这样，JAVA层就需要跳过RMS而直接通过本地方法调用本地接口，相应的RMS的JAVA代码就可以从MIDP中删去。</p>
		<p style="TEXT-INDENT: 30px">而在文件系统的本地方法层，MIDP会调用目标平台的数据存取接口来实现MIDP本身的数据存取。这些接口是平台相关的，是文件系统中需要移植的部分。这些调用被放在文件src/share/native/storage.c中，主要包括文件的打开（open）、文件的关闭（close）、文件的读写（read、write）、文件属性的获取（size、freesize等）、文件的删除（delete）、文件的定位（seek）、文件的删节（truncate）等。以下便是MIDP文件系统在Windows下的部分实现。</p>
		<p style="TEXT-INDENT: 30px">文件的打开：</p>
		<p style="TEXT-INDENT: 30px">int storageOpen(char**ppszError,char*pszAsciiFilename,int ioMode){</p>
		<p style="TEXT-INDENT: 30px">……</p>
		<p style="TEXT-INDENT: 30px">handle=open(pszAsciiFilename,flags,creationMode)；</p>
		<p style="TEXT-INDENT: 30px">……</p>
		<p style="TEXT-INDENT: 30px">}</p>
		<p style="TEXT-INDENT: 30px">文件的关闭：</p>
		<p style="TEXT-INDENT: 30px">void storageClose(char**ppszError,int handle){</p>
		<p style="TEXT-INDENT: 30px">……</p>
		<p style="TEXT-INDENT: 30px">status=close(handle)；</p>
		<p style="TEXT-INDENT: 30px">……</p>
		<p style="TEXT-INDENT: 30px">}</p>
		<p style="TEXT-INDENT: 30px">文件的读取：</p>
		<p style="TEXT-INDENT: 30px">int storageRead(char**ppszError,int handle,char*buffer,int length)</p>
		<p style="TEXT-INDENT: 30px">{</p>
		<p style="TEXT-INDENT: 30px">……</p>
		<p style="TEXT-INDENT: 30px">bytesRead=read(handle,buffer,length);</p>
		<p style="TEXT-INDENT: 30px">……</p>
		<p style="TEXT-INDENT: 30px">}</p>
		<p style="TEXT-INDENT: 30px">文件的写入：</p>
		<p style="TEXT-INDENT: 30px">void storageWrite(char**ppszError,int handle,char*buffer,int length){</p>
		<p style="TEXT-INDENT: 30px">……</p>
		<p style="TEXT-INDENT: 30px">bytesWritten=write(handle,buffer,length);</p>
		<p style="TEXT-INDENT: 30px">……</p>
		<p style="TEXT-INDENT: 30px">}</p>
		<p style="TEXT-INDENT: 30px">还有许多其它有关文件的操作，移植时只需使用目标平台的API替换以上的Widnows调用，这里就不再逐一列举。</p>
		<p style="TEXT-INDENT: 30px">2.3 用户图形化接口</p>
		<p style="TEXT-INDENT: 30px">用户图形化接口包括画点、画线、作圆、作椭圆、位图拷贝等基本作图函数（可在GRAPHICS.C中找到）；有关定时器、屏幕刷新和键盘触控笔消息等有关与用户交互的操作（可在TEXT.C中找到），它是整个MIDP移植中工作量最大，也是对上层应用的执行效率影响最大的一个部分。这是因为用户在应用中看到的各种图形和文字都是调用底层的图形函数在屏幕上作图的结果。由于屏幕要频繁刷新，图形函数也就成为应用调用最多的接口。因此，移植者必须使每一个底层作图函数与硬件设备紧密配合起来，并使用最高效的算法。</p>
		<p style="TEXT-INDENT: 30px">在不同的平台上，能最大地发挥其作图功能的函数和算法不尽相，这就要求移植者作大量细致的工作，按照MIDP规范的要求逐一重新实现每一个作图函数和屏幕刷新函 数。下面我们就以将画线函数和位图拷贝函数在Windows上的实现为例，简单说明移植要做的工作（键盘、触控笔是以事件消息的方式实现的，它们的移植与事件消息的移植相同）。</p>
		<p style="TEXT-INDENT: 30px">Windows的画线函数接口：</p>
		<p style="TEXT-INDENT: 30px">Void LCDUIdrawLine(int pixel,short*clip,void*dst,int dotted,int x1,int y1,int x2,int y2){</p>
		<p style="TEXT-INDENT: 30px">……</p>
		<p style="TEXT-INDENT: 30px">Polyline(hdc,pts,2);/*绘x1,y1点像素信号*/</p>
		<p style="TEXT-INDENT: 30px">……</p>
		<p style="TEXT-INDENT: 30px">}</p>
		<p style="TEXT-INDENT: 30px">Windwos的屏幕刷新函数：</p>
		<p style="TEXT-INDENT: 30px">Void refreshPaintWindow(int x1,int y1,int x2,int y2){</p>
		<p style="TEXT-INDENT: 30px">RECT r；</p>
		<p style="TEXT-INDENT: 30px">If(x1&lt;x2){</p>
		<p style="TEXT-INDENT: 30px">r.left=x1+x_offset;r.right=x2+x_offset;</p>
		<p style="TEXT-INDENT: 30px">}else{</p>
		<p style="TEXT-INDENT: 30px">r.left=x2+x_offset;r.right=x1+x_offset;</p>
		<p style="TEXT-INDENT: 30px">}</p>
		<p style="TEXT-INDENT: 30px">if(y1&lt;y2){</p>
		<p style="TEXT-INDENT: 30px">r.top=y1+y_offset;r.bottom=y2+y_offset;</p>
		<p style="TEXT-INDENT: 30px">}else{</p>
		<p style="TEXT-INDENT: 30px">r.top=y2+y_offset;r.bottom=y1+y_offset；</p>
		<p style="TEXT-INDENT: 30px">}</p>
		<p style="TEXT-INDENT: 30px">++r.bottom;++r.right;</p>
		<p style="TEXT-INDENT: 30px">InvalidateRect(hMainWindow,&amp;r,KNI_TRUE);</p>
		<p style="TEXT-INDENT: 30px">}</p>
		<p style="TEXT-INDENT: 30px">如果目标平台对这些GUI接口函数有不同实现法，可以用这些方法替换以上的Windows系统调用，这样才能使MIDP图形化用户接口正确地工作，并充分发挥目标平台的工作效率。</p>
		<p style="TEXT-INDENT: 30px">2.4 网络</p>
		<p style="TEXT-INDENT: 30px">MIDP的网络功能是指基于MIDP的J2ME应用可以通过HTTP等网络协议进行下载安装，不同的MIDlet实体也可以通过它交换信息，实现资源共享。遵循HTTP协议的规定，移植者必须利用目标平台的底层网络接口重新实现网络的初始化（networkInit）、建立连接（open0）、断开连接（close0）、接收数据（read0）、获取缓冲区的剩余空间（available0）、关闭发送（shutdownOutput）。如果目标设备具有服务器功能，还要实现serversocket所有上述功能。所有上述接口都在文件socketProtocol_md.c中实现。</p>
		<p style="TEXT-INDENT: 30px">Windwos中获取IP地址的实现：</p>
		<p style="TEXT-INDENT: 30px">Int prim_com_sun_midp_io_j2me_socket_Protocol_getIpNumber</p>
		<p style="TEXT-INDENT: 30px">(char*host)</p>
		<p style="TEXT-INDENT: 30px">{</p>
		<p style="TEXT-INDENT: 30px">……</p>
		<p style="TEXT-INDENT: 30px">hp=gethostbyname(host);</p>
		<p style="TEXT-INDENT: 30px">……</p>
		<p style="TEXT-INDENT: 30px">}</p>
		<p style="TEXT-INDENT: 30px">如果目标平台还需要其它网络协议（datagram、comm），其移植过程与Socket的移植基本相同。</p>
		<p style="TEXT-INDENT: 30px">2.5 应用管理系统（AMS）</p>
		<p style="TEXT-INDENT: 30px">MIDP的应用管理系统(application management system)负责管理当前设备中安装的J2ME应用，其功能包括MIDlet的加载、安装、显示、更新和删除。AMS从main.c中的函数main()开始执行，根据其输入初始化一些系统参数，包括系统路径（classJ2ME MIDP 移植 平台无关 本地代码）、堆空间大小（heapsize）、命令行（command line）等，然后就启动KVM，而KVM就会从AMS的JAVA界面main.java开始解释执行java代码。AMS的所有管理功能都是用JAVA语言实现的，因此AMS的实现是与平台无关的。但不同的平台可能有不同的系统参数和格式，对AMS的界面网络也可能有不同的要求。所以，移植者有可能要修改main.c中解析系统参数的部分，保证AMS能正确解析目标平台的所有参数；同时修改AMS的JAVA层，使其界面网络满足用户的需求。</p>
		<p style="TEXT-INDENT: 30px">2.6 多媒体</p>
		<p style="TEXT-INDENT: 30px">MIDP2.0较MIDP1.0最大的改变就是在MIDP2.0中向应用提供了音频接口（Audio API）的支持。音频接口是移动设备媒体接口MMAPI（Mobile Media API）的一部分。音频的播放过程为5个部分（unrealized、realized、prefetched、started、closed），同时它有自己的音频播放事件的传道和处理过程。MIDP音频播放部分所要做的移植工作就主要集中在声音播放接口，事件的处理方式和兼容不同的音频文件格式上。</p>
		<p style="TEXT-INDENT: 30px">播放接口的移植：不同的目标平台，提供的音频系统API是不同的，有的系统甚至根本没有提供播放音频的API。这时，移植者就要根据目标平台的实现情况替换或自己实现音频的播放接口。</p>
		<p style="TEXT-INDENT: 30px">Windows的音频播放接口（win32/native/MMATONE.C）：</p>
		<p style="TEXT-INDENT: 30px">Java_javax_microedition_media_Manager_nPlayTone(KNITRAPS)</p>
		<p style="TEXT-INDENT: 30px">{</p>
		<p style="TEXT-INDENT: 30px">……</p>
		<p style="TEXT-INDENT: 30px">chn1=getMidiChnl();</p>
		<p style="TEXT-INDENT: 30px">if(chnl==-1){</p>
		<p style="TEXT-INDENT: 30px">KNI_ReturnInt(0)；</p>
		<p style="TEXT-INDENT: 30px">}</p>
		<p style="TEXT-INDENT: 30px">tones[chn].msg=((note&amp;0xff)&lt;&lt;8)|0x00000090|(chnl&amp;0xf);</p>
		<p style="TEXT-INDENT: 30px">msg=((vol&amp;0xff)&lt;&lt;16)|((note&amp;0xff)&lt;&lt;8)|0x90|(chnl &amp;0xf);</p>
		<p style="TEXT-INDENT: 30px">midiOutShortMsg(midiOut,msg);</p>
		<p style="TEXT-INDENT: 30px">timerID=timeSetEvent(dur,TIMERES,(LPTIME CALLBACK)timeToneProc,(DWORD)chnl,TIME_ONESHOT);</p>
		<p style="TEXT-INDENT: 30px">tones[chnl].timerID=timerID;</p>
		<p style="TEXT-INDENT: 30px">……</p>
		<p style="TEXT-INDENT: 30px">}</p>
		<p style="TEXT-INDENT: 30px">事件传递的移植：MIDP中音频播放结束的事件（EOM）是专门通过系统的消息机制传递，而不是通过MIDP的事件传递，因此也需要移植：</p>
		<p style="TEXT-INDENT: 30px">Windows---OM的传递（win32/native/MMAEVT.C）:void injectNativeEvent(int pID,int curMTime){</p>
		<p style="TEXT-INDENT: 30px">PostMessage(hMain Window,WM_APP,(WPARAM)(pID),(LPARAM)(curMTime));</p>
		<p style="TEXT-INDENT: 30px">return;</p>
		<p style="TEXT-INDENT: 30px">}</p>
		<p style="TEXT-INDENT: 0px">
				<b>3 总结</b>
		</p>
		<p style="TEXT-INDENT: 30px">综上所述，MID2.0的移植要以两个方面为出发点：①兼容性。移植后的MIDP实现必须能够在目标平台上正常运行，所有与目标平台不兼容的调用都必须替换为能完成相同功能且兼容的目标平台的系统调用或过程。②高效性。移植后的MIDP实现必须能充分发挥目标平台的效率和特性，用最小的代价完成MIDP的功能。另外，MIDP的移植还要分满足最终用户的个性化要求，为它们设计出丰富多彩的界面网络。 </p>
<img src ="http://www.blogjava.net/faintbear/aggbug/57798.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/faintbear/" target="_blank">小力力力</a> 2006-07-12 14:32 <a href="http://www.blogjava.net/faintbear/archive/2006/07/12/57798.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Linux以及各大发行版介绍</title><link>http://www.blogjava.net/faintbear/archive/2006/02/21/31868.html</link><dc:creator>小力力力</dc:creator><author>小力力力</author><pubDate>Tue, 21 Feb 2006 14:13:00 GMT</pubDate><guid>http://www.blogjava.net/faintbear/archive/2006/02/21/31868.html</guid><wfw:comment>http://www.blogjava.net/faintbear/comments/31868.html</wfw:comment><comments>http://www.blogjava.net/faintbear/archive/2006/02/21/31868.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/faintbear/comments/commentRss/31868.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/faintbear/services/trackbacks/31868.html</trackback:ping><description><![CDATA[<STRONG>什么是Linux？</STRONG> <BR><BR>也许很多人会不屑的说，Linux不就是个操作系统么。错！Linux不是一个操作系统，严格来讲，<STRONG>Linux只是一个操作系统中的内核</STRONG>。内核是什么？内核建立了计算机软件与硬件之间通讯的平台，内核提供系统服务，比如文件管理、虚拟内存、设备I/O等。 <BR><BR>既然Linux只是一个内核。那么我们通常所说的Linux操作系统又是什么？我们通常所说的Linux，指 <STRONG>GNU/Linux</STRONG> ，即采用Linux内核的GNU操作系统。是的，操作系统的实际名称是GNU。什么是GNU？GNU代表<STRONG>G</STRONG>NU’s <STRONG>N</STRONG>ot <STRONG>U</STRONG>nix。可以说是一个操作系统又可以说是一种规范。比如，众所周知的PHP，原名为Personal HomePage（个人主页），根据GNU的软件命名规则，PHP现已更名为<STRONG>P</STRONG>HP: <STRONG>H</STRONG>ypertext <STRONG>P</STRONG>reprocessor（超文本预处理程序）。 <BR><BR><STRONG>谁编写/创造了Linux？</STRONG> <BR><BR>Linux最早由Linus Torvalds在1991年开始编写。在这之前，Richard Stallman创建了Free Software Foundation（FSF）组织以及GNU项目，并不断的编写创建GNU程序（程序的许可方式均为<STRONG>GPL</STRONG>: General Public License）。在不断的有程序员和开发者加入到GNU组织中后，变造就了今天我们所看到的Linux，或称GNU/Linux。 <BR><BR><STRONG>什么是Linux发行版？</STRONG> <BR><BR>正如之前所说的，Linux只是一个内核。然而，一个完整的操作系统不仅仅是内核而已。所以，许多个人、组织和企业，开发了基于GNU/Linux的Linux发行版。这其中最著名的便是Red Hat公司的Red Hat系列以及社区（community）组织的Debian系列。 <BR><BR>下面我就简单得介绍一下目前比较著名、流行的Linux发行版本。部分资料来源：<A href="http://www.distrowatch.com/" target=_new rel=nofollow>DistroWatch.com</A> <BR><BR><STRONG>Mandriva</STRONG> <BR><IMG alt=http://www.noobu.com/img/linux_distro/mandrake.png src="http://www.noobu.com/img/linux_distro/mandrake.png"> <BR><BR>Mandriva原名Mandrake，最早由Gaël Duval创建并在1998年7月发布。记得前两年国内刚开始普及Linux时，Mandrake非常流行。说起Mandrake的历史，其实最早Mandrake的开发者是基于Redhat进行开发的。Redhat默认采用GNOME桌面系统，而Mandrake将之改为KDE。而由于当时的Linux普遍比较难安装，不适合第一次接触Linux的新手，所以Mandrake还简化了安装系统。我想这也是当时Mandrake在国内如此红火的原因之一。Mandrake在易用性方面的确是下了不少功夫，包括默认情况下的硬件检测等。 <BR><BR>Mandrake的开发完全透明化，包括“cooker”。当系统有了新的测试版本后，便可以在cooker上找到。之前Mandrake的新版本的发布速度很快，但从9.0之后便开始减缓。估计是希望能够延长版本的生命力以确保稳定和安全性。 <BR><BR>优点：友好的操作界面，图形配置工具，庞大的社区技术支持，NTFS分区大小变更 <BR>缺点：部分版本bug较多，最新版本只先发布给Mandrake俱乐部的成员 <BR>软件包管理系统：urpmi (RPM) <BR>免费下载：FTP即时发布下载，ISO在版本发布后数星期内提供 <BR>官方主页：<A href="http://www.mandrivalinux.com/" target=_new rel=nofollow>http://www.mandrivalinux.com/</A> <BR><BR><STRONG>Red Hat</STRONG> <BR><IMG alt=http://www.noobu.com/img/linux_distro/fedora.png src="http://www.noobu.com/img/linux_distro/fedora.png"> <BR><BR>国内，乃至是全世界的Linux用户所最熟悉、最耳闻能详的发行版想必就是Red Hat了。Red Hat最早由Bob Young和Marc Ewing在1995年创建。而公司在最近才开始真正步入盈利时代，归功于收费的Red Hat Enterprise Linux（RHEL，Red Hat的企业版）。而正统的Red Hat版本早已停止技术支持，最后一版是Red Hat 9.0。于是，目前Red Hat分为两个系列：由Red Hat公司提供收费技术支持和更新的Red Hat Enterprise Linux，以及由社区开发的免费的Fedora Core。Fedora Core 1发布于2003年年末，而FC的定位便是桌面用户。FC提供了最新的软件包，同时，它的版本更新周期也非常短，仅六个月。目前最新版本为FC 3，而FC4也预定将于今年6月发布。这也是为什么服务器上一般不推荐采用Fedora Core。 <BR><BR>适用于服务器的版本是Red Hat Enterprise Linux，而由于这是个收费的操作系统。于是，国内外许多企业或空间商选择<A href="http://www.centos.org/" target=_new rel=nofollow>CentOS</A>。CentOS可以算是RHEL的克隆版，但它最大的好处是免费！菜鸟油目前的服务器便采用的CentOS 3.4。 <BR><BR>优点：拥有数量庞大的用户，优秀的社区技术支持，许多创新 <BR>缺点：免费版（Fedora Core）版本生命周期太短，多媒体支持不佳 <BR>软件包管理系统：up2date (RPM), YUM (RPM) <BR>免费下载：是 <BR>官方主页：<A href="http://www.redhat.com/" target=_new rel=nofollow>http://www.redhat.com/</A> <BR><BR><STRONG>SUSE</STRONG> <BR><IMG alt=http://www.noobu.com/img/linux_distro/suse.png src="http://www.noobu.com/img/linux_distro/suse.png"> <BR><BR>SUSE是德国最著名的Linux发行版，在全世界范围中也享有较高的声誉。SUSE自主开发的软件包管理系统YaST也大受好评。SUSE于2003年年末被Novell收购。 <BR><BR>SUSE之后的发布显得比较混乱，比如9.0版本是收费的，而10.0版本（也许由于各种压力）又免费发布。这使得一部分用户感到困惑，也转而使用其它发行版本。但是，瑕不掩瑜，SUSE仍然是一个非常专业、优秀的发行版。 <BR><BR>优点：专业，易用的YaST软件包管理系统 <BR>缺点：FTP发布通常要比零售版晚1~3个月 <BR>软件包管理系统：YaST (RPM), 第三方APT (RPM) 软件库（repository） <BR>免费下载：取决于版本 <BR>官方主页：<A href="http://www.suse.com/" target=_new rel=nofollow>http://www.suse.com/</A> <BR><BR><STRONG>Debian GNU/Linux</STRONG> <BR><IMG alt=http://www.noobu.com/img/linux_distro/debian.png src="http://www.noobu.com/img/linux_distro/debian.png"> <BR><BR>Debian是菜鸟油服务器之前所采用的操作系统。Debian最早由Ian Murdock于1993年创建。可以算是迄今为止，最遵循GNU规范的Linux系统。Debian系统分为三个版本分支（branch）：stable, testing 和 unstable。截至2005年5月，这三个版本分支分别对应的具体版本为：Woody, Sarge 和 Sid。其中，unstable为最新的测试版本，其中包括最新的软件包，但是也有相对较多的bug，适合桌面用户。testing的版本都经过unstable中的测试，相对较为稳定，也支持了不少新技术（比如SMP等）。而Woody一般只用于服务器，上面的软件包大部分都比较过时，但是稳定和安全性都非常的高。菜鸟油之前所采用的是Debian Sarge。 <BR><BR>为何有如此多的用户痴迷于Debian呢（包括笔者在内）？apt-get / dpkg是原因之一。dpkg是Debian系列特有的软件包管理工具，它被誉为所有Linux软件包管理工具（比如RPM）最强大的！配合apt-get，在Debian上安装、升级、删除和管理软件变得异常容易。许多Debian的用户都开玩笑的说，Debian将他们养懒了，因为只要简单得敲一下”apt-get upgrade &amp;&amp; apt-get update”，机器上所有的软件就会自动更新了…… <BR><BR>优点：遵循GNU规范，100%免费，优秀的网络和社区资源，强大的apt-get <BR>缺点：安装相对不易，stable分支的软件极度过时 <BR>软件包管理系统：APT (DEB) <BR>免费下载：是 <BR>官方主页：<A href="http://www.debian.org/" target=_new rel=nofollow>http://www.debian.org/</A> <BR><BR><STRONG>Ubuntu</STRONG> <BR><IMG alt=http://www.noobu.com/img/linux_distro/ubuntu.png src="http://www.noobu.com/img/linux_distro/ubuntu.png"> <BR><BR>笔者的桌面电脑便使用的Ubuntu。依照笔者的理解，简单而言，Ubuntu就是一个拥有Debian所有的优点，以及自己所加强的优点的近乎完美的Linux操作系统。:) Ubuntu是一个相对较新的发行版，但是，它的出现可能改变了许多潜在用户对Linux的看法。也许，从前人们会认为Linux难以安装、难以使用，但是，Ubuntu出现后，这些都成为了历史。Ubuntu基于Debian Sid，所以这也就是笔者所说的，Ubuntu拥有Debian的所有优点，包括apt-get。然而，不仅如此而已，Ubuntu默认采用的GNOME桌面系统也将Ubuntu的界面装饰的简易而不失华丽。当然，如果你是一个KDE的拥护者的话，Kubuntu同样适合你！ <BR><BR>Ubuntu的安装非常的人性化，只要按照提示一步一步进行，安装和Windows同样简便！并且，Ubuntu被誉为对硬件支持最好最全面的Linux发行版之一，许多在其他发行版上无法使用，或者默认配置时无法使用的硬件，在Ubuntu上轻松搞定。并且，Ubuntu采用自行加强的内核（kernel），安全性方面更上一层楼。并且，Ubuntu默认不能直接root登陆，必须从第一个创建的用户通过su或sudo来获取root权限（这也许不太方便，但无疑增加了安全性，避免用户由于粗心而损坏系统）。Ubuntu的版本周期为六个月，弥补了Debian更新缓慢的不足。 <BR><BR>优点：人气颇高的论坛提供优秀的资源和技术支持，固定的版本更新周期和技术支持，可从Debian Woody直接升级 <BR>缺点：还未建立成熟的商业模式 <BR>软件包管理系统：APT (DEB) <BR>免费下载：是 <BR>官方主页：<A href="http://www.ubuntulinux.org/" target=_new rel=nofollow>http://www.ubuntulinux.org/</A> <BR><BR><STRONG>Gentoo</STRONG> <BR><IMG alt=http://www.noobu.com/img/linux_distro/gentoo.png src="http://www.noobu.com/img/linux_distro/gentoo.png"> <BR><BR>Gentoo最初由Daniel Robbins（前Stampede Linux和FreeBSD的开发者之一）创建。由于开发者对FreeBSD的熟识，所以Gentoo拥有媲美FreeBSD的广受美誉的ports系统——portage。（Ports和Portage都是用于在线更新软件的系统，类似apt-get，但还是有很大不同）Gentoo的首个稳定版本发布于2002年。 <BR><BR>Gentoo的出名是因为其高度的自定制性：因为它是一个基于源代码的（source-based）发行版。尽管安装时可以选择预先编译好的软件包，但是大部分使用Gentoo的用户都选择自己手动编译。这也是为什么Gentoo适合比较有Linux使用经验的老手使用的原因。但是要注意的是，由于编译软件需要消耗大量的时间，所以如果你所有的软件都自己编译，并安装KDE桌面系统等比较大的软件包，可能需要几天时间才能编译完…… <BR><BR>优点：高度的可定制性，完整的使用手册，媲美Ports的Portage系统，适合“臭美”的高手使用^^ <BR>缺点：编译耗时多，安装缓慢 <BR>软件包管理系统：Portage (SRC) <BR>免费下载：是 <BR>官方主页：<A href="http://www.gentoo.org/" target=_new rel=nofollow>http://www.gentoo.org/</A> <BR><BR><STRONG>Slackware</STRONG> <BR><IMG alt=http://www.noobu.com/img/linux_distro/slackware.png src="http://www.noobu.com/img/linux_distro/slackware.png"> <BR><BR>Slackware由Patrick Volkerding创建于1992年。算起来应当是历史最悠久的Linux发行版。曾经Slackware非常的流行，但是当Linux越来越普及，用户的技术层面越来越广（更多的新手）后，Slackware渐渐的被新来的人们所遗忘。在其他主流发行版强调易用性的时候，Slackware依然固执的追求最原始的效率——所有的配置均还是要通过配置文件来进行。 <BR><BR>尽管如此，Slackware仍然深入人心（大部分都是比较有经验的Linux老手）。Slackware稳定、安全，所以仍然有大批的忠实用户。由于Slackware尽量采用原版的软件包而不进行任何修改，所以制造新bug的几率便低了很多。Slackware的版本更新周期较长（大约1年），但是新版本的软件仍然不间断的提供给用户下载。 <BR><BR>优点：非常稳定、安全，高度坚持UNIX的规范 <BR>缺点：所有的配置均通过编辑文件来进行，自动硬件检测能力较差 <BR>软件包管理系统：Slackware Package Management (TGZ) <BR>免费下载：是 <BR>官方主页：<A href="http://www.slackware.com/" target=_new rel=nofollow>http://www.slackware.com/</A> <BR><BR><STRONG>Knoppix</STRONG> <BR><IMG alt=http://www.noobu.com/img/linux_distro/knoppix.png src="http://www.noobu.com/img/linux_distro/knoppix.png"> <BR><BR>由德国的Klaus Knopper开发的Knoppix，是一个基于Debian的发行版。Knoppix严格算起来是一款LiveCD Linux，所谓的LiveCD就是整个操作系统都在一张光盘上，只要开机从光盘启动，就能拥有一个完整的Linux系统！无需安装！当然，Knoppix也能够非常轻松的安装到硬盘上。其强大的硬件检测能力、系统修复能力、即时压缩传输技术，都令人大加称赞。可以说，在LiveCD界，Knoppix是无人能及的！ <BR><BR>优点：无需安装可直接运行于CD上，优秀的硬件检测能力，可作为系统急救盘使用 <BR>缺点：LiveCD由于光盘的数据读取速度限制导致性能大幅下降 <BR>软件包管理系统：APT (DEB) <BR>免费下载：是 <BR>官方主页：<A href="http://www.knoppix.com/" target=_new rel=nofollow>http://www.knoppix.com/</A> <BR><BR><STRONG>MEPIS</STRONG> <BR><IMG alt=http://www.noobu.com/img/linux_distro/mepis.png src="http://www.noobu.com/img/linux_distro/mepis.png"> <BR><BR>MEPIS由Warren Woodford在2003年建立。MEPIS虽然刚建立不久，但是迅速的传播在Linux用户间。简单来说，MEPIS是一个集合了Debian Sid和Knoppix的产物。用户即能将之当作LiveCD使用，也能使用常规的图形界面进行安装。 <BR><BR>MEPIS默认集成安装了Java Runtime Environment、Flash插件、nVidia加速驱动等许多常用的程序。用户可以非常轻松的安装完系统后就直接开始使用，而不用到处寻找资料如何下载、如何安装、如何配置这些软件。这不仅给Linux新手带来了便捷，也给老手们节约了相当多的时间。 <BR><BR>优点：LiveCD与常规安装两用，优秀的硬件检测能力，预装了许多实用的软件 <BR>缺点：建立时间不长，默认的界面有些寒酸 <BR>软件包管理系统：APT (DEB) <BR>免费下载：是 <BR>官方主页：<A href="http://www.mepis.org/" target=_new rel=nofollow>http://www.mepis.org/</A> <BR><BR><STRONG>Xandros</STRONG> <BR><IMG alt=http://www.noobu.com/img/linux_distro/xandros.png src="http://www.noobu.com/img/linux_distro/xandros.png"> <BR><BR>Xandros建立在已经成为历史的Corel Linux之上。当初Corel Linux的公司由于财政上的困难，被迫终止了Corel Linux的开发，而Xandros适时的将Corel Linux部门买下，于2002年10月推出全新的Xandros Desktop。 <BR><BR>Xandros的卖点在于极其简单的安装和使用，所以它的市场定位是那些没有任何Linux使用经验的新手，或是习惯使用Windows的用户。Xandros的标准版和增强版都是商业软件，分别售价$40和$99美元。不过你仍然可以在<A href="http://www.xandros.com/products/home/desktopoc/dsk_oc_download.html" target=_new rel=nofollow>这里</A>下载到免费的公开发行版。 <BR><BR>优点：适合完全没有经验的新手，安装完以后就能立即投入使用，自带非常不错的工具 <BR>缺点：商业软件 <BR>软件包管理系统：Xandros Networks (DEB) 或 APT (DEB) （可选，但不提供技术支持） <BR>免费下载：<A href="http://www.xandros.com/products/home/desktopoc/dsk_oc_download.html" target=_new rel=nofollow>公开发行版</A> <BR>官方主页：<A href="http://www.xandros.com/" target=_new rel=nofollow>http://www.xandros.com/</A> <BR><BR><STRONG>FreeBSD</STRONG> <BR><IMG alt=http://www.noobu.com/img/linux_distro/freebsd.png src="http://www.noobu.com/img/linux_distro/freebsd.png"> <BR><BR>首先要强调的是：<STRONG>FreeBSD不是一个Linux系统！</STRONG> 可是，为什么笔者要介绍FreeBSD呢？因为FreeBSD的用户也相当多，其许多特性都与Linux相类似。事实上，Linux和BSD（Berkeley Software Distribution）均是UNIX的演化分支。并且，Linux中相当多的特性和功能（比如用于配置DNS的Bind软件）都是取自于BSD的。而FreeBSD便是BSD家族中最出名，用户数量最多的一个发行版。MEZOC之前所采用的便是FreeBSD系统。 <BR><BR>FreeBSD建立于1993年，拥有相当长的历史。FreeBSD拥有两个分支：stable和current。顾名思义，stable是稳定版，而current则是添加了新技术的测试版。另外，FreeBSD会不定期的发布新的版本，称为RELEASE，stable和current均有自己的RELEASE版本。比如4.11-RELEASE和5.3-RELEASE，请注意，这并不代表后者比前者的版本新。这仅仅代表前者（数字小的版本）是stable版本，后者（数字大的版本）是current版本。 <BR><BR>FreeBSD除了作为服务器系统外，也适合桌面用户。不过，考虑到软件方面的兼容性，一般用户选择FreeBSD作为桌面系统不是很明智。作为服务器而言，FreeBSD是相当优秀的。曾经有人说过，同样的服务器硬件配置，运行同样的一个vBulletin论坛，FreeBSD所用的资源要比Linux少。这也是为什么许多空间商极力推崇FreeBSD的原因。:) <BR><BR>优点：速度快，非常稳定，优秀的使用手册，Ports系统 <BR>缺点：比起Linux而言对硬件的支持较差，对于桌面系统而言软件的兼容性是个问题 <BR>软件包管理系统：Ports (TBZ) <BR>免费下载：是 <BR>官方主页：<A href="http://www.freebsd.org/" target=_new rel=nofollow>http://www.freebsd.org/</A> <BR><BR>以上介绍了目前较为流行的各Linux发行版本，希望对大家有所帮助。同时，笔者也希望越来越多的朋友投入到Linux的大家庭中！\^O^/ <img src ="http://www.blogjava.net/faintbear/aggbug/31868.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/faintbear/" target="_blank">小力力力</a> 2006-02-21 22:13 <a href="http://www.blogjava.net/faintbear/archive/2006/02/21/31868.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>RISC和CISC </title><link>http://www.blogjava.net/faintbear/archive/2006/02/20/31698.html</link><dc:creator>小力力力</dc:creator><author>小力力力</author><pubDate>Mon, 20 Feb 2006 12:46:00 GMT</pubDate><guid>http://www.blogjava.net/faintbear/archive/2006/02/20/31698.html</guid><wfw:comment>http://www.blogjava.net/faintbear/comments/31698.html</wfw:comment><comments>http://www.blogjava.net/faintbear/archive/2006/02/20/31698.html#Feedback</comments><slash:comments>3</slash:comments><wfw:commentRss>http://www.blogjava.net/faintbear/comments/commentRss/31698.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/faintbear/services/trackbacks/31698.html</trackback:ping><description><![CDATA[<font size="2">RISC和CISC&nbsp;
<br><br>　　CPU从指令集的特点上可以分为两类：CISC和RISC。我们所熟悉的&nbsp;Intel&nbsp;系列CPU就是&nbsp;CISC&nbsp;的&nbsp;CPU&nbsp;的典型代表。
那么，RISC&nbsp;又是什么呢？RISC是英文Reduced&nbsp;Instruction&nbsp;Set&nbsp;Computer的缩写，汉语意思为"精简指令系统计算机
"。相对应的CISC就是"复杂指令系统计算机"的意思。&nbsp;
<br><br>　　随着大规模集成电路技术的发展，计算机的硬件成本不断下降，软件成本不断提高，使得指令系统增加了更多更复杂的指令，以提高操作系统的效率。
另外，同一系列的新型机对其指令系统只能扩充而不能减去旧型机的任意一条，以达到程序兼容。这样一来，指令系统越来越复杂，有的计算机指令甚至达到数百
条。人们就称这种计算机为CISC（Complex&nbsp;Instruction&nbsp;Set&nbsp;Computer）。如IBM公司的大、中型计算机，Intel公
司的8086、80286、80386微处理器等。&nbsp;
<br><br>　　日益庞大的指令系统不仅使计算机研制周期变长，而且还有难以调试、难以维护等一些自身无法克服的缺点。&nbsp;
<br><br>后来人们发现机器执行的指令中85％左右的都是简单指令，复杂指令甚少，因此开始研制精简指令系统计算机(RISC)。于是RISC技术在高端服
务器和工作站上更是得到了广泛的应用。Intel的Pentium问世以来(92年末)，融合了RISC技术，也逐步渗透到了中小工作站和服务器市场。&nbsp;
<br>　　&nbsp;
<br>这种种因素使计算机指令产生了“简单指令”和“复杂指令”之分。70年代以前的计算机均用传统的CISC指令结构，即完全采用复杂指令来支持高级语言、应用程序和操作系统。这种PC不但成本高且效率较低，速度受限。&nbsp;
<br><br><br><br>　　目前，RISC和CISC各有优势，而且界限并不那么明显了。现代的CPU往往采用CISC的外围，内部加入了RISC的特性。就连
Intel最新的Pentium&nbsp;II等CISC芯片也具有了明显的RISC特征。另外，超长指令集CPU由于融合了RISC和CISC的优势，成为未来
的CPU发展方向之一。</font>






<img src ="http://www.blogjava.net/faintbear/aggbug/31698.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/faintbear/" target="_blank">小力力力</a> 2006-02-20 20:46 <a href="http://www.blogjava.net/faintbear/archive/2006/02/20/31698.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>