<?xml version="1.0" encoding="GBK"?>
<?xml-stylesheet href="/style/rss.css" type="text/css"?>
<rss version="2.0" xmlns:eb="http://blog.tom.com/">
<channel>
  <title>reflex</title>
  <link>http://blog.tom.com/tigerkings941220</link>
  <description><![CDATA[Expressingyourselfhonestly..... ]]></description>
  <language>zh</language>
  <generator>newblog.tom.com RSS</generator>
  <pubDate></pubDate>    <item>
		<title><![CDATA[ HTTP协议的运作方式 ]]></title>
		<link>http://blog.tom.com/tigerkings941220/article/9392.html</link>
		<description><![CDATA[ <p>出处:<font color="#0E3E92">中国协议分析网</font> 责编:<font color="#0E3E92">chinaitpower</font></p>
<p>&nbsp;</p>
<p>HTTP<a href="http://www.chinaitpower.com/Net/xieyi/index.html" target="_blank">协议</a>是基于请求／响应范式的。一个客户机与服务器建立连接后，发送一个请求给服务器，请求方式的格式为，统一资源标识符、<a href="http://www.chinaitpower.com/Net/xieyi/index.html" target="_blank">协议</a>版本号，后边是MIME信息包括请求修饰符、客户机信息和可能的内容。服务器接到请求后，给予相应的响应信息，其格式为一个状态行包括信息的<a href="http://www.chinaitpower.com/Net/xieyi/index.html" target="_blank">协议</a>版本号、一个成功或错误的代码，后边是MIME信息包括服务器信息、实体信息和可能的内容。<br>
　　许多HTTP通讯是由一个用户代理初始化的并且包括一个申请在源服务器上资源的请求。最简单的情况可能是在用户代理(UA)和源服务器(O)之间通过一个单独的连接来完成(见图2-1)。<br>
图2-1</p>
<p align="center"><img src="http://www.chinaitpower.com/A-A-B/Image/2005/08/02/2214177174.gif" border="0"></p>
<p><br>
　　当一个或多个中介出现在请求／响应链中时，情况就变得复杂一些。中介由三种：代理(Proxy)、网关(Gateway)和通道(Tunnel)。一个代理根据URI的绝对格式来接受请求，重写全部或部分消息，通过URI的标识把已格式化过的请求发送到服务器。网关是一个接收代理，作为一些其它服务器的上层，并且如果必须的话，可以把请求翻译给下层的服务器<a href="http://www.chinaitpower.com/Net/xieyi/index.html" target="_blank">协议</a>。一个通道作为不改变消息的两个连接之间的中继点。当通讯需要通过一个中介(例如：防火墙等)或者是中介不能识别消息的内容时，通道经常被使用。图2-2&gt;</p>
<p align="center"><img src="http://www.chinaitpower.com/A-A-B/Image/2005/08/02/2141735975.gif" border="0"></p>
<p><br>
　　上面的图2-2表明了在用户代理(UA)和源服务器(O)之间有三个中介(A,B和C)。一个通过整个链的请求或响应消息必须经过四个连接段。这个区别是重要的，因为一些HTTP通讯选择可能应用于最近的连接、没有通道的邻居，应用于链的终点或应用于沿链的所有连接。尽管图2-2是线性的，每个参与者都可能从事多重的、并发的通讯。例如，B可能从许多客户机接收请求而不通过A，并且／或者不通过C把请求送到A，在同时它还可能处理A的请求。<br>
　　任何针对不作为通道的汇聚可能为处理请求启用一个内部缓存。缓存的效果是请求／响应链被缩短，条件是沿链的参与者之一具有一个缓存的响应作用于那个请求。下图说明结果链，其条件是针对一个未被UA或A加缓存的请求，B有一个经过C来自O的一个前期响应的缓存拷贝。<br>
图2-3</p>
<p align="center"><img src="http://www.chinaitpower.com/A-A-B/Image/2005/08/02/2141754971.gif" border="0"></p>
<p><br>
　　在Internet上，HTTP通讯通常发生在TCP/IP连接之上。缺省端口是TCP80，但其它的端口也是可用的。但这并不预示着HTTP<a href="http://www.chinaitpower.com/Net/xieyi/index.html" target="_blank">协议</a>在Internet或其它网络的其它<a href="http://www.chinaitpower.com/Net/xieyi/index.html" target="_blank">协议</a>之上才能完成。HTTP只预示着一个可靠的传输。<br>
　　以上简要介绍了HTTP<a href="http://www.chinaitpower.com/Net/xieyi/index.html" target="_blank">协议</a>的宏观运作方式，下面介绍一下HTTP<a href="http://www.chinaitpower.com/Net/xieyi/index.html" target="_blank">协议</a>的内部操作过程。<br>
　　首先，简单介绍基于HTTP<a href="http://www.chinaitpower.com/Net/xieyi/index.html" target="_blank">协议</a>的客户/服务器模式的信息交换过程，如图2-4所示，它分四个过程，建立连接、发送请求信息、发送响应信息、关闭连接。<br>
图2-4</p>
<p align="center"><img src="http://www.chinaitpower.com/A-A-B/Image/2005/08/02/2214189231.gif" border="0"></p>
<p><br>
　　在WWW中，“客户”与“服务器”是一个相对的概念，只存在于一个特定的连接期间，即在某个连接中的客户在另一个连接中可能作为服务器。WWW服务器运行时，一直在TCP80端口(WWW的缺省端口)监听，等待连接的出现。<br>
　　下面，讨论HTTP<a href="http://www.chinaitpower.com/Net/xieyi/index.html" target="_blank">协议</a>下客户/服务器模式中信息交换的实现。　　1.建立连接　　连接的建立是通过申请套接字(Socket)实现的。客户打开一个套接字并把它约束在一个端口上，如果成功，就相当于建立了一个虚拟文件。以后就可以在该虚拟文件上写数据并通过网络向外传送。<br>
　　2.发送请求<br>
　　打开一个连接后，客户机把请求消息送到服务器的停留端口上，完成提出请求动作。<br>
　　HTTP/1.0　　请求消息的格式为：<br>
　　请求消息=请求行(通用信息|请求头|实体头)CRLF[实体内容]<br>
　　请求　行=方法　请求URL　HTTP版本号　CRLF<br>
　　方　　法=GET|HEAD|POST|扩展方法<br>
　　U　R　L=<a href="http://www.chinaitpower.com/Net/xieyi/index.html" target="_blank">协议</a>名称+宿主名+目录与文件名<br>
　　请求行中的方法描述指定资源中应该执行的动作，常用的方法有GET、HEAD和POST。不同的请求对象对应GET的结果是不同的，对应关系如下：<br>
　　对象　　　　　　GET的结果<br>
　　文件　　　　　　文件的内容<br>
　　程序　　　　　　该程序的执行结果<br>
　　数据库查询　　　查询结果<br>
　　HEAD——要求服务器查找某对象的元信息，而不是对象本身。<br>
　　POST——从客户机向服务器传送数据，在要求服务器和CGI做进一步处理时会用到POST方法。POST主要用于发送HTML文本中FORM的内容，让CGI程序处理。<br>
　　一个请求的例子为：<br>
　　GEThttp://networking.zju.edu.cn/zju/index.htmHTTP/1.0<br>
　　头信息又称为元信息，即信息的信息，利用元信息可以实现有条件的请求或应答。<br>
　　请求头——告诉服务器怎样解释本次请求，主要包括用户可以接受的数据类型、压缩方法和语言等。<br>
　　实体头——实体信息类型、长度、压缩方法、最后一次修改时间、数据有效期等。<br>
　　实体——请求或应答对象本身。<br>
　　3.发送响应<br>
　　服务器在处理完客户的请求之后，要向客户机发送响应消息。<br>
　　HTTP/1.0的响应消息格式如下：<br>
　　响应消息=状态行(通用信息头|响应头|实体头)　CRLF　〔实体内容〕<br>
　　状态行=HTTP版本号　状态码　原因叙述<br>
　　状态码表示响应类型<br>
　　1××　　保留<br>
　　2××　　表示请求成功地接收<br>
　　3××　　为完成请求客户需进一步细化请求<br>
　　4××　　客户错误<br>
　　5××　　服务器错误<br>
　　响应头的信息包括：服务程序名，通知客户请求的URL需要认证，请求的资源何时能使用。<br>
　　4.关闭连接<br>
　　客户和服务器双方都可以通过关闭套接字来结束TCP/IP对话<br></p>
<p align="center"><img src="http://www.chinaitpower.com/A-A-B/Image/2005/08/02/2214177174.gif" border="0"></p>
<p align="center"><img src="http://www.chinaitpower.com/A-A-B/Image/2005/08/02/2141735975.gif" border="0"></p>
<p align="center"><img src="http://www.chinaitpower.com/A-A-B/Image/2005/08/02/2141754971.gif" border="0"></p>
<p align="center"><img src="http://www.chinaitpower.com/A-A-B/Image/2005/08/02/2214189231.gif" border="0"></p> ]]></description>
		<eb:creationDate>2008-04-12 15:04:10</eb:creationDate>
		<eb:modificationDate></eb:modificationDate>
    </item>
    <item>
		<title><![CDATA[ HTTP是怎样工作的 ]]></title>
		<link>http://blog.tom.com/tigerkings941220/article/9391.html</link>
		<description><![CDATA[ 由于HTTP<a href="http://www.chinaitpower.com/Net/xieyi/index.html" target="_blank">协议</a>是基于请求/响应范式的(相当于客户机/服务器)。一个客户机与服务器建立连接后，发送一个请求给服务器，请求方式的格式为：统一资源标识符(URL)、<a href="http://www.chinaitpower.com/Net/xieyi/index.html" target="_blank">协议</a>版本号，后边是MIME信息包括请求修饰符、客户机信息和可能的内容。服务器接到请求后，给予相应的响应信息，其格式为一个状态行，包括信息的<a href="http://www.chinaitpower.com/Net/xieyi/index.html" target="_blank">协议</a>版本号、一个成功或错误的代码，后边是MIME信息包括服务器信息、实体信息和可能的内容。<br>
<br>
　　许多HTTP通讯是由一个用户代理初始化的并且包括一个申请在源服务器上资源的请求。最简单的情况可能是在用户代理和服务器之间通过一个单独的连接来完成。在Internet上，HTTP通讯通常发生在TCP/IP连接之上。缺省端口是TCP80，但其它的端口也是可用的。但这并不预示着HTTP<a href="http://www.chinaitpower.com/Net/xieyi/index.html" target="_blank">协议</a>在Internet或其它网络的其它<a href="http://www.chinaitpower.com/Net/xieyi/index.html" target="_blank">协议</a>之上才能完成。HTTP只预示着一个可靠的传输。<br>
<br>
　　这个过程就好像我们打电话订货一样，我们可以打电话给商家，告诉他我们需要什么规格的商品，然后商家再告诉我们什么商品有货，什么商品缺货。这些，我们是通过电话线用电话联系(HTTP是通过TCP/IP)，当然我们也可以通过传真，只要商家那边也有传真。<br>
<br>
　　以上简要介绍了HTTP<a href="http://www.chinaitpower.com/Net/xieyi/index.html" target="_blank">协议</a>的宏观运作方式，下面介绍一下HTTP<a href="http://www.chinaitpower.com/Net/xieyi/index.html" target="_blank">协议</a>的内部操作过程。<br>
<br>
　　在WWW中，“客户”与“服务器”是一个相对的概念，只存在于一个特定的连接期间，即在某个连接中的客户在另一个连接中可能作为服务器。基于HTTP<a href="http://www.chinaitpower.com/Net/xieyi/index.html" target="_blank">协议</a>的客户/服务器模式的信息交换过程，它分四个过程：建立连接、发送请求信息、发送响应信息、关闭连接。这就好像上面的例子，我们电话订货的全过程。<br>
<br>
　　其实简单说就是任何服务器除了包括HTML文件以外，还有一个HTTP驻留程序，用于响应用户请求。你的浏览器是HTTP客户，向服务器发送请求，当浏览器中输入了一个开始文件或点击了一个超级链接时，浏览器就向服务器发送了HTTP请求，此请求被送往由IP地址指定的URL。驻留程序接收到请求，在进行必要的操作后回送所要求的文件。在这一过程中，在网络上发送和接收的数据已经被分成一个或多个数据包(packet)，每个数据包包括：要传送的数据；控制信息，即告诉网络怎样处理数据包。TCP/IP决定了每个数据包的格式。如果事先不告诉你，你可能不会知道信息被分成用于传输和再重新组合起来的许多小块。<br>
<br>
　　也就是说商家除了拥有商品之外，它也有一个职员在接听你的电话，当你打电话的时候，你的声音转换成各种复杂的数据，通过电话线传输到对方的电话机，对方的电话机又把各种复杂的数据转换成声音，使得对方商家的职员能够明白你的请求。这个过程你不需要明白声音是怎么转换成复杂的数据的。<br> ]]></description>
		<eb:creationDate>2008-04-12 15:02:43</eb:creationDate>
		<eb:modificationDate></eb:modificationDate>
    </item>
    <item>
		<title><![CDATA[ 协议分析的优势—HTTP分析器检测网络攻击 ]]></title>
		<link>http://blog.tom.com/tigerkings941220/article/9390.html</link>
		<description><![CDATA[ <p>从性能、效率、检测率、误报率等各方面看，使用<a href="http://www.chinaitpower.com/Net/xieyi/index.html" target="_blank">协议</a>分析的入侵检测系统比起使用简单模式匹配的入侵检测系统有着较大的优势。下面我们就以HTTP<a href="http://www.chinaitpower.com/Net/xieyi/index.html" target="_blank">协议</a>为例，结合KIDS（金诺网安入侵检测系统）中使用的HTTP分析器，对这两种方法进行比较说明。<br>
<br>
GET/scripts/..%c1%1c../winnt/system32/cmd.exe?/c+dirHTTP/1.0<br>
<br>
一个针对IIS的Unicode攻击的第一步一般是通过浏览器送出类似下面这样一个HTTP请求：<br>
<br>
TCPdport:80;content:“%c1%1c”/i;alert:“IISUnicodeDirectoryTraversal”<br>
<br>
TCPdport:80;content:“cmd.exe”/i;alert:“Attempttoexecutecmd”<br>
<br>
使用模式匹配的入侵检测系统会使用类似于下面的规则进行检测：<br>
<br>
第一条规则表示，如果检测到一个TCP包发向80端口，并且其中含有字符串“%c1%1c”，系统就发出报警“IISUnicodeTraversal”；第二条规则表示，如果检测到一个TCP包发向80端口，并且其中含有字符串“cmd.exe”（忽略大小写），系统就发出报警“Attempttoexecutecmd”。<br>
<br>
抛开实现上的优化等问题，这样一个系统有着下面两个严重缺陷：<br>
<br>
●误报　这个方法不考虑TCP连接是否已建立，也不考虑匹配字串会不会可能是合法数据的一部分。特别是后一情况尤为严重。拿换码序列“%c1%1c”来说，它完全可以是Cookie或GET/POST数据中的合法成员。<br>
<br>
●漏报　这一检测方法要求匹配字串出现在同一数据包中，攻击者完全可以使用多个数据包来实施这一攻击。使用Telnet目标主机80，然后直接在命令行上输入上面的HTTP请求并加上两个回车，就可以发出攻击，而这样的攻击可能使用了多至64个数据包。攻击者也可以对“cmd.exe”进行换码处理——如使用“%63md.exe”，上面的第二条检测规则就完全没用了。<br>
<br>
KIDS中的HTTP分析器恰恰是针对这两个缺陷设计的。它具有以下特点：<br>
<br>
●使用插件方式运行时动态载入　如果不需要对HTTP进行监测，可以不加载HTTP分析器以节省网络传感器的内存开销。<br>
<br>
●KIDS引擎的TCP流重组能力：可对分散在多个数据包中的HTTP请求进行分析处理。<br>
<br>
●完整获取整个HTTP请求：可对请求超长（可能为缓冲区溢出攻击）进行判断，即使一个HTTP请求跨越了多个TCP包。<br>
<br>
●完整分析HTTP0.9、HTTP1.0和HTTP1.1<a href="http://www.chinaitpower.com/Net/xieyi/index.html" target="_blank">协议</a>：可对一个HTTP连接中的多个HTTP请求分别进行分析处理。<br>
<br>
●可对向代理服务器发出的HTTP请求进行分析处理。<br>
<br>
●把HTTP请求分解为方法、主机、路径、查询字串等部分分别进行分析处理。对路径部分会进行解码处理，并对解码前后的路径分别进行检验。<br>
<br>
HTTP分析器里的规则是以XML的方式分层进行组织。我们主要关心的HTTP方法是“GET”、“HEAD”和“POST”，所以我们在Method中对此进行了规定；这意味着，我们只对这三种类型之一的完整HTTP请求进行分析处理。HTTP及其代理的常用端口80、3128和8080在network部分用port标签进行了规定。rules部分中的host可规定禁止访问的网站（以域名形式）。path部分规定了如何对解码前的路径进行检验，而path_decoded部分规定了如何对解码后的路径进行检验。对于包含“%63md.exe”的路径，HTTP分析器解码后会先得到“cmd.exe”，然后很容易就能在规则中匹配到，并产生编号为1056的事件。HTTP分析器会把事件号和相关信息以统一的格式递交给响应模块做下一步处理。<br>
<br>
综上所述，KIDS中的HTTP分析器以独立的检测器模块方式工作，对HTTP请求进行分析处理，能够更可靠、更有效地对通过HTTP<a href="http://www.chinaitpower.com/Net/xieyi/index.html" target="_blank">协议</a>发起的攻击进行检测。显然，以模块化的方式对高层<a href="http://www.chinaitpower.com/Net/xieyi/index.html" target="_blank">协议</a>进行分析处理，将是未来入侵检测的方向。<br></p> ]]></description>
		<eb:creationDate>2008-04-12 15:01:16</eb:creationDate>
		<eb:modificationDate></eb:modificationDate>
    </item>
    <item>
		<title><![CDATA[ HTTP和WWW的配置注意事项 ]]></title>
		<link>http://blog.tom.com/tigerkings941220/article/9389.html</link>
		<description><![CDATA[ <p>&nbsp;出处:<font color="#0E3E92">中国协议分析网</font> 责编:<font color="#0E3E92">chinaitpower</font></p>
<p>&nbsp;</p>
<p>一、HTTP的安全因素<br>
对于HTPP要关注的两个基本安全情况之一是，一个恶意的客户能对HTTP服务器做些什么。在大多数情况<br>
下，我们对HTTP服务器安全性的考虑同我们对其他服务器如匿名FTP服务器处理来自因特网的连接的安全性考虑<br>
是一样的。你要确保用户的这些连接只能访问到你提供给他们访问的信息，并且不能让他们欺骗你的服务器来<br>
获得他们不应得到的信息。<br>
<br>
有许多种方法完成这个目标，包括：<br>
1）仔细配置你的服务器中的安全及访问控制功能，来限制哪些用户可以访问服务器及他们能访问的区域。<br>
2）以一个非特权用户来运行服务器。<br>
3）使用CHROOT机制来限制服务器操作是在你的文件系统中的一个特定区域中，你以在服务器内或通过一<br>
个外部交换程序来使用CHROOT。<br>
4）不要将要保密信息放在服务器的机器上，这样的话，即使有人入侵到你的服务器上，由于那里没有他们<br>
所感兴趣的东西，至少是没有他们无法从正常渠道得到的信息。5）对于你的网络中蓁机器进行安全配置以便<br>
即使有人入侵的话，也只限于是该服务器这台机器，他们也难以进一步从你的网络中得到更多的信息。要这样<br>
做的话首先必须不能将服务器运行在内部网络中。<br>
<br>
HTTP服务器本身只提供有限的服务，没有许多要关心的安全问题。但在HTTP服务器你要担心的唯一功能<br>
是：它能使用外部程序，特别是能通过CGI（公共网关接口COMMONGATEWAYINTERFACE）与用户交互，CGI是<br>
HTTP提供用户信息如何与服务器连接并通过它传递给外部程序的一个功能。许多HTTP服务器配置成自动运行外<br>
部程序来生成HTML页面。这些程序通常称为CGI程序，甚至它们所使用的CGI并不是程序。如果有人向HTTP服务<br>
器发出一个数据查询要求，HTTP服务器执行一个外部程序来执行这个查询要求并生成HTML页面来作为回答。<br>
<br>
有两个理由需要对外部程序的安全性担心的：<br>
1）入侵者能不能欺骗外部程序去做一些它们不应做的事？<br>
2）入侵者能不能上载他们自己的外部程序并执行它们？<br>
<br>
你可能要在MACINTOSH、DOS和WINDOWS机器上HTTP服务器，这些机器有好的HTTP服务器但通常没有其他的<br>
功能如保密。由于它们不能运行其他服务，没有强有力的外部程序能力。它们越简单，处理安全的能力就越<br>
弱。<br></p> ]]></description>
		<eb:creationDate>2008-04-12 14:59:11</eb:creationDate>
		<eb:modificationDate></eb:modificationDate>
    </item>
    <item>
		<title><![CDATA[ HTTP协议的几个重要概念 ]]></title>
		<link>http://blog.tom.com/tigerkings941220/article/9388.html</link>
		<description><![CDATA[ <p>出处:<font color="#0E3E92">中国协议分析网</font> 责编:<font color="#0E3E92">chinaitpower</font></p>
<p>&nbsp;</p>
<p>1.连接(Connection)：一个传输层的实际环流，它是建立在两个相互通讯的应用程序之间。<br>
　　2.消息(Message)：HTTP通讯的基本单位，包括一个结构化的八元组序列并通过连接传输。<br>
　　3.请求(Request)：一个从客户端到服务器的请求信息包括应用于资源的方法、资源的标识符和<a href="http://www.chinaitpower.com/Net/xieyi/index.html" target="_blank">协议</a>的版本号<br>
　　4.响应(Response)：一个从服务器返回的信息包括HTTP<a href="http://www.chinaitpower.com/Net/xieyi/index.html" target="_blank">协议</a>的版本号、请求的状态(例如“成功”或“没找到”)和文档的MIME类型。<br>
　　5.资源(Resource)：由URI标识的网络数据对象或服务。<br>
　　6.实体(Entity)：数据资源或来自服务资源的回映的一种特殊表示方法，它可能被包围在一个请求或响应信息中。一个实体包括实体头信息和实体的本身内容。<br>
　　7.客户机(Client)：一个为发送请求目的而建立连接的应用程序。<br>
　　8.用户代理(Useragent)：初始化一个请求的客户机。它们是浏览器、编辑器或其它用户工具。<br>
　　9.服务器(Server)：一个接受连接并对请求返回信息的应用程序。<br>
　　10.源服务器(Originserver)：是一个给定资源可以在其上驻留或被创建的服务器。<br>
　　11.代理(Proxy)：一个中间程序，它可以充当一个服务器，也可以充当一个客户机，为其它客户机建立请求。请求是通过可能的翻译在内部或经过传递到其它的服务器中。一个代理在发送请求信息之前，必须解释并且如果可能重写它。<br>
　　代理经常作为通过防火墙的客户机端的门户，代理还可以作为一个帮助应用来通过<a href="http://www.chinaitpower.com/Net/xieyi/index.html" target="_blank">协议</a>处理没有被用户代理完成的请求。<br>
　　12.网关(Gateway)：一个作为其它服务器中间媒介的服务器。与代理不同的是，网关接受请求就好象对被请求的资源来说它就是源服务器；发出请求的客户机并没有意识到它在同网关打交道。<br>
　　网关经常作为通过防火墙的服务器端的门户，网关还可以作为一个<a href="http://www.chinaitpower.com/Net/xieyi/index.html" target="_blank">协议</a>翻译器以便存取那些存储在非HTTP系统中的资源。<br>
　　13.通道(Tunnel)：是作为两个连接中继的中介程序。一旦激活，通道便被认为不属于HTTP通讯，尽管通道可能是被一个HTTP请求初始化的。当被中继的连接两端关闭时，通道便消失。当一个门户(Portal)必须存在或中介(Intermediary)不能解释中继的通讯时通道被经常使用。<br>
　　14.缓存(Cache)：反应信息的局域存储。<br></p> ]]></description>
		<eb:creationDate>2008-04-12 14:57:47</eb:creationDate>
		<eb:modificationDate></eb:modificationDate>
    </item>
    <item>
		<title><![CDATA[ 初识“HTTP” ]]></title>
		<link>http://blog.tom.com/tigerkings941220/article/9387.html</link>
		<description><![CDATA[ <p>出处:<font color="#0E3E92">中国协议分析网</font> 责编:<font color="#0E3E92">chinaitpower</font></p>
<p>&nbsp;</p>
<p>当我们想浏览一个网站的时候，只要在浏览器的地址栏里输入网站的地址就可以了，例如：www.microsoft.com，但是在浏览器的地址栏里面出现的却是：http://www.microsoft.com，你知道为什么会多出一个“http”吗？<br>
<br>
　　一、HTTP<a href="http://www.chinaitpower.com/Net/xieyi/index.html" target="_blank">协议</a>是什么<br>
<br>
　　我们在浏览器的地址栏里输入的网站地址叫做URL(UniformResourceLocator，统一资源定位符)。就像每家每户都有一个门牌地址一样，每个网页也都有一个Internet地址。当你在浏览器的地址框中输入一个URL或是单击一个超级链接时，URL就确定了要浏览的地址。浏览器通过超文本传输<a href="http://www.chinaitpower.com/Net/xieyi/index.html" target="_blank">协议</a>(HTTP)，将Web服务器上站点的网页代码提取出来，并翻译成漂亮的网页。因此，在我们认识HTTP之前，有必要先弄清楚URL的组成,例如：http://www.microsoft.com/china/index.htm。它的含义如下：<br>
<br>
　　1.http://：代表超文本传输<a href="http://www.chinaitpower.com/Net/xieyi/index.html" target="_blank">协议</a>，通知microsoft.com服务器显示Web页，通常不用输入；<br>
<br>
　　2.www：代表一个Web(万维网)服务器；<br>
<br>
　　3.Microsoft.com/：这是装有网页的服务器的域名，或站点服务器的名称；<br>
<br>
　　4.China/：为该服务器上的子目录，就好像我们的文件夹；<br>
<br>
　　5.Index.htm：index.htm是文件夹中的一个HTML文件(网页)。<br>
<br>
　　我们知道，Internet的基本<a href="http://www.chinaitpower.com/Net/xieyi/index.html" target="_blank">协议</a>是TCP/IP<a href="http://www.chinaitpower.com/Net/xieyi/index.html" target="_blank">协议</a>，然而在TCP/IP模型最上层的是应用层(Applicationlayer)，它包含所有高层的<a href="http://www.chinaitpower.com/Net/xieyi/index.html" target="_blank">协议</a>。高层<a href="http://www.chinaitpower.com/Net/xieyi/index.html" target="_blank">协议</a>有：文件传输<a href="http://www.chinaitpower.com/Net/xieyi/index.html" target="_blank">协议</a>FTP、电子邮件传输<a href="http://www.chinaitpower.com/Net/xieyi/index.html" target="_blank">协议</a>SMTP、域名系统服务DNS、网络新闻传输<a href="http://www.chinaitpower.com/Net/xieyi/index.html" target="_blank">协议</a>NNTP和HTTP<a href="http://www.chinaitpower.com/Net/xieyi/index.html" target="_blank">协议</a>等。<br>
<br>
　　HTTP<a href="http://www.chinaitpower.com/Net/xieyi/index.html" target="_blank">协议</a>(Hypertext　Transfer　Protocol，超文本传输<a href="http://www.chinaitpower.com/Net/xieyi/index.html" target="_blank">协议</a>)是用于从WWW服务器传输超文本到本地浏览器的传送<a href="http://www.chinaitpower.com/Net/xieyi/index.html" target="_blank">协议</a>。它可以使浏览器更加高效，使网络传输减少。它不仅保证计算机正确快速地传输超文本文档，还确定传输文档中的哪一部分，以及哪部分内容首先显示(如文本先于图形)等。这就是你为什么在浏览器中看到的网页地址都是以“http://”开头的原因。<br>
<br>
　　自WWW诞生以来，一个多姿多彩的资讯和虚拟的世界便出现在我们眼前，可是我们怎么能够更加容易地找到我们需要的资讯呢？当决定使用超文本作为WWW文档的标准格式后，于是在1990年，科学家们立即制定了能够快速查找这些超文本文档的<a href="http://www.chinaitpower.com/Net/xieyi/index.html" target="_blank">协议</a>，即HTTP<a href="http://www.chinaitpower.com/Net/xieyi/index.html" target="_blank">协议</a>。经过几年的使用与发展，得到不断的完善和扩展，目前在WWW中使用的是HTTP/1.0的第六版。<br>
<br>
　　二、HTTP是怎样工作的<br>
<br>
　　既然我们明白了URL的构成，那么HTTP是怎么工作呢？我们接下来就要讨论这个问题。<br>
<br>
　　由于HTTP<a href="http://www.chinaitpower.com/Net/xieyi/index.html" target="_blank">协议</a>是基于请求/响应范式的(相当于客户机/服务器)。一个客户机与服务器建立连接后，发送一个请求给服务器，请求方式的格式为：统一资源标识符(URL)、<a href="http://www.chinaitpower.com/Net/xieyi/index.html" target="_blank">协议</a>版本号，后边是MIME信息包括请求修饰符、客户机信息和可能的内容。服务器接到请求后，给予相应的响应信息，其格式为一个状态行，包括信息的<a href="http://www.chinaitpower.com/Net/xieyi/index.html" target="_blank">协议</a>版本号、一个成功或错误的代码，后边是MIME信息包括服务器信息、实体信息和可能的内容。<br>
<br>
　　许多HTTP通讯是由一个用户代理初始化的并且包括一个申请在源服务器上资源的请求。最简单的情况可能是在用户代理和服务器之间通过一个单独的连接来完成。在Internet上，HTTP通讯通常发生在TCP/IP连接之上。缺省端口是TCP80，但其它的端口也是可用的。但这并不预示着HTTP<a href="http://www.chinaitpower.com/Net/xieyi/index.html" target="_blank">协议</a>在Internet或其它网络的其它<a href="http://www.chinaitpower.com/Net/xieyi/index.html" target="_blank">协议</a>之上才能完成。HTTP只预示着一个可靠的传输。<br>
<br>
　　这个过程就好像我们打电话订货一样，我们可以打电话给商家，告诉他我们需要什么规格的商品，然后商家再告诉我们什么商品有货，什么商品缺货。这些，我们是通过电话线用电话联系(HTTP是通过TCP/IP)，当然我们也可以通过传真，只要商家那边也有传真。<br>
<br>
　　以上简要介绍了HTTP<a href="http://www.chinaitpower.com/Net/xieyi/index.html" target="_blank">协议</a>的宏观运作方式，下面介绍一下HTTP<a href="http://www.chinaitpower.com/Net/xieyi/index.html" target="_blank">协议</a>的内部操作过程。<br>
<br>
　　在WWW中，“客户”与“服务器”是一个相对的概念，只存在于一个特定的连接期间，即在某个连接中的客户在另一个连接中可能作为服务器。基于HTTP<a href="http://www.chinaitpower.com/Net/xieyi/index.html" target="_blank">协议</a>的客户/服务器模式的信息交换过程，它分四个过程：建立连接、发送请求信息、发送响应信息、关闭连接。这就好像上面的例子，我们电话订货的全过程。<br>
<br>
　　其实简单说就是任何服务器除了包括HTML文件以外，还有一个HTTP驻留程序，用于响应用户请求。你的浏览器是HTTP客户，向服务器发送请求，当浏览器中输入了一个开始文件或点击了一个超级链接时，浏览器就向服务器发送了HTTP请求，此请求被送往由IP地址指定的URL。驻留程序接收到请求，在进行必要的操作后回送所要求的文件。在这一过程中，在网络上发送和接收的数据已经被分成一个或多个数据包(packet)，每个数据包包括：要传送的数据；控制信息，即告诉网络怎样处理数据包。TCP/IP决定了每个数据包的格式。如果事先不告诉你，你可能不会知道信息被分成用于传输和再重新组合起来的许多小块。<br>
<br>
　　也就是说商家除了拥有商品之外，它也有一个职员在接听你的电话，当你打电话的时候，你的声音转换成各种复杂的数据，通过电话线传输到对方的电话机，对方的电话机又把各种复杂的数据转换成声音，使得对方商家的职员能够明白你的请求。这个过程你不需要明白声音是怎么转换成复杂的数据的。<br></p> ]]></description>
		<eb:creationDate>2008-04-12 14:56:22</eb:creationDate>
		<eb:modificationDate></eb:modificationDate>
    </item>
    <item>
		<title><![CDATA[ HTTP协议基础 ]]></title>
		<link>http://blog.tom.com/tigerkings941220/article/9386.html</link>
		<description><![CDATA[ <div>http://tech.163.com/school · 2005-04-27 15:39:09 · 来源: 中国协议网</div>
<div>&nbsp;</div>
<div>
<p style="TEXT-INDENT: 2em">HTTP（HyperTextTransferProtocol）是超文本传输协议的缩写，它用于传送WWW方式的数据，关于HTTP协议的详细内容请参考RFC2616。HTTP协议采用了请求/响应模型。客户端向服务器发送一个请求，请求头包含请求的方法、URI、协议版本、以及包含请求修饰符、客户信息和内容的类似于MIME的消息结构。服务器以一个状态行作为响应，相应的内容包括消息协议的版本，成功或者错误编码加上包含服务器信息、实体元信息以及可能的实体内容。</p>
<p style="TEXT-INDENT: 2em">通常HTTP消息包括客户机向服务器的请求消息和服务器向客户机的响应消息。这两种类型的消息由一个起始行，一个或者多个头域，一个只是头域结束的空行和可选的消息体组成。HTTP的头域包括通用头，请求头，响应头和实体头四个部分。每个头域由一个域名，冒号（:）和域值三部分组成。域名是大小写无关的，域值前可以添加任何数量的空格符，头域可以被扩展为多行，在每行开始处，使用至少一个空格或制表符。</p>
<p style="TEXT-INDENT: 2em">通用头域</p>
<p style="TEXT-INDENT: 2em">通用头域包含请求和响应消息都支持的头域，通用头域包含Cache-Control、Connection、Date、Pragma、Transfer-Encoding、Upgrade、Via。对通用头域的扩展要求通讯双方都支持此扩展，如果存在不支持的通用头域，一般将会作为实体头域处理。下面简单介绍几个在UPnP消息中使用的通用头域。</p>
<p style="TEXT-INDENT: 2em">Cache-Control头域</p>
<p style="TEXT-INDENT: 2em">Cache-Control指定请求和响应遵循的缓存机制。在请求消息或响应消息中设置Cache-Control并不会修改另一个消息处理过程中的缓存处理过程。请求时的缓存指令包括no-cache、no-store、max-age、max-stale、min-fresh、only-if-cached，响应消息中的指令包括public、private、no-cache、no-store、no-transform、must-revalidate、proxy-revalidate、max-age。各个消息中的指令含义如下：</p>
<p style="TEXT-INDENT: 2em">Public指示响应可被任何缓存区缓存。</p>
<p style="TEXT-INDENT: 2em">Private指示对于单个用户的整个或部分响应消息，不能被共享缓存处理。这允许服务器仅仅描述当用户的部分响应消息，此响应消息对于其他用户的请求无效。</p>
<p style="TEXT-INDENT: 2em">no-cache指示请求或响应消息不能缓存</p>
<p style="TEXT-INDENT: 2em">no-store用于防止重要的信息被无意的发布。在请求消息中发送将使得请求和响应消息都不使用缓存。</p>
<p style="TEXT-INDENT: 2em">max-age指示客户机可以接收生存期不大于指定时间（以秒为单位）的响应。</p>
<p style="TEXT-INDENT: 2em">min-fresh指示客户机可以接收响应时间小于当前时间加上指定时间的响应。</p>
<p style="TEXT-INDENT: 2em">max-stale指示客户机可以接收超出超时期间的响应消息。如果指定max-stale消息的值，那么客户机可以接收超出超时期指定值之内的响应消息。</p>
<p style="TEXT-INDENT: 2em">Date头域</p>
<p style="TEXT-INDENT: 2em">Date头域表示消息发送的时间，时间的描述格式由rfc822定义。例如，Date:Mon,31Dec200104:25:57GMT。Date描述的时间表示世界标准时，换算成本地时间，需要知道用户所在的时区。</p>
<p style="TEXT-INDENT: 2em">Pragma头域</p>
<p style="TEXT-INDENT: 2em">Pragma头域用来包含实现特定的指令，最常用的是Pragma:no-cache。在HTTP/1.1协议中，它的含义和Cache-Control:no-cache相同。</p>
<p style="TEXT-INDENT: 2em">请求消息</p>
<p style="TEXT-INDENT: 2em">请求消息的第一行为下面的格式：</p>
<p style="TEXT-INDENT: 2em">MethodSPRequest-URISPHTTP-VersionCRLFMethod表示对于Request-URI完成的方法，这个字段是大小写敏感的，包括OPTIONS、GET、HEAD、POST、PUT、DELETE、TRACE。方法GET和HEAD应该被所有的通用WEB服务器支持，其他所有方法的实现是可选的。GET方法取回由Request-URI标识的信息。HEAD方法也是取回由Request-URI标识的信息，只是可以在响应时，不返回消息体。POST方法可以请求服务器接收包含在请求中的实体信息，可以用于提交表单，向新闻组、BBS、邮件群组和数据库发送消息。</p>
<p style="TEXT-INDENT: 2em">SP表示空格。Request-URI遵循URI格式，在此字段为星号（*）时，说明请求并不用于某个特定的资源地址，而是用于服务器本身。HTTP-Version表示支持的HTTP版本，例如为HTTP/1.1。CRLF表示换行回车符。请求头域允许客户端向服务器传递关于请求或者关于客户机的附加信息。请求头域可能包含下列字段Accept、Accept-Charset、Accept-Encoding、Accept-Language、Authorization、From、Host、If-Modified-Since、If-Match、If-None-Match、If-Range、If-Range、If-Unmodified-Since、Max-Forwards、Proxy-Authorization、Range、Referer、User-Agent。对请求头域的扩展要求通讯双方都支持，如果存在不支持的请求头域，一般将会作为实体头域处理。</p>
<p style="TEXT-INDENT: 2em">典型的请求消息：</p>
<p style="TEXT-INDENT: 2em">GEThttp://download.microtool.de:80/somedata.exe</p>
<p style="TEXT-INDENT: 2em">Host:download.microtool.de</p>
<p style="TEXT-INDENT: 2em">Accept:*/*</p>
<p style="TEXT-INDENT: 2em">Pragma:no-cache</p>
<p style="TEXT-INDENT: 2em">Cache-Control:no-cache</p>
<p style="TEXT-INDENT: 2em">Referer:http://download.microtool.de/</p>
<p style="TEXT-INDENT: 2em">User-Agent:Mozilla/4.04[en](Win95;I;Nav)</p>
<p style="TEXT-INDENT: 2em">Range:bytes=554554-</p>
<p style="TEXT-INDENT: 2em">上例第一行表示HTTP客户端（可能是浏览器、下载程序）通过GET方法获得指定URL下的文件。棕色的部分表示请求头域的信息，绿色的部分表示通用头部分。</p>
<p style="TEXT-INDENT: 2em">Host头域</p>
<p style="TEXT-INDENT: 2em">Host头域指定请求资源的Intenet主机和端口号，必须表示请求url的原始服务器或网关的位置。HTTP/1.1请求必须包含主机头域，否则系统会以400状态码返回。</p>
<p style="TEXT-INDENT: 2em">Referer头域</p>
<p style="TEXT-INDENT: 2em">Referer头域允许客户端指定请求uri的源资源地址，这可以允许服务器生成回退链表，可用来登陆、优化cache等。他也允许废除的或错误的连接由于维护的目的被追踪。如果请求的uri没有自己的uri地址，Referer不能被发送。如果指定的是部分uri地址，则此地址应该是一个相对地址。</p>
<p style="TEXT-INDENT: 2em">Range头域</p>
<p style="TEXT-INDENT: 2em">Range头域可以请求实体的一个或者多个子范围。例如，</p>
<p style="TEXT-INDENT: 2em">表示头500个字节：bytes=0-499</p>
<p style="TEXT-INDENT: 2em">表示第二个500字节：bytes=500-999</p>
<p style="TEXT-INDENT: 2em">表示最后500个字节：bytes=-500</p>
<p style="TEXT-INDENT: 2em">表示500字节以后的范围：bytes=500-</p>
<p style="TEXT-INDENT: 2em">第一个和最后一个字节：bytes=0-0,-1</p>
<p style="TEXT-INDENT: 2em">同时指定几个范围：bytes=500-600,601-999</p>
<p style="TEXT-INDENT: 2em">但是服务器可以忽略此请求头，如果无条件GET包含Range请求头，响应会以状态码206（PartialContent）返回而不是以200（OK）。</p>
<p style="TEXT-INDENT: 2em">User-Agent头域</p>
<p style="TEXT-INDENT: 2em">User-Agent头域的内容包含发出请求的用户信息。</p>
<p style="TEXT-INDENT: 2em">响应消息</p>
<p style="TEXT-INDENT: 2em">响应消息的第一行为下面的格式：</p>
<p style="TEXT-INDENT: 2em">HTTP-VersionSPStatus-CodeSPReason-PhraseCRLF</p>
<p style="TEXT-INDENT: 2em">HTTP-Version表示支持的HTTP版本，例如为HTTP/1.1。Status-Code是一个三个数字的结果代码。Reason-Phrase给Status-Code提供一个简单的文本描述。Status-Code主要用于机器自动识别，Reason-Phrase主要用于帮助用户理解。Status-Code的第一个数字定义响应的类别，后两个数字没有分类的作用。第一个数字可能取5个不同的值：</p>
<p style="TEXT-INDENT: 2em">1xx:信息响应类，表示接收到请求并且继续处理</p>
<p style="TEXT-INDENT: 2em">2xx:处理成功响应类，表示动作被成功接收、理解和接受</p>
<p style="TEXT-INDENT: 2em">3xx:重定向响应类，为了完成指定的动作，必须接受进一步处理</p>
<p style="TEXT-INDENT: 2em">4xx:客户端错误，客户请求包含语法错误或者是不能正确执行</p>
<p style="TEXT-INDENT: 2em">5xx:服务端错误，服务器不能正确执行一个正确的请求</p>
<p style="TEXT-INDENT: 2em">响应头域允许服务器传递不能放在状态行的附加信息，这些域主要描述服务器的信息和Request-URI进一步的信息。响应头域包含Age、Location、Proxy-Authenticate、Public、Retry-After、Server、Vary、Warning、WWW-Authenticate。对响应头域的扩展要求通讯双方都支持，如果存在不支持的响应头域，一般将会作为实体头域处理。</p>
<p style="TEXT-INDENT: 2em">典型的响应消息：</p>
<p style="TEXT-INDENT: 2em">HTTP/1.0200OK</p>
<p style="TEXT-INDENT: 2em">Date:Mon,31Dec200104:25:57GMT</p>
<p style="TEXT-INDENT: 2em">Server:Apache/1.3.14(Unix)</p>
<p style="TEXT-INDENT: 2em">Content-type:text/html</p>
<p style="TEXT-INDENT: 2em">Last-modified:Tue,17Apr200106:46:28GMT</p>
<p style="TEXT-INDENT: 2em">Etag:"a030f020ac7c01:1e9f"</p>
<p style="TEXT-INDENT: 2em">Content-length:39725426</p>
<p style="TEXT-INDENT: 2em">Content-range:bytes554554-40279979/40279980</p>
<p style="TEXT-INDENT: 2em">上例第一行表示HTTP服务端响应一个GET方法。棕色的部分表示响应头域的信息，绿色的部分表示通用头部分，红色的部分表示实体头域的信息。</p>
<p style="TEXT-INDENT: 2em">Location响应头</p>
<p style="TEXT-INDENT: 2em">Location响应头用于重定向接收者到一个新URI地址。</p>
<p style="TEXT-INDENT: 2em">Server响应头</p>
<p style="TEXT-INDENT: 2em">Server响应头包含处理请求的原始服务器的软件信息。此域能包含多个<a href="http://tech.163.com/production/"><font color="#0066CC">产品</font></a>标识和注释，产品标识一般按照重要性排序。</p>
<p style="TEXT-INDENT: 2em">实体</p>
<p style="TEXT-INDENT: 2em">请求消息和响应消息都可以包含实体信息，实体信息一般由实体头域和实体组成。实体头域包含关于实体的原信息，实体头包括Allow、Content-Base、Content-Encoding、Content-Language、Content-Length、Content-Location、Content-MD5、Content-Range、Content-Type、Etag、Expires、Last-Modified、extension-header。extension-header允许客户端定义新的实体头，但是这些域可能无法未接受方识别。实体可以是一个经过编码的字节流，它的编码方式由Content-Encoding或Content-Type定义，它的长度由Content-Length或Content-Range定义。</p>
<p style="TEXT-INDENT: 2em">Content-Type实体头</p>
<p style="TEXT-INDENT: 2em">Content-Type实体头用于向接收方指示实体的介质类型，指定HEAD方法送到接收方的实体介质类型，或GET方法发送的请求介质类型Content-Range实体头</p>
<p style="TEXT-INDENT: 2em">Content-Range实体头用于指定整个实体中的一部分的插入位置，他也指示了整个实体的长度。在服务器向客户返回一个部分响应，它必须描述响应覆盖的范围和整个实体长度。一般格式：</p>
<p style="TEXT-INDENT: 2em">Content-Range:bytes-unitSPfirst-byte-pos-last-byte-pos/entity-legth</p>
<p style="TEXT-INDENT: 2em">例如，传送头500个字节次字段的形式：Content-Range:bytes0-499/1234如果一个http消息包含此节（例如，对范围请求的响应或对一系列范围的重叠请求），Content-Range表示传送的范围，Content-Length表示实际传送的字节数。</p>
<p style="TEXT-INDENT: 2em">Last-modified实体头</p>
<p style="TEXT-INDENT: 2em">Last-modified实体头指定服务器上保存内容的最后修订时间。</p>
</div> ]]></description>
		<eb:creationDate>2008-04-12 14:53:43</eb:creationDate>
		<eb:modificationDate></eb:modificationDate>
    </item>
    <item>
		<title><![CDATA[ GCC笔记 ]]></title>
		<link>http://blog.tom.com/tigerkings941220/article/9333.html</link>
		<description><![CDATA[ <div id="art" style="MARGIN: 15px">
<p><strong><font color="#FF0000">The History of GCC</font></strong></p>
<hr style="WIDTH: 100%; HEIGHT: 2px">
<div style="TEXT-ALIGN: left">1984年，Richard Stallman发起了自由软件运动，GNU (Gnu's Not Unix)项目应运而生，3年后，最初版的GCC横空出世，成为第一款可移植、可优化、支持ANSI C的开源C编译器。<br>
GCC最初的全名是GNU C Compiler,之后，随着GCC支持的语言越来越多，它的名称变成了GNU Compiler Collection。<br>
这里介绍的gcc是GCC的前端，C编译器.<br></div>
<p><br>
<font style="FONT-WEIGHT: bold; COLOR: rgb(255,1,2)" color="#0000FF">警告信息</font></p>
<hr style="WIDTH: 100%; HEIGHT: 2px">
<div style="TEXT-ALIGN: left">&nbsp;&nbsp;&nbsp; -Wall : 显示所有常用的编译警告信息。<br>
&nbsp;&nbsp; &nbsp;-W&nbsp;&nbsp;&nbsp; : 显示更多的常用编译警告，如：变量未使用、一些逻辑错误。<br>
&nbsp;&nbsp; &nbsp;-Wconversion : 警告隐式类型转换。<br>
&nbsp;&nbsp; &nbsp;-Wshadow : 警告影子变量（在代码块中再次声明已声明的变量)<br>
&nbsp;&nbsp; &nbsp;-Wcast-qual ：警告指针修改了变量的修饰符。如：指针修改const变量。<br>
&nbsp;&nbsp; &nbsp;-Wwrite-strings : 警告修改const字符串。<br>
&nbsp;&nbsp; &nbsp;-Wtraditional : 警告ANSI编译器与传统C编译器有不同的解释。<br>
&nbsp;&nbsp; &nbsp;-Werror : 即使只有警告信息，也不编译。（gcc默认：若只有警告信息，则进行编译，若有错误信息，则不编译）<br></div>
<p><br>
<font style="COLOR: rgb(255,1,2)" color="#0000FF"><span style="FONT-WEIGHT: bold">C语言标准</span></font></p>
<hr style="WIDTH: 100%; HEIGHT: 2px">
<div style="TEXT-ALIGN: left">你可以在gcc的命令行中通过指定选项来选择相应的C语言标准: 从传统c到最新的GNU扩展C. 默认情况下, gcc使用最新的GNU C扩展.<br>
<br>
&nbsp;&nbsp; &nbsp;-ansi : 关闭GNU扩展中与ANSI C相抵触的部分。<br>
&nbsp;&nbsp; &nbsp;-pedantic&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; : 关闭所有的GNU扩展。<br>
&nbsp;&nbsp; &nbsp;-std=c89&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; : 遵循C89标准<br>
&nbsp;&nbsp; &nbsp;-std=c99&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; : 遵循C99标准<br>
&nbsp;&nbsp;&nbsp; -std=traditional : 使用原始C<br>
注意：后4个选项可以与-ansi结合使用，也可以单独使用。<br>
<br>
可在gcc中使用大量GNU C扩展.<br></div>
<p><font style="FONT-WEIGHT: bold; COLOR: rgb(255,1,2)" color="#0000FF">生成特定格式的文件</font></p>
<hr style="WIDTH: 100%; HEIGHT: 2px">
<p>以hello.c为例子，可以设置选项生成hello.i, hello.s, hello.o以及最终的hello文件：<br>
<br>
&nbsp;&nbsp; &nbsp;hello.c : 最初的源代码文件；<br>
&nbsp;&nbsp; &nbsp;hello.i : 经过编译预处理的源代码；<br>
&nbsp;&nbsp; &nbsp;hello.s : 汇编处理后的汇编代码；<br>
&nbsp;&nbsp; &nbsp;hello.o : 编译后的目标文件，即含有最终编译出的机器码，但它里面所引用的其他文件中函数的内存位置尚未定义。<br>
&nbsp;&nbsp;&nbsp; hello / a.out : 最终的可执行文件<br>
&nbsp;&nbsp;&nbsp; (还有.a(静态库文件), .so(动态库文件), .s(汇编源文件)留待以后讨论)<br></p>
<table style="BORDER-RIGHT: rgb(153,153,153) 1px solid; BORDER-TOP: rgb(153,153,153) 1px solid; FONT-SIZE: 12px; BORDER-LEFT: rgb(153,153,153) 1px solid; WIDTH: 80%; BORDER-BOTTOM: rgb(153,153,153) 1px solid" align="center">
<tbody>
<tr>
<td>
<p>如果你不通过-o指定生成可执行文件名，那么会默认生成a.out. 不指定生成文件名肯能覆盖你上次生成的a.out.<br></p>
<p><span style="FONT-WEIGHT: bold">e.g.</span><br>
$ <span style="COLOR: rgb(0,1,255)">gcc hello.c</span><br>
在不给gcc传递任何参数的情况下, gcc执行默认的操作: 将源文件编译为目标文件--&gt; 将目标文件连接为可执行文件(名为a.out) --&gt; 删除目标文件.<br></p>
<p>-c生成.o文件时，默认生成与源代码的主干同名的.o文件。比如对应hello.c生成hello.o. 但也可在生成目标文件时指定目标文件名(注意同时要给出.o后缀): $ <span style="COLOR: rgb(0,1,255)">gcc -c -o demo.o demo.c</span><br></p>
</td>
</tr>
</tbody>
</table>
<p>&nbsp;&nbsp;&nbsp; $ <span style="COLOR: rgb(0,1,255)">gcc -Wall -c hello.c&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; : 生成hello.o<br>
&nbsp;&nbsp; &nbsp;$ <span style="COLOR: rgb(0,1,255)">gcc -Wall -c -save-temps hello.c</span>&nbsp; : 生成hello.i, hello.s, hello.o<br>
&nbsp;&nbsp; &nbsp;注意-Wall 选项的使用场合：仅在涉及到编译（即会生成.o文件时，用-Wall）&nbsp;&nbsp;<br></p>
<p><font style="FONT-WEIGHT: bold; COLOR: rgb(255,1,2)" color="#0000FF">多文件编译、连接</font><br></p>
<hr style="WIDTH: 100%; HEIGHT: 2px">
<p>如果原文件分布于多个文件中：file1.c, file2,c<br>
&nbsp;&nbsp; &nbsp;$ <span style="COLOR: rgb(0,1,255)">gcc -Wall file1.c file2.c -o name</span></p>
<p>若对其中一个文件作了修改，则可只重新编译该文件,再连接所有文件：<br>
&nbsp;&nbsp; &nbsp;$ <span style="COLOR: rgb(0,1,255)">gcc -Wall -c file2.c</span><br>
&nbsp;&nbsp; &nbsp;$ <span style="COLOR: rgb(0,1,255)">gcc file1.c file2.o -c name</span></p>
<p>注意：若编译器在命令行中从左向右顺序读取.o文件，则它们的出现顺序有限制：含有某函数定义的文件必须出现在含有调用该函数的文件之后。好在GCC无此限制。<br></p>
<p><font color="#FF0000"><strong>编译预处理</strong></font></p>
<hr style="WIDTH: 100%; HEIGHT: 2px">
以上述的hello.c为例, 要对它进行编译预备处理, 有两种方法: 在gcc中指定-E选项, 或直接调用cpp.gcc的编译预处理命令程序为cpp，比较新版本的gcc已经将cpp集成了，但仍提供了cpp命令. 可以直接调用cpp命令, 也可以在gcc中指定-E选项指定它只进行编译预处理.<br>
<br>
$ <span style="COLOR: rgb(0,1,255)">gcc -E hello.c&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style="COLOR: rgb(0,1,2)">==&nbsp; $</span> cpp hello.c</span><br>
上述命令马上将预处理结果显示出来. 不利于观看. 可采用-c将预处理结果保存:<br>
$ <span style="COLOR: rgb(0,1,255)">gcc -E -c hello.i hello.c&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style="COLOR: rgb(0,1,2)">==&nbsp; $</span> cpp -o hello.i hello.c</span><br>
注意, -c指定名称要给出".i"后缀.<br>
<br>
另外, gcc针对编译预处理提供了一些选项:<br>
(1) 除了直接在源代码中用 #define NAME来定义宏外，gcc可在命令行中定义宏：-DNAME(其中NAME为宏名),&nbsp; 也可对宏赋值: -DNAME=value 注意等号两边不能有空格! 由于宏扩展只是一个替换过程，也可以将value换成表达式，但要在两边加上双括号: -DNAME="statement"<br>
e.g. $ <font color="#0000FF">gcc -Wall -DVALUE="2+2" tmp.c -o tmp</font><br>
如果不显示地赋值，如上例子，只给出：-DVALUE,gcc将使用默认值：1.<br>
<br>
(2) 除了用户定义的宏外, 有一些宏是编译器自动定义的，它们以__开头，运行: $ <span style="COLOR: rgb(0,1,255)">cpp -dM /dev/null</span>, 可以看到这些宏. 注意, 其中含有不以__开头的非ANSI宏，它们可以通过-ansi选项被禁止。<br>
&nbsp;<br>
<font color="#0000FF"><strong>查看宏扩展<br>
<br></strong></font>1, 运行 $ <span style="COLOR: rgb(0,1,255)">gcc -E test.c</span> ，gcc对test.c进行编译预处理，并立马显示结果. (不执行编译) 2, 运行 $ <span style="COLOR: rgb(0,1,255)">gcc -c -save-temps test.c</span> ，不光产生test.o，还产生test.i, test.s，前者是编译预处理结果, 后者是汇编结果.<br>
&nbsp;&nbsp;&nbsp;<br>
<font color="#0000FF"><strong>利用Emacs查看编译预处理结果<br>
<br></strong></font>针对含有编译预处理命令的代码，可以利用emacs方便地查看预处理结果，而不需执行编译，更为方便的是，可以只选取一段代码，而非整个文件：<br>
1，选择想要查看的代码<br>
2，C-c C-e (M-x c-macro-expand)<br>
这样，就自动在一个名为"Macroexpansion"的buffer中显示pre-processed结果.<br>
<br>
<span style="FONT-WEIGHT: bold; COLOR: rgb(255,1,2)">生成汇编代码</span><br>
<hr style="WIDTH: 100%; HEIGHT: 2px">
使用"-S"选项指定gcc生成以".s"为后缀的汇编代码:<br>
$ <span style="COLOR: rgb(0,1,255)">gcc -S hello.c</span><br>
$ <span style="COLOR: rgb(0,1,255)">gcc -S -o hello.s hello.c</span><br style="COLOR: rgb(0,1,255)">
<br>
生成汇编语言的格式取决于目标平台. 另外, 如果是多个.c文件, 那么针对每一个.c文件生成一个.s文件.<br>
<p><span style="FONT-WEIGHT: bold; COLOR: rgb(0,1,255)"><font color="#FF0000">包含头文件</font></span></p>
<hr id="null">
在程序中包含与连接库对应的头文件是很重要的方面，要使用库，就一定要能正确地引用头文件。一般在代码中通过#include引入头文件, 如果头文件位于系统默认的包含路径(/usr/includes), 则只需在#include中给出头文件的名字, 不需指定完整路径.&nbsp; 但若要包含的头文件位于系统默认包含路径之外, 则有其它的工作要做: 可以(在源文件中)同时指定头文件的全路径. 但考虑到可移植性，最好通过-I在调用gcc的编译命令中指定。
<p>下面看这个求立方的小程序(阴影语句表示刚开始不存在）:</p>
<table style="BORDER-RIGHT: rgb(153,153,153) 1px solid; BORDER-TOP: rgb(153,153,153) 1px solid; FONT-SIZE: 12px; BORDER-LEFT: rgb(153,153,153) 1px solid; WIDTH: 80%; BORDER-BOTTOM: rgb(153,153,153) 1px solid" align="center">
<tbody>
<tr>
<td>#include &lt;stdio.h&gt;<br>
<span style="COLOR: rgb(199,199,199)">#include &lt;math.h&gt;</span><br>
int main(int argc, char *argv[])<br>
{<br>
&nbsp; double x = pow (2.0, 3.0);<br>
&nbsp; printf("The cube of 2.0 is %f\n", x);<br>
&nbsp; return 0;<br>
}</td>
</tr>
</tbody>
</table>
<p>使用gcc-2.95来编译它(-lm选项在后面的连接选项中有介绍, 这里只讨论头文件的包含问题):<br>
$ <span style="COLOR: rgb(0,1,255)">gcc-2.95 -Wall pow.c -lm -o pow_2.95</span><br>
<font color="#494949">pow.c: In function `main':<br>
pow.c:5: warning: implicit declaration of function `pow'</font></p>
<p>程序编译成功，但gcc给出警告: pow函数隐式声明。<br>
$ <span style="COLOR: rgb(0,1,255)">./pow_2.95</span><br>
<font color="#494949">The cube of 2.0 is 1.000000</font></p>
<p>明显执行结果是错误的，在源程序中引入头文件(#include &lt;math.h&gt;)，消除了错误。</p>
<table style="BORDER-RIGHT: rgb(153,153,153) 1px solid; BORDER-TOP: rgb(153,153,153) 1px solid; FONT-SIZE: 12px; BORDER-LEFT: rgb(153,153,153) 1px solid; WIDTH: 80%; BORDER-BOTTOM: rgb(153,153,153) 1px solid" align="center">
<tbody>
<tr>
<td>不要忽略Warning信息！它可能预示着，程序虽然编译成功，但运行结果可能有错。故，起码加上"-Wall"编译选项！并尽量修正Warning警告。<br></td>
</tr>
</tbody>
</table>
<p><font style="FONT-WEIGHT: bold; COLOR: rgb(0,1,255)" color="#9999CC">搜索路径</font></p>
<p>首先要理解 #include&lt;file.h&gt;和#include"file.h"的区别:<br>
#include&lt;file.h&gt;只在默认的系统包含路径搜索头文件<br>
#include"file.h"首先在当前目录搜索头文件, 若头文件不位于当前目录, 则到系统默认的包含路径搜索头文件.<br></p>
<p>UNIX类系统默认的系统路径为：</p>
<p>头文件，包含路径：　/usr/local/include/&nbsp; or&nbsp; /usr/include/<br>
库文件，连接路径：　/usr/local/lib/&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; or&nbsp; /usr/lib/&nbsp;&nbsp;&nbsp;</p>
<p>对于标准c库(glibc或其它c库)的头文件, 我们可以直接在源文件中使用#include &lt;file.h&gt;来引入头文件.<br></p>
<p>如果要在源文件中引入自己的头文件, 就需要考虑下面的问题:<br></p>
<p>1, 如果使用非系统头文件, 头文件和源文件位于同一个目录, 如何引用头文件呢?<br>
——我们可以简单地在源文件中使用 #include "file.h", gcc将当前目录的file.h引入到源文件. 如果你很固执, 仍想使用#include &lt;file.h&gt;语句, 可以在调用gcc时添加"-I."来将当前目录添加到系统包含路径. 细心的朋友可能会想到: 这样对引用其它头文件会不会有影响? 比如, #include&lt;file.h&gt;之后紧接着一个#include&lt;math.h&gt;, 它能正确引入math.h吗? 答案是: 没有影响. 仍然能正确引用math.h. 我的理解是: "-I."将当前目录作为包含路径的第一选择, 若在当前目录找不到头文件, 则在默认路径搜索头文件. 这实际上和#include"file.h"是一个意思.<br></p>
<p>2, 对于比较大型的工程, 会有许多用户自定义的头文件, 并且头文件和.c文件会位于不同的目录. 又该如何在.c文件中引用头文件呢?<br>
—— 可以直接在.c文件中利用#include“/path/file.h", 通过指定头文件的路径(可以是绝对路径, 也可以是相对路径)来包含头文件. 但这明显降低了程序的可移植性. 在别的系统环境下编译可能会出现问题. 所以还是利用"-I"选项指定头文件完整的包含路径.<br></p>
<table style="BORDER-RIGHT: rgb(153,153,153) 1px solid; BORDER-TOP: rgb(153,153,153) 1px solid; FONT-SIZE: 12px; BORDER-LEFT: rgb(153,153,153) 1px solid; WIDTH: 80%; BORDER-BOTTOM: rgb(153,153,153) 1px solid" align="center">
<tbody>
<tr>
<td>针对头文件比较多的情况, 最好把它们统一放在一个目录中, 比如~/project/include. 这样就不需为不同的头文件指定不同的路径. 如果你嫌每次输入这么多选项太麻烦, 你可以通过设置环境变量来添加路径:<br>
$ <font color="#0000FF">C_INCLUDE_PATH=/opt/gdbm-1.8.3/include</font><br>
$ <font color="#0000FF">export C_INCLUDE_PATH</font><br>
$ <font color="#0000FF">LIBRART_PATH=/opt/gdbm-1.8.3/lib</font><br>
$ <font color="#0000FF">export LIBRART_PATH<br>
<br></font>可一次指定多个搜索路径，":"用于分隔它们，"."表示当前路径，如：<br>
$ <span style="COLOR: rgb(0,1,255)">C_INCLUDE_PATH=.:/opt/gdbm-1.8.3/include:/net/include</span><br>
$ <span style="COLOR: rgb(0,1,255)">LIBRARY_PATH=.:/opt/gdbm-1.8.3/lib:/net/lib</span><br>
（可以添加多个路径，路径之间用:相隔，.代表当前目录，若.在最前头，也可省略）<br>
<br>
当然，若想永久地添加这些路径，可以在.bash_profile中添加上述语句.<br></td>
</tr>
</tbody>
</table>
<p>3, 还有一个比较猥琐的办法: 系统默认的包含路径不是/usr/include或/usr/local/include么? 我把自己的头文件拷贝到其中的一个目录, 不就可以了么? 的确可以这样, 如果你只想在你自己的机器上编译运行这个程序的话<img src="http://www.cublog.cn/images/face/004.gif">.<br></p>
前面介绍了三种添加搜索路径的方法，如果这三种方法一起使用，优先级如何呢？<br>
命令行设置　&gt; 环境变量设置　&gt; 系统默认<br>
<br>
<p><font style="FONT-WEIGHT: bold; COLOR: rgb(255,1,2)" color="#9999CC">与外部库连接<br></font></p>
<hr style="WIDTH: 100%; HEIGHT: 2px">
前面介绍了如何包含头文件. 而头文件和库是息息相关的, 使用库时, 要在源代码中包含适当的头文件，这样才能声明库中函数的原型(发布库时, 就需要给出相应的头文件).<br>
<br>
和包含路径一样, 系统也有默认的连接路径:<br>
<span style="COLOR: rgb(73,73,73)">头文件，包含路径：　/usr/local/include/&nbsp; or&nbsp; /usr/include/</span><br style="COLOR: rgb(73,73,73)">
<span style="COLOR: rgb(73,73,73)">库文件，连接路径：　/usr/local/lib/&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; or&nbsp; /usr/lib/&nbsp;&nbsp;</span><br>
<br>
同样地, 我们想要使用某个库里的函数, 必须将这个库连接到使用那些函数的程序中.<br>
<br>
<table style="BORDER-RIGHT: rgb(153,153,153) 1px solid; BORDER-TOP: rgb(153,153,153) 1px solid; FONT-SIZE: 12px; BORDER-LEFT: rgb(153,153,153) 1px solid; WIDTH: 80%; BORDER-BOTTOM: rgb(153,153,153) 1px solid" align="center">
<tbody>
<tr>
<td>有一个例外: libc.a或libc.so (C标准库,它包含了ANSI C所定义的C函数)是不需要你显式连接的, 所有的C程序在运行时都会自动加载c标准库.<br></td>
</tr>
</tbody>
</table>
<br>
除了C标准库之外的库称之为"外部库", 它可能是别人提供给你的, 也可能是你自己创建的(后面有介绍如何创建库的内容).<br>
<br>
外部库有两种：(1)静态连接库lib.a<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; (2)共享连接库lib.so
<p><span style="COLOR: rgb(0,1,255)">两者的共同点：</span><br>
&nbsp;&nbsp;&nbsp; .a, .so都是.o目标文件的集合，这些目标文件中含有一些函数的定义（机器码），而这些函数将在连接时会被最终的可执行文件用到。</p>
<p><span style="COLOR: rgb(0,1,255)">两者的区别：</span><br>
&nbsp;&nbsp; &nbsp;静态库.a&nbsp; : 当程序与静态库连接时，库中目标文件所含的所有将被程序使用的函数的机器码被copy到最终的可执行文件中. 静态库有个缺点: 占用磁盘和内存空间. 静态库会被添加到和它连接的每个程序中, 而且这些程序运行时, 都会被加载到内存中. 无形中又多消耗了更多的内存空间.<br></p>
<p>&nbsp;&nbsp;&nbsp; 共享库.so : 与共享库连接的可执行文件只包含它需要的函数的引用表，而不是所有的函数代码，只有在程序执行时, 那些需要的函数代码才被拷贝到内存中, 这样就使可执行文件比较小, 节省磁盘空间(更进一步，操作系统使用虚拟内存，使得一份共享库驻留在内存中被多个程序使用).共享库还有个优点: 若库本身被更新, 不需要重新编译与它连接的源程序。</p>
<p><span style="FONT-WEIGHT: bold; COLOR: rgb(0,1,255)">静态库</span></p>
<p>下面我们来看一个简单的例子，计算2.0的平方根（假设文件名为sqrt.c）：</p>
<table style="BORDER-RIGHT: rgb(153,153,153) 1px solid; BORDER-TOP: rgb(153,153,153) 1px solid; FONT-SIZE: 12px; BORDER-LEFT: rgb(153,153,153) 1px solid; WIDTH: 80%; BORDER-BOTTOM: rgb(153,153,153) 1px solid" align="center">
<tbody>
<tr>
<td><font size="2"><span style="FONT-FAMILY: times new roman">#include &lt;math.h&gt;</span><br style="FONT-FAMILY: times new roman">
<span style="FONT-FAMILY: times new roman">#include &lt;stdio.h&gt;</span><br style="FONT-FAMILY: times new roman">
<span style="FONT-FAMILY: times new roman">int</span><br style="FONT-FAMILY: times new roman">
<span style="FONT-FAMILY: times new roman">main (void)</span><br style="FONT-FAMILY: times new roman">
<span style="FONT-FAMILY: times new roman">{</span><br style="FONT-FAMILY: times new roman">
<span style="FONT-FAMILY: times new roman">double x = sqrt (2.0);</span><br style="FONT-FAMILY: times new roman">
<span style="FONT-FAMILY: times new roman">printf ("The square root of 2.0 is %f\n", x);</span><br style="FONT-FAMILY: times new roman">
<span style="FONT-FAMILY: times new roman">return 0;</span><br style="FONT-FAMILY: times new roman">
<span style="FONT-FAMILY: times new roman">}</span></font><br></td>
</tr>
</tbody>
</table>
<p>用gcc将它编译为可执行文件：<br>
$ <span style="COLOR: rgb(0,1,255)">gcc -Wall sqrt.c -o sqrt</span><br>
编译成功，没有任何警告或错误信息。执行结果也正确。<br>
$ <span style="COLOR: rgb(0,1,255)">./sqrt</span><br style="COLOR: rgb(0,1,255); FONT-STYLE: italic">
<span style="COLOR: rgb(0,1,255); FONT-STYLE: italic"><span style="COLOR: rgb(0,1,2)">The square root of 2.0 is 1.414214</span></span><br>
&nbsp;&nbsp;&nbsp;&nbsp;<br>
下面我们来看看刚才使用的gcc版本：<br>
$ <span style="COLOR: rgb(0,1,255)">gcc --version</span><br>
&nbsp;&nbsp;<span style="FONT-STYLE: italic">gcc (GCC) 4.0.2 20050808 (prerelease) (Ubuntu 4.0.1-4ubuntu9)</span></p>
<p>现在我用2.95版的gcc把sqrt.c再编译一次：<br>
$ <span style="COLOR: rgb(0,1,255)">gcc-2.95 -Wall sqrt.c -o sqrt_2.95</span><br>
&nbsp;&nbsp;<span style="FONT-STYLE: italic">/tmp/ccVBJd2H.o: In function `main':</span><br style="FONT-STYLE: italic">
<span style="FONT-STYLE: italic">&nbsp;&nbsp;sqrt.c:(.text+0x16): undefined reference to `sqrt'</span><br style="FONT-STYLE: italic">
<span style="FONT-STYLE: italic">&nbsp;&nbsp;&nbsp;&nbsp; collect2: ld returned 1 exit status</span><br>
&nbsp;&nbsp;&nbsp;&nbsp;<br>
编译器会给出上述错误信息，这是因为sqrt函数不能与外部数学库"libm.a"相连。sqrt函数没有在程序中定义，也不存在于默认C库 "libc.a"中，如果用gcc-2.95，应该显式地选择连接库。上述出错信息中的"/tmp/ccVBJd2H.o"是gcc创造的临时目标文件，用作连接时用。</p>
<p>使用下列的命令可以成功编译：<br>
$ <span style="COLOR: rgb(0,1,255)">gcc-2.95 -Wall sqrt.c /usr/lib/libm.a -o sqrt_2.95</span><span style="COLOR: rgb(0,1,255); FONT-STYLE: italic"><br></span><span style="COLOR: rgb(0,1,255)"><span style="COLOR: rgb(0,1,2)">它告知gcc:在编译sqrt.c时，加入位于/usr/lib中的libm.a库（C数学库）。</span></span></p>
<table style="BORDER-RIGHT: rgb(153,153,153) 1px solid; BORDER-TOP: rgb(153,153,153) 1px solid; FONT-SIZE: 12px; BORDER-LEFT: rgb(153,153,153) 1px solid; WIDTH: 80%; BORDER-BOTTOM: rgb(153,153,153) 1px solid" align="center">
<tbody>
<tr>
<td>C库文件默认位于/usr/lib, /usr/local/lib系统目录中； gcc默认地从/usr/local/lib, /usr/lib中搜索库文件。（在我的Ubuntu系统中，C库文件位于/urs/lib中。<br></td>
</tr>
</tbody>
</table>
<p><span style="COLOR: rgb(0,1,255)"><span style="COLOR: rgb(0,1,2)">这里还要注意连接顺序的问题，比如上述命令，如果我改成：<br></span></span>$ <span style="COLOR: rgb(0,1,255)">gcc-2.95 -Wall /usr/lib/libm.a</span> <span style="COLOR: rgb(0,1,255)">sqrt.c</span> <span style="COLOR: rgb(0,1,255)">-o sqrt_2.95</span><span style="COLOR: rgb(0,1,255); FONT-STYLE: italic"><br></span><span style="COLOR: rgb(0,1,255)"><span style="COLOR: rgb(0,1,2)">gcc会给出出错信息：<br></span></span><span style="COLOR: rgb(0,1,255); FONT-STYLE: italic"><span style="COLOR: rgb(0,1,2)">&nbsp;/tmp/cc6b3bIa.o: In function `main':</span><br style="COLOR: rgb(0,1,2)">
<span style="COLOR: rgb(0,1,2)">&nbsp;sqrt.c:(.text+0x16): undefined reference to `sqrt'</span><br style="COLOR: rgb(0,1,2)">
<span style="COLOR: rgb(0,1,2)">&nbsp;collect2: ld returned 1 exit status</span></span></p>
<p><span style="COLOR: rgb(0,1,255)"><span style="COLOR: rgb(0,1,2)">正如读取目标文件的顺序，gcc也在命令行中从左向右读取库文件——任何包含某函数定义的库文件必须位于调用该函数的目标文件之后！</span></span></p>
<p><span style="COLOR: rgb(0,1,255)"><span style="COLOR: rgb(0,1,2)">指定库文件的绝对路径比较繁琐，有一种简化方法，相对于上述命令，可以用下面的命令来替代：<br></span></span>$ <span style="COLOR: rgb(0,1,255)">gcc-2.95 -Wall sqrt.c -lm -o sqrt_2.95</span><span style="COLOR: rgb(0,1,255); FONT-STYLE: italic"><br></span><span style="COLOR: rgb(0,1,255)"><span style="COLOR: rgb(0,1,2)">其中的"-l"表示与库文件连接，"m"代表"libm.a"中的m。一般而言，"-lNAME"选项会使gcc将目标文件与名为"libNAME.a"的库文件相连。（这里假设使用默认目录中的库，对于其他目录中的库文件，参考后面的“搜索路径”。）</span><br></span></p>
<p>上面所提到的"libm.a"就是静态库文件，所有静态库文件的扩展名都是.a！<br>
$ <span style="COLOR: rgb(0,1,255)">whereis libm.a</span><br>
&nbsp;&nbsp;<span style="FONT-STYLE: italic">libm: /usr/lib/libm.a /usr/lib/libm.so</span></p>
<p>正如前面所说，默认的库文件位于/usr/lib/或/usr/local/lib/目录中。其中，libm.a是静态库文件，libm.so是后面会介绍的动态共享库文件。</p>
<p>如果调用的函数都包含在libc.a中（C标准库被包含在/usr/lib/libc.a中，它包含了ANSI C所定义的C函数）。那么没有必要显式指定libc.a：所有的C程序运行时都自动包含了C标准库！（试试 $ gcc-2.95 -Wall hello.c -o hello)。</p>
<p><span style="FONT-WEIGHT: bold; COLOR: rgb(0,1,255)">共享库</span></p>
<p>正因为共享库的优点，如果系统中存在.so库，gcc默认使用共享库（在/usr/lib/目录中，库文件以共享和静态两种版本存在）。&nbsp;</p>
<p>运行：$ <font color="#0000FF">gcc -Wall -L. hello.c -lNAME -o hello</font><br>
gcc先检查是否有替代的libNAME.so库可用。&nbsp;&nbsp;&nbsp;</p>
<p>正如前面所说，共享库以.so为扩展名（so == shared object)。</p>
<p>那么，如果不想用共享库，而只用静态库呢？可以加上 -static选项<br>
$ <font color="#0000FF">gcc -Wall -static hello.c -lNAME -o hello</font><br>
它等价于：<br>
$ <font color="#0000FF">gcc -Wall hello.c libNAME.a -o hello</font></p>
<p style="COLOR: rgb(118,118,118)">$ gcc-2.95 -Wall sqrt.c -static -lm -o sqrt_2.95_static<br>
$ gcc-2.95 -Wall sqrt.c -lm -o sqrt_2.95_default<br>
$ gcc-2.95 -Wall sqrt.c /usr/lib/libm.a -o sqrt_2.95_a<br>
$ gcc-2.95 -Wall sqrt.c /usr/lib/libm.so -o sqrt_2.95_so</p>
<p style="COLOR: rgb(118,118,118)"><span style="COLOR: rgb(118,118,118)">$ ls -l sqrt*</span><br style="COLOR: rgb(118,118,118)">
<span style="COLOR: rgb(118,118,118)">-rwxr-xr-x&nbsp; 1 zp zp&nbsp; 21076 2006-04-25 14:52 sqrt_2.95_a</span><br style="COLOR: rgb(118,118,118)">
<span style="COLOR: rgb(118,118,118)">-rwxr-xr-x&nbsp; 1 zp zp&nbsp;&nbsp; 7604 2006-04-25 14:52 sqrt_2.95_default</span><br style="COLOR: rgb(118,118,118)">
<span style="COLOR: rgb(118,118,118)">-rwxr-xr-x&nbsp; 1 zp zp&nbsp;&nbsp; 7604 2006-04-25 14:52 sqrt_2.95_so</span><br style="COLOR: rgb(118,118,118)">
<span style="COLOR: rgb(118,118,118)">-rwxr-xr-x&nbsp; 1 zp zp 487393 2006-04-25 14:52 sqrt_2.95_static</span></p>
<p>上述用四种方式编译sqrt.c，并比较了可执行文件的大小。奇怪的是，-static -lm 和 /lib/libm.a为什么有区别？有知其原因着，恳请指明，在此谢谢了！ :)</p>
<p>如果libNAME.a在当前目录，应执行下面的命令：<br>
$ <font color="#0000FF">gcc -Wall -L. hello.c -lNAME -o hello</font><br>
-L.表示将当前目录加到连接路径。</p>
<p><font style="COLOR: rgb(0,1,255)" color="#9999CC"><strong>利用GNU archiver创建库</strong></font></p>
<p>$ <font color="#0000FF">ar cr libhello.a hello_fn.o by_fn.o</font><br>
从hello_fn.o和by_fn.o创建libihello.a，其中cr表示：creat &amp; replace<br>
$ <font color="#0000FF">ar t libhello.a</font><br>
列出libhello.a中的内容，t == table<br>
（也可创建libhello.so）</p>
<p>关于创建库的详细介绍，可参考本blog的<a href="http://www.cublog.cn/u/13991/showart.php?id=104690" target="_blank">GNU binutils笔记</a><br></p>
<br>
<font color="#FF0000"><strong>调试</strong></font>
<hr style="WIDTH: 100%; HEIGHT: 2px">
一般地，可执行文件中是不包含任何对源代码的参考的，而debugger要工作，就要知道目标文件／可执行文件中的机器码对应的源代码的信息（如：哪条语句、函数名、变量名...). debugger工作原理：将函数名、变量名，对它们的引用，将所有这些对象对应的代码行号储存到目标文件或可执行文件的符号表中。
<p>GCC提供-g选项，将调试信息加入到目标文件或可执行文件中。<br>
$ <font color="#0000FF">gcc -Wall -g hello.c -o hello</font><br>
<br>
注意：若发生了段错误，但没有core dump，是由于系统禁止core文件的生成！<br>
$ <font color="#0000FF">ulimit -c</font>　　，若显示为0，则系统禁止了core dump<br></p>
<p><span style="FONT-WEIGHT: bold">解决方法</span>:<br>
$ <font color="#0000FF">ulimit -c unlimited</font>　　（只对当前shell进程有效）<br>
或在~/.bashrc　的最后加入： ulimit -c unlimited （一劳永逸）<br></p>
<p><font color="#FF0000"><strong>优化</strong></font></p>
<hr style="WIDTH: 100%; HEIGHT: 2px">
GCC具有优化代码的功能，代码的优化是一项比较复杂的工作，它可归为：源代码级优化、速度与空间的权衡、执行代码的调度。
<p>GCC提供了下列优化选项：<br>
&nbsp;&nbsp;&nbsp; -O0&nbsp; : 默认不优化（若要生成调试信息，最好不优化）<br>
&nbsp;&nbsp;&nbsp; -O1&nbsp; : 简单优化，不进行速度与空间的权衡优化；&nbsp;&nbsp;&nbsp;<br>
&nbsp;&nbsp;&nbsp; -O2&nbsp; : 进一步的优化，包括了调度。（若要优化，该选项最适合，它是GNU发布软件的默认优化级别；<br>
&nbsp;&nbsp;&nbsp; -O3&nbsp; : 鸡肋，兴许使程序速度更慢；<br>
&nbsp;&nbsp;&nbsp; -funroll-loops&nbsp; : 展开循环，会使可执行文件增大，而速度是否增加取决于特定环境；<br>
&nbsp;&nbsp;&nbsp; -Os&nbsp; : 生成最小执行文件；<br>
<br>
一般来说，调试时不优化，一般的优化选项用-O2（gcc允许-g与-O2联用，这也是GNU软件包发布的默认选项），embedded可以考虑-Os。</p>
<p>注意：此处为O！（非0或小写的o,-o是指定可执行文件名）。<br>
<br>
检验优化结果的方法：$ <span style="COLOR: rgb(0,1,255)">time ./prog</span></p>
<p>time测量指定程序的执行时间，结果由三部分组成：<br>
&nbsp;&nbsp;&nbsp; real : 进程总的执行时间, 它和系统负载有关(包括了进程调度,切换的时间)<br>
&nbsp;&nbsp;&nbsp; user: 被测量进程中用户指令的执行时间<br>
&nbsp;&nbsp;&nbsp; sys&nbsp; : 被测量进程中内核代用户指令执行的时间</p>
<p>user和sys的和被称为CPU时间.</p>
<p>注意：对代码的优化可能会引发警告信息，移出警告的办法不是关闭优化，而是调整代码。</p>
</div> ]]></description>
		<eb:creationDate>2008-04-03 11:24:32</eb:creationDate>
		<eb:modificationDate></eb:modificationDate>
    </item>
    <item>
		<title><![CDATA[ 加密技术术语 ]]></title>
		<link>http://blog.tom.com/tigerkings941220/article/9332.html</link>
		<description><![CDATA[ <div id="fontzoom">
<p>2.1.1　数据加密标准<br>
　　<br>
　　数据加密标准（Data Encryption Standard，DES）是美国国家标准局于1977年开发的对称密钥算法。它是一种对称密钥算法，可以使用40~56位长的密钥。另一种称为3DES的加密策略也使用同样的DES算法，但它并不只是加密一次，而是先加密一次，再加密（解密）一次，最后做第三次加密，每一次加密都使用不同的密钥。这一过程显著地增加了解密数据的难度。<br>
　　<br>
　　2.1.2　RSA算法<br>
　　<br>
　　RSA是一种根据它的发明者Rivest，Shamir和Adleman命名的公开密钥算法。它方便了对称密钥加密中的密钥交换过程。它还可以用来产生数字签名。<br>
　　<br>
　　2.1.3　Diffie-Hellman<br>
　　<br>
　　Diffie-Hellman是一种简化在非安全网络上交换秘密密钥的公开密钥加密算法。算法是以Diffie和Hellman命名的，他们在1977年出版了第一个公开密钥加密的公开搜索。它的主要目的是简化在不安全网络上交换秘密会话密钥的过程。<br>
　　<br>
　　2.1.4　散列函数<br>
　　<br>
　　散列（Hash）函数是一种单向的算法，它提取任意长度的一段信息并产生固定长度的乱序的摘要。摘要是文本的一个截取，只包含与文本最相关的部分，这就是Hash函数所产生的结果。Hash是一种单向的定义。我们可以在给定的文本上运行算法来获得固定长度的Hash值，但不能从Hash过程中获得最初的文本。Hash函数可以唯一地定义文本。它对每个唯一的消息而言就像是指纹一样。不同的消息可以产生不同的值，并且相同的消息会产生完全相同的Hash，Hash值可以用于维持数据的完整性。如果A发送一个消息给B，并给B消息的Hash。B可以在消息上运行用在该消息上的同一Hash算法，并用计算得到的Hash值与B收到的Hash值相比较。如果发现根据消息计算出来的Hash与B收到的Hash值不同，就可以知道数据在传输的过程中出错了。<br>
　　<br>
　　2.1.5　消息摘要：MD5<br>
　　<br>
　　消息摘要（Message Digest 5，MD5）是一种符合工业标准的单向128位的Hash算法，由RSA Data Security Inc.开发，它可以从一段任意长的消息中产生一个128位的Hash值。（例如，质询握手身份验证协议 （CHAP）通过使用MD5来协商一种加密身份验证的安全形式。CHAP在响应时使用质询－响应机制和单向MD5散列。用这种方法，用户可以向服务器证明自己知道密码，但不必实际将密码发送到网络上，从而保证了密码本身的安全性。）<br>
　　<br>
　　2.1.6　安全散列算法：SHA-1<br>
　　<br>
　　Secure Hash Algorithm（SHA-1）是一种产生160位Hash值的单向Hash算法。它类似于MD5，但安全性比MD5更高。<br>
　　<br>
　　2.1.7　Hash信息验证码HMAC（Hash message authentication codes）<br>
　　<br>
　　HMAC可以用来验证接收消息和发送消息的完全一致性（完整性）。<br>
　　<br>
　　2.1.8　数字签名<br>
　　<br>
　　数字签名标准（Digital Signature Standard，DSS）是国家标准技术研究所开发的数字签名算法。数字签名（Digital Signature，DS）是一种用使用者的私有密钥加密的Hash值。它可以像正常签名一样用于认证，但它也可以用做与签名相关的信息完整性的认证。<br>
　　<br>
　　2.1.9　认证授权<br>
　　<br>
　　认证授权（Certificate Authority，CA）是一个实体，通常是一台计算机，它保存了一些公开密钥。事实上，它保存的一些称为认证的目标包含了用户或设备的信息，其中包括它们的公开密钥。认证包含某个公开密钥的所有者的认证信息，如：姓名、地址、公司等。认证的目的有两个：<br>
　　<br>
　　●标志一个用户或设备的公开密钥。<br>
　　<br>
　　●确认假设的公开密钥的拥有者是公开密钥的真实拥有者。<br>
　　<br>
　　认证授权（CA）是一个记录了所有认证的第三方。使用CA，用户可以有一个认证的集中贮藏处，它允许用户获得其他用户的公开密钥并认证那些用户。它提供了一个记录用户和设备公开密钥的集中存放点。它还提供了认证授权，当用户从CA中获得用户A的公开密钥时，密钥确实是用户A的而不是其他人的。<br>
　　<br>
　　2.1.10　OAKLEY密钥决定协议<br>
　　<br>
　　Hilarie Orman提出的"OAKLEY密钥决定协议"，Oakley和SKEME各自定义了一种建立已认证密钥交换的方法，包括载荷的结构、所承载载荷信息、处理它们的顺序以及如何使用它们。Oakley描述了一系列的密钥交换模式，提供密钥交换和刷新功能。<br>
　　<br>
　　2.1.11　SKEME: Secure Key Exchange Mechanism<br>
　　<br>
　　Hugo Krawczik提出的"安全密钥交换机制(SKEME)"SKEME描述了通用密钥交换技术，提供匿名性、防抵赖和快速刷新。<br>
　　<br>
　　2.1.12　互连网安全联盟及密钥管理协议<br>
　　<br>
　　互连网安全联盟及密钥管理协议（Internet Security Association and Key Management Protocol，ISAKMP）是一个定义在主机之间交换密钥和协商安全参数的框架。ISAKMP定义密钥在非安全网络上交换的一般机制，ISAKMP定义的信息包括报文中消息的位置和通信过程发生的机制，但它不指明使用的协议和算法。<br>
　　<br>
　　2.1.13　互连网密钥交换<br>
　　<br>
　　互连网密钥交换（Internet Key Exchange，IKE）是一种实现密钥交换定义的协议。IKE是一种在ISAKMP框架下运行的协议。它是从其他密钥交换协议OaKley和SKEME中派生而来的。IKE用于在对等端之间认证密钥并在它们之间建立共享的安全策略。IKE用于确认，譬如标志一个要求加密会话的对等端，以及决定对等端使用何种算法和何种密钥。<br>
　　<br>
　　IKE考虑了IPSec使用的所有算法交换秘密密钥的复杂性。通过将密钥管理函数替代普通的密钥管理协议，简化了将新的算法添加进IPSec协议栈的过程。IKE使用公开密钥加密算法在非安全网络上安全地交换秘密密钥。<br>
　　<br>
　　2.1.14　Kerberos V5身份验证<br>
　　<br>
　　Kerberos V5是用于处理用户和系统身份的身份验证的Internet标准安全协议，是在Windows 2000 Server域中进行身份验证的主要安全协议。使用Kerberos V5，通过网络线路所发送的密码将经过加密而不作为纯文本进行发送。Kerberos V5协议校验了用户的身份和网络服务。这种双重验证被称为相互身份验证。</p>
</div>
<div align="right">责任编辑 webmaster</div> ]]></description>
		<eb:creationDate>2008-04-03 11:18:19</eb:creationDate>
		<eb:modificationDate></eb:modificationDate>
    </item>
    <item>
		<title><![CDATA[ GCC - 一切从这里开始 ]]></title>
		<link>http://blog.tom.com/tigerkings941220/article/9331.html</link>
		<description><![CDATA[ <p>来源：<a href="http://blog.csdn.net/wl076" target="_blank">http://blog.csdn.net/wl076</a></p>
<p>&nbsp;</p>
<div id="fontzoom">
<p>你能想象使用封闭源代码的私有编译器编译自由软件吗？你怎么知道编译器在你的可执行文件中加入了什么？可能会加入各种后门和木马。Ken Thompson是一个著名的黑客，他编写了一个编译器，当编译器编译自己时，就在'login'程序中留下后门和永久的木马。请到 <a href="http://www.acm.org/classics/sep95"><u>这里</u></a> 阅读他对这个杰作的描述。幸运的是，我们有了gcc。当你进行 <font face="新宋体">configure; make; make install</font> 时， gcc在幕后做了很多繁重的工作。如何才能让gcc为我们工作呢？我们将开始编写一个纸牌游戏，不过我们只是为了演示编译器的功能，所以尽可能地精简了代 码。我们将从头开始一步一步地做，以便理解编译过程，了解为了制作可执行文件需要做些什么，按什么顺序做。我们将看看如何编译C程序，以及如何使用编译选 项让gcc按照我们的要求工作。步骤（以及所用工具）如下： <a href="http://www.linuxfocus.org/ChineseGB/March2002/article229.shtml#precomp"><u>预编译</u></a> (gcc -E)， <a href="http://www.linuxfocus.org/ChineseGB/March2002/article229.shtml#comp"><u>编译</u></a> (gcc)， <a href="http://www.linuxfocus.org/ChineseGB/March2002/article229.shtml#assem"><u>汇编</u></a> (as)，和 <a href="http://www.linuxfocus.org/ChineseGB/March2002/article229.shtml#link"><u>连接</u></a> (ld)。</p>
<h2>开始...</h2>
<p>首先，我们应该知道如何调用编译器。实际上，这很简单。我们将从那个著名的第一个C程序开始。（各位老前辈，请原谅我）。</p>
＃i nclude &lt;stdio.h&gt;<br>
<br>
int main()<br>
<br>
{<br>
printf("Hello World!<br>
");<br>
}<br>
<p>把这个文件保存为 <font face="新宋体">game.c</font>。 你可以在命令行下编译它：</p>
gcc game.c<br>
<p>在默认情况下，C编译器将生成一个名为 <font face="新宋体">a.out</font> 的可执行文件。你可以键入如下命令运行它：</p>
a.out<br>
<strong>Hello World</strong><br>
<br>
<p>每一次编译程序时，新的 <font face="新宋体">a.out</font> 将覆盖原来的程序。你无法知道是哪个程序创建了 <font face="新宋体">a.out</font>。我们可以通过使用 <font face="新宋体">-o</font> 编译选项，告诉 gcc我们想把可执行文件叫什么名字。我们将把这个程序叫做 <font face="新宋体">game</font>，我们可以使用任何名字，因为C没有Java那样的命名限制。</p>
gcc -o game game.c<br>
game<br>
<strong>Hello World</strong><br>
<br>
<p>到现在为止，我们离一个有用的程序还差得很远。如果你觉得沮丧，你可以想一想我们已经编译并运行了一个程序。因为我们将一点一点为这个程序添加功 能，所以我们必须保证让它能够运行。似乎每个刚开始学编程的程序员都想一下子编一个1000行的程序，然后一次修改所有的错误。没有人，我是说没有人，能 做到这个。你应该先编一个可以运行的小程序，修改它，然后再次让它运行。这可以限制你一次修改的错误数量。另外，你知道刚才做了哪些修改使程序无法运行， 因此你知道应该把注意力放在哪里。这可以防止这样的情况出现：你认为你编写的东西应该能够工作，它也能通过编译，但它就是不能运行。请切记，能够通过编译 的程序并不意味着它是正确的。</p>
<p>下一步为我们的游戏编写一个头文件。头文件把数据类型和函数声明集中到了一处。这可以保证数据结构定义的一致性，以便程序的每一部分都能以同样的方式看待一切事情。</p>
#ifndef DECK_H<br>
#define DECK_H<br>
<br>
#define DECKSIZE 52<br>
<br>
typedef struct deck_t<br>
{<br>
int card[DECKSIZE];<br>
/* number of cards used */<br>
int dealt;<br>
}deck_t;<br>
<br>
#endif /* DECK_H */<br>
<p>把这个文件保存为 <font face="新宋体">deck.h</font>。只能编译 <font face="新宋体">.c</font> 文件，所以我们必须修改 game.c。在game.c的第2行，写上 <font face="新宋体">＃i nclude "deck.h"</font>。在第5行写上 <font face="新宋体">deck_t deck;</font>。为了保证我们没有搞错，把它重新编译一次。</p>
gcc -o game game.c<br>
<p>如果没有错误，就没有问题。如果编译不能通过，那么就修改它直到能通过为止。</p>
<h2><a>预编译</a></h2>
<p>编译器是怎么知道 <font face="新宋体">deck_t</font> 类型是什么的呢？因为在预编译期间，它实际上把"deck.h"文件复制到了"game.c"文件中。源代码中的预编译指示以"#"为前缀。你可以通过在gcc后加上 <font face="新宋体">-E</font> 选项来调用预编译器。</p>
gcc -E -o game_precompile.txt game.c<br>
wc -l game_precompile.txt<br>
3199 game_precompile.txt<br>
<p>几乎有3200行的输出！其中大多数来自 <font face="新宋体">stdio.h</font> 包含文件，但是如果你查看这个文件的话，我们的声明也在那里。如果你不用 <font face="新宋体">-o</font> 选项指定输出文件名的话，它就输出到控制台。预编译过程通过完成三个主要任务给了代码很大的灵活性。</p>
<ol>
<li>把"include"的文件拷贝到要编译的源文件中。</li>
<li>用实际值替代"define"的文本。</li>
<li>在调用宏的地方进行宏替换。</li>
</ol>
<p>这就使你能够在整个源文件中使用符号常量（即用DECKSIZE表示一付牌中的纸牌数量），而符号常量是在一个地方定义的，如果它的值发生了变化，所有使用符号常量的地方都能自动更新。在实践中，你几乎不需要单独使用 <font face="新宋体">-E</font> 选项，而是让它把输出传送给编译器。</p>
<h2><a>编译</a></h2>
<p>作为一个中间步骤，gcc把你的代码翻译成汇编语言。它一定要这样做，它必须通过分析你的代码搞清楚你究竟想要做什么。如果你犯了语法错误，它就会告诉你，这样编译就失败了。人们有时会把这一步误解为整个过程。但是，实际上还有许多工作要gcc去做呢。</p>
<h2><a>汇编</a></h2>
<p><font face="新宋体">as</font> 把汇编语言代码转换为目标代码。事实上目标代码并不能在CPU上运行，但它离完成已经很近了。编译器选项 <font face="新宋体">-c</font> 把 .c 文件转换为以 .o 为扩展名的目标文件。 如果我们运行</p>
gcc -c game.c<br>
<p>我们就自动创建了一个名为game.o的文件。这里我们碰到了一个重要的问题。我们可以用任意一个 .c 文件创建一个目标文件。正如我们在下面所看到的，在连接步骤中我们可以把这些目标文件组合成可执行文件。让我们继续介绍我们的例子。因为我们正在编写一个 纸牌游戏，我们已经把一付牌定义为 <font face="新宋体">deck_t</font>，我们将编写一个洗牌函数。这个函数接受一个指向deck类型的指针，并把一付随机的牌装入deck类型。它使用'drawn' 数组跟踪记录那些牌已经用过了。这个具有DECKSIZE个元素的数组可以防止我们重复使用一张牌。</p>
＃i nclude &lt;stdlib.h&gt;<br>
＃i nclude &lt;stdio.h&gt;<br>
＃i nclude &lt;time.h&gt;<br>
＃i nclude "deck.h"<br>
<br>
static time_t seed = 0;<br>
<br>
void shuffle(deck_t *pdeck)<br>
{<br>
/* Keeps track of what numbers have been used */<br>
int drawn[DECKSIZE] = {0};<br>
int i;<br>
<br>
/* One time initialization of rand */<br>
if(0 == seed)<br>
{<br>
seed = time(NULL);<br>
srand(seed);<br>
}<br>
for(i = 0; i &lt; DECKSIZE; i++)<br>
{<br>
int value = -1;<br>
do<br>
{<br>
value = rand() % DECKSIZE;<br>
}<br>
while(drawn[value] != 0);<br>
<br>
/* mark value as used */<br>
drawn[value] = 1;<br>
<br>
/* debug statement */<br>
printf("%i<br>
", value);<br>
pdeck-&gt;card[i] = value;<br>
}<br>
pdeck-&gt;dealt = 0;<br>
return;<br>
}<br>
<p>把这个文件保存为 <font face="新宋体">shuffle.c</font>。我们在这个代码中加入了一条 调试语句，以便运行时，能输出所产生的牌号。这并没有为我们的程序添加功能，但是现在到了关键时刻，我们看看究竟发生了什么。因为我们的游戏还在初级阶 段，我们没有别的办法确定我们的函数是否实现了我们要求的功能。使用那条printf语句，我们就能准确地知道现在究竟发生了什么，以便在开始下一阶段之 前我们知道牌已经洗好了。在我们对它的工作感到满意之后，我们可以把那一行语句从代码中删掉。这种调试程序的技术看起来很粗糙，但它使用最少的语句完成了 调试任务。以后我们再介绍更复杂的调试器。</p>
<p>请注意两个问题。</p>
<ol>
<li>我们用传址方式传递参数，你可以从'&amp;'（取地址）操作符看出来。这把变量的机器地址传递给了函数，因此函数自己就能改变变量的值。也可以使用全局变量编写程序，但是应该尽量少使用全局变量。指针是C的一个重要组成部分，你应该充分地理解它。</li>
<li>我们在一个新的 .c 文件中使用函数调用。操作系统总是寻找名为'main'的函数，并从那里开始执行。 <font face="新宋体">shuffle.c</font> 中没有'main'函数，因此不能编译为独立的可执行文件。我们必须把它与另一个具有'main'函数并调用'shuffle'的程序组合起来。</li>
</ol>
<p>运行命令</p>
gcc -c shuffle.c<br>
<p>并确定它创建了一个名为 <font face="新宋体">shuffle.o</font> 的新文件。编辑game.c文件，在第7行，在 deck_t类型的变量 <font face="新宋体">deck</font> 声明之后，加上下面这一行：</p>
shuffle(&amp;deck);<br>
<p>现在，如果我们还象以前一样创建可执行文件，我们就会得到一个错误</p>
gcc -o game game.c<br>
<br>
/tmp/ccmiHnJX.o: In function `main':<br>
/tmp/ccmiHnJX.o(.text+0xf): undefined reference to `shuffle'<br>
collect2: ld returned 1 exit status<br>
<p>编译成功了，因为我们的语法是正确的。但是连接步骤却失败了，因为我们没有告诉编译器'shuffle'函数在哪里。那么，到底什么是连接？我们怎样告诉编译器到哪里寻找这个函数呢？</p>
<h2><a>连接</a></h2>
<p>连接器<font face="新宋体">ld</font>，使用下面的命令，接受前面由 <font face="新宋体">as</font> 创建的目标文件并把它转换为可执行文件</p>
gcc -o game game.o shuffle.o<br>
<p>这将把两个目标文件组合起来并创建可执行文件 <font face="新宋体">game</font>。</p>
<p>连接器从shuffle.o目标文件中找到 <font face="新宋体">shuffle</font> 函数，并把它包括进可执行文件。目标文件的真正好处在于，如果我们想再次使用那个函数，我们所要做的就是包含"deck.h" 文件并把 <font face="新宋体">shuffle.o</font> 目标文件连接到新的可执行文件中。</p>
<p>象这样的代码重用是经常发生的。虽然我们并没有编写前面作为调试语句调用的 <font face="新宋体">printf</font> 函数，连接器却能从我们用 <font face="新宋体">＃i nclude &lt;stdlib.h&gt;</font> 语句包含的文件中找到它的声明，并把存储在C库（/lib/libc.so.6）中的目标代码连接进来。这种方式使我们可以使用已能正确工作的其他人的函 数，只关心我们所要解决的问题。这就是为什么头文件中一般只含有数据和函数声明，而没有函数体。一般，你可以为连接器创建目标文件或函数库，以便连接进可 执行文件。我们的代码可能产生问题，因为在头文件中我们没有放入任何函数声明。为了确保一切顺利，我们还能做什么呢？</p>
<h2><a>另外两个重要选项</a></h2>
<p><font face="新宋体">-Wall</font> 选项可以打开所有类型的语法警告，以便帮助我们确定代码是正确的，并且尽可能实现可移植性。当我们使用这个选项编译我们的代码时，我们将看到下述警告：</p>
game.c:9: warning: implicit declaration of function `shuffle'<br>
<p>这让我们知道还有一些工作要做。我们需要在头文件中加入一行代码，以便告诉编译器有关 <font face="新宋体">shuffle</font> 函数的一切，让它可以做必要的检查。听起来象是一种狡辩，但这样做 可以把函数的定义与实现分离开来，使我们能在任何地方使用我们的函数，只要包含新的头文件 并把它连接到我们的目标文件中就可以了。下面我们就把这一行加入deck.h中。</p>
void shuffle(deck_t *pdeck);<br>
<p>这就可以消除那个警告信息了。</p>
<p>另一个常用编译器选项是优化选项 <font face="新宋体">-O#</font> (即 -O2)。 这是告诉编译器你需要什么级别的优化。编译器具有一整套技巧可以使你的代码运行得更快一点。对于象我们这种小程序，你可能注意不到差别，但对于大型程序来说，它可以大幅度提高运行速度。你会经常碰到它，所以你应该知道它的意思。</p>
<h2><a>调试</a></h2>
<p>我们都知道，代码通过了编译并不意味着它按我们得要求工作了。你可以使用下面的命令验证是否所有的号码都被使用了</p>
game | sort - n | less<br>
<p>并且检查有没有遗漏。如果有问题我们该怎么办？我们如何才能深入底层查找错误呢？</p>
<p>你可以使用调试器检查你的代码。大多数发行版都提供著名的调试器：gdb。如果那些众多的命令行选项让你感到无所适从，那么你可以使用KDE提供的一个很好的前端工具 <a href="http://members.nextra.at/johsixt/kdbg.html"><u>KDbg</u></a>。还有一些其它的前端工具，它们都很相似。要开始调试，你可以选择 File-&gt;Executable 然后找到你的 <font face="新宋体">game</font> 程序。当你按下F5键或选择 Execution-&gt;从菜单运行时，你可以在另一个窗口中看到输出。怎么回事？在那个窗口中我们什么也看不到。不要担心，KDbg没有出问题。问 题在于我们在可执行文件中没有加入任何调试信息，所以KDbg不能告诉我们内部发生了什么。编译器选项 <font face="新宋体">-g</font> 可以把必要的调试信息加入目标文件。你必须用这个选项编译目标文件（扩展名为.o），所以命令行成了：</p>
gcc -g -c shuffle.c game.c<br>
gcc -g -o game game.o shuffle.o<br>
<p>这就把钩子放入了可执行文件，使gdb和KDbg能指出运行情况。调试是一种很重要的技术，很值得你花时间学习如何使用。调试器帮助程序员的方法是它能在源代码中设置“断点”。现在你可以用右键单击调用 <font face="新宋体">shuffle</font> 函数的那行代码，试着设置断点。那一行边上会出现一个红色的小圆圈。现在当你按下F5键时，程序就会在那一行停止执行。按F8可以跳入shuffle函数。呵，我们现在可以看到 <font face="新宋体">shuffle.c</font> 中的代码了！我们可以控制程序一步一步地执行，并看到究竟发生了什么事。如果你把光标暂停在局部变量上，你将能看到变量的内容。太好了。这比那条 <font face="新宋体">printf</font> 语句好多了，是不是？</p>
<h2>小结</h2>
<p>本文大体介绍了编译和调试C程序的方法。我们讨论了编译器走过的步骤，以及为了让编译器做这些工作应该给gcc传递哪些选项。我们简述了有关连接共享函数库的问题，最后介绍了调试器。真正了解你所从事的工作还需要付出许多努力，但我希望本文能让你正确地起步。你可以在 <font face="新宋体">gcc</font>、 <font face="新宋体">as</font> 和 <font face="新宋体">ld</font>的 <font face="新宋体">man</font> 和 <font face="新宋体">info</font> page中找到更多的信息。</p>
<p>自己编写代码可以让你学到更多的东西。作为练习你可以以本文的纸牌游戏为基础，编写一个21点游戏。那时你可以学学如何使用调试器。使用GUI的KDbg开始可以更容易一些。如果你每次只加入一点点功能，那么很快就能完成。切记，一定要保持程序一直能运行！</p>
<p>要想编写一个完整的游戏，你需要下面这些内容：</p>
<ul>
<li>一个纸牌玩家的定义（即，你可以把deck_t定义为player_t）。</li>
<li>一个给指定玩家发一定数量牌的函数。记住在纸牌中要增加“已发牌”的数量，以便能知道还有那些牌可发。还要记住玩家手中还有多少牌。</li>
<li>一些与用户的交互，问问玩家是否还要另一张牌。</li>
<li>一个能打印玩家手中的牌的函数。 <em>card</em> 等于value % 13 （得数为0到12），<em>suit</em> 等于 value / 13 （得数为0到3）。</li>
<li>一个能确定玩家手中的value的函数。Ace的value为零并且可以等于1或11。King的value为12并且可以等于10。</li>
</ul>
</div>
<div align="right">责任编辑 webmaster</div> ]]></description>
		<eb:creationDate>2008-04-03 11:11:13</eb:creationDate>
		<eb:modificationDate></eb:modificationDate>
    </item>
    <item>
		<title><![CDATA[ GCC入门详解 ]]></title>
		<link>http://blog.tom.com/tigerkings941220/article/9330.html</link>
		<description><![CDATA[ <div id="fontzoom">
<div id="art" style="MARGIN: 15px" width="100%">作为自由软件的旗舰项目，Richard Stallman 在十多年前刚开始写作 GCC 的时候，还只是把它当作仅仅一个 C 程序语言的编译器；GCC 的意思也只是 GNU C Compiler 而已。经过了这么多年的发展，GCC 已经不仅仅能支持 C 语言；它现在还支持 Ada 语言、C++ 语言、Java 语言、Objective C 语言、Pascal 语言、COBOL语言，以及支持函数式编程和逻辑编程的 Mercury 语言，等等。而 GCC 也不再单只是 GNU C 语言编译器的意思了，而是变成了 GNU Compiler Collection 也即是 GNU 编译器家族的意思了。另一方面，说到 GCC 对于操作系统平台及硬件平台支持，概括起来就是一句话：无所不在。<br>
<h3>1　程序编译过程</h3>
<hr style="WIDTH: 100%; HEIGHT: 2px">
　　GCC是CUI(命令行交互界面)程序，这让许多从Windows走出来 Guier们感到恐惧。实际上它也有许多前端窗口界面，Windows下有Dev C++，Linux下譬如KDevelopment，但既然选择了GCC还是将CUL进行到底吧，没有难与不难的问题，只有做与不做的问题！<br>
<br>
　　下面基于一个具体而微的程序，讨论GCC的使用。示例程序如下：<br>
<br>
<div style="MARGIN-LEFT: 80px"><span style="COLOR: rgb(0,0,153)">//test.c</span><br style="COLOR: rgb(0,0,153)">
<span style="COLOR: rgb(0,0,153)">#include &lt;stdio.h&gt;</span><br style="COLOR: rgb(0,0,153)">
<span style="COLOR: rgb(0,0,153)">int main(void)</span><br style="COLOR: rgb(0,0,153)">
<span style="COLOR: rgb(0,0,153)">{</span><br style="COLOR: rgb(0,0,153)">
<span style="COLOR: rgb(0,0,153)">&nbsp; printf("Hello World!\n");</span><br style="COLOR: rgb(0,0,153)">
<span style="COLOR: rgb(0,0,153)">&nbsp; return 0;</span><br style="COLOR: rgb(0,0,153)">
<span style="COLOR: rgb(0,0,153)">}</span><br></div>
<div style="MARGIN-LEFT: 80px"><br></div>
　　这个程序，一步到位的编译指令是:<br>
<div style="MARGIN-LEFT: 80px"><span style="COLOR: rgb(0,0,153)">gcc test.c -o test</span><br></div>
<br>
　　输出的可执行文件名为test，Windows用户可能会感到奇怪，可执行文件明怎么没有.exe扩展名呢？Linux系统中，文件类型并非以扩展名识别的！<br>
<br>
　　实质上，上述编译过程是分为四个阶段进行的，即预处理(也称预编译，Preprocessing)、编译(Compilation)、汇编 (Assembly)和连接(Linking)。<br>
<h4>1.1　预处理</h4>
　　运行预处理命令：<br>
<div style="MARGIN-LEFT: 80px; COLOR: rgb(0,0,153)">gcc -E test.c -o test.i　<span style="COLOR: rgb(0,0,0)">或</span>　gcc -E test.c</div>
<br>
可以输出test.i文件中存放着test.c经预处理之后的代码。打开test.i文件，看一看，就明白了。后面那条指令，是直接在命令行窗口中输出预处理后的代码，而不是以文件作为输出设备。gcc的-E选项，可以让编译器在预处理后停止，并输出预处理结果。在本例中，预处理结果就是将stdio.h 文件中的内容插入到test.c中了。<br>
<br>
　　gcc的-o选项，用于输出处理结果到文件中。<br>
<h4>1.2　编译为汇编代码</h4>
　　预处理之后，可直接对生成的test.i文件编译，生成汇编代码：<br>
<div style="MARGIN-LEFT: 80px"><span style="COLOR: rgb(0,0,153)">gcc -S test.i -o test.s</span><br></div>
<br>
　　gcc的-S选项，表示在程序编译期间，在生成汇编代码后，停止，-o输出汇编代码文件。<br>
<br>
　　生成的汇编代码如下：<br>
<div style="MARGIN-LEFT: 80px"><span style="COLOR: rgb(0,0,153)">&nbsp;&nbsp;&nbsp; .file&nbsp;&nbsp;&nbsp; "test.c"</span><br style="COLOR: rgb(0,0,153)">
<span style="COLOR: rgb(0,0,153)">&nbsp;&nbsp;&nbsp; .section&nbsp;&nbsp;&nbsp; .rodata</span><br style="COLOR: rgb(0,0,153)">
<span style="COLOR: rgb(0,0,153)">&nbsp;&nbsp;&nbsp; .align 4</span><br style="COLOR: rgb(0,0,153)">
<span style="COLOR: rgb(0,0,153)">.LC0:</span><br style="COLOR: rgb(0,0,153)">
<span style="COLOR: rgb(0,0,153)">&nbsp;&nbsp;&nbsp; .string&nbsp;&nbsp;&nbsp; "Hello World,Linux programming!"</span><br style="COLOR: rgb(0,0,153)">
<span style="COLOR: rgb(0,0,153)">&nbsp;&nbsp;&nbsp; .text</span><br style="COLOR: rgb(0,0,153)">
<span style="COLOR: rgb(0,0,153)">.globl main</span><br style="COLOR: rgb(0,0,153)">
<span style="COLOR: rgb(0,0,153)">&nbsp;&nbsp;&nbsp; .type&nbsp;&nbsp;&nbsp; main, @function</span><br style="COLOR: rgb(0,0,153)">
<span style="COLOR: rgb(0,0,153)">main:</span><br style="COLOR: rgb(0,0,153)">
<span style="COLOR: rgb(0,0,153)">&nbsp;&nbsp;&nbsp; leal&nbsp;&nbsp;&nbsp; 4(%esp), %ecx</span><br style="COLOR: rgb(0,0,153)">
<span style="COLOR: rgb(0,0,153)">&nbsp;&nbsp;&nbsp; andl&nbsp;&nbsp;&nbsp; $-16, %esp</span><br style="COLOR: rgb(0,0,153)">
<span style="COLOR: rgb(0,0,153)">&nbsp;&nbsp;&nbsp; pushl&nbsp;&nbsp;&nbsp; -4(%ecx)</span><br style="COLOR: rgb(0,0,153)">
<span style="COLOR: rgb(0,0,153)">&nbsp;&nbsp;&nbsp; pushl&nbsp;&nbsp;&nbsp; %ebp</span><br style="COLOR: rgb(0,0,153)">
<span style="COLOR: rgb(0,0,153)">&nbsp;&nbsp;&nbsp; movl&nbsp;&nbsp;&nbsp; %esp, %ebp</span><br style="COLOR: rgb(0,0,153)">
<span style="COLOR: rgb(0,0,153)">&nbsp;&nbsp;&nbsp; pushl&nbsp;&nbsp;&nbsp; %ecx</span><br style="COLOR: rgb(0,0,153)">
<span style="COLOR: rgb(0,0,153)">&nbsp;&nbsp;&nbsp; subl&nbsp;&nbsp;&nbsp; $4, %esp</span><br style="COLOR: rgb(0,0,153)">
<span style="COLOR: rgb(0,0,153)">&nbsp;&nbsp;&nbsp; movl&nbsp;&nbsp;&nbsp; $.LC0, (%esp)</span><br style="COLOR: rgb(0,0,153)">
<span style="COLOR: rgb(0,0,153)">&nbsp;&nbsp;&nbsp; call&nbsp;&nbsp;&nbsp; puts</span><br style="COLOR: rgb(0,0,153)">
<span style="COLOR: rgb(0,0,153)">&nbsp;&nbsp;&nbsp; movl&nbsp;&nbsp;&nbsp; $0, %eax</span><br style="COLOR: rgb(0,0,153)">
<span style="COLOR: rgb(0,0,153)">&nbsp;&nbsp;&nbsp; addl&nbsp;&nbsp;&nbsp; $4, %esp</span><br style="COLOR: rgb(0,0,153)">
<span style="COLOR: rgb(0,0,153)">&nbsp;&nbsp;&nbsp; popl&nbsp;&nbsp;&nbsp; %ecx</span><br style="COLOR: rgb(0,0,153)">
<span style="COLOR: rgb(0,0,153)">&nbsp;&nbsp;&nbsp; popl&nbsp;&nbsp;&nbsp; %ebp</span><br style="COLOR: rgb(0,0,153)">
<span style="COLOR: rgb(0,0,153)">&nbsp;&nbsp;&nbsp; leal&nbsp;&nbsp;&nbsp; -4(%ecx), %esp</span><br style="COLOR: rgb(0,0,153)">
<span style="COLOR: rgb(0,0,153)">&nbsp;&nbsp;&nbsp; ret</span><br style="COLOR: rgb(0,0,153)">
<span style="COLOR: rgb(0,0,153)">&nbsp;&nbsp;&nbsp; .size&nbsp;&nbsp;&nbsp; main, .-main</span><br style="COLOR: rgb(0,0,153)">
<span style="COLOR: rgb(0,0,153)">&nbsp;&nbsp;&nbsp; .ident&nbsp;&nbsp;&nbsp; "GCC: (GNU) 4.1.0 20060304 (Red Hat 4.1.0-3)"</span><br style="COLOR: rgb(0,0,153)">
<span style="COLOR: rgb(0,0,153)">&nbsp;&nbsp;&nbsp; .section&nbsp;&nbsp;&nbsp; .note.GNU-stack,"",@progbits</span><br></div>
<h4>1.3　汇编(Assembly)</h4>
　　如果你学过汇编语言，那么你就该知道程序编译到了这个地步，应当使用汇编器，将汇编语言翻译为机器代码了。这一步尤其重要，因为它决定了你生成的程序，能够运行在哪种机器上。gcc使用的汇编器是gas。<br>
<br>
　　在Intel IA-32平台上，还有一些常用的汇编器有：<br>
<ul>
<li>微软的MASM，这是Intel平台上所有汇编器的鼻祖了，它现在已不是微软的独立产品，只是与Visual Studio捆绑在一起。但微软允许其他组织免费分发MASM 6.0。</li>
<li>NASM，最初是为UNIX环境开发的商业汇编器，最近成为开源的了，可生成UNIX、MS-DOS和32位Windows格式的可执行文件。</li>
<li>HLA（high level assembler）是Randall Hyde教授创建的，可以在DOS、Windows和Linux操作系统上生成Intel指令码。但HLA设计的主要目的是向初级程序员讲授汇编语言，学院气太浓，不够实用。</li>
</ul>
　　与这些汇编器相比，gas可以在不同处理器平台上工作，通常它可以自动检测底层硬件平台并生成适合该平台的正确机器指令码。gas另一个特性是能够创建不同于程序设计所在平台的指令码，譬如我在Intel计算机上工作，但可以为MIPS计算机写程序。<br>
<br>
　　对于上一小节中生成的汇编代码文件test.s，gas汇编器负责将其编译为目标文件，如下：<br>
<div style="MARGIN-LEFT: 80px"><span style="COLOR: rgb(0,0,153)">gcc -c test.s -o test.o</span><br>
<br></div>
<h4>1.4　连接</h4>
　　gcc连接器是gas提供的，负责将程序的目标文件与所需的所有附加的目标文件连接起来，最终生成可执行文件。附加的目标文件包括静态连接库和动态连接库。<br>
<br>
　　对于上一小节中生成的test.o，将其与Ｃ标准输入输出库进行连接，最终生成程序test：<br>
<div style="MARGIN-LEFT: 80px"><span style="COLOR: rgb(0,0,153)">gcc test.o -o test</span><br>
<br></div>
　　在命令行窗口中，运行test这个小程序，让它说HelloWorld吧！<br>
<br>
<h3>2、多个程序文件的编译</h3>
<hr style="WIDTH: 100%; HEIGHT: 2px">
<br>
　　通常整个程序是由多个源文件组成的，相应地也就形成了多个编译单元，使用GCC能够很好地管理这些编译单元。假设有一个由test1.c和 test2.c两个源文件组成的程序，为了对它们进行编译，并最终生成可执行程序test，可以使用下面这条命令：<br>
<div style="MARGIN-LEFT: 80px"><span style="COLOR: rgb(0,0,153)"># gcc test1.c test2.c -o test</span><br></div>
<br>
　　如果同时处理的文件不止一个，GCC仍然会按照预处理、编译和链接的过程依次进行。如果深究起来，上面这条命令大致相当于依次执行如下三条命令：<br style="COLOR: rgb(0,0,153)">
<div style="MARGIN-LEFT: 80px"><span style="COLOR: rgb(0,0,153)"># gcc -c test1.c -o test1.o</span><br style="COLOR: rgb(0,0,153)">
<span style="COLOR: rgb(0,0,153)"># gcc -c test2.c -o test2.o</span><br style="COLOR: rgb(0,0,153)">
<span style="COLOR: rgb(0,0,153)"># gcc test1.o test2.o -o test</span><br>
<br></div>
　　需要打这么多编译指令，看着都累，许多Guier们又要抱怨了。的确如此，如果单单使用GCC来编译你的程序，一千个程序源文件的项目编译至少要在命令行窗口中敲1k次文件名，才能完成一次编译。如果代码有了改动，重新编译，需要再原样输入一次编译指令。再技术高超的Cler也会累死的，但是很奇怪，那些Cler们至今依然活的很生龙活虎，这得益于GNU Make工具，详情见<span style="COLOR: rgb(255,0,0)">Make基础</span>一节。<br>
<h3>3、检错</h3>
<hr style="WIDTH: 100%; HEIGHT: 2px">
<br>
　　GCC包含完整的出错检查和警告提示功能，可以帮助程序员写出更为标准、健壮的代码。如下面的代码：<br>
<div style="MARGIN-LEFT: 80px"><span style="COLOR: rgb(0,0,153)">//illcode.c</span><br style="COLOR: rgb(0,0,153)">
<span style="COLOR: rgb(0,0,153)">#include &lt;stdio.h&gt;</span><br style="COLOR: rgb(0,0,153)">
<span style="COLOR: rgb(0,0,153)">void main(void)</span><br style="COLOR: rgb(0,0,153)">
<span style="COLOR: rgb(0,0,153)">{</span><br style="COLOR: rgb(0,0,153)">
<span style="COLOR: rgb(0,0,153)">&nbsp; long long int var = 1;</span><br style="COLOR: rgb(0,0,153)">
<span style="COLOR: rgb(0,0,153)">&nbsp; printf("It is not standard C code!\n");</span><br style="COLOR: rgb(0,0,153)">
<span style="COLOR: rgb(0,0,153)">&nbsp; printf("long long int var=%d",var);</span><br style="COLOR: rgb(0,0,153)">
<span style="COLOR: rgb(0,0,153)">}</span><br>
<br></div>
　　这种代码，可能在老的Ｃ语言课本里能够见到，但它是不符合ANSI/ISO C语言标准的。我让同学在Visual Stdio .net 2003上编译了一下，没检测出什么问题来。下面看看GCC可不可以：<br>
<div style="MARGIN-LEFT: 80px"><span style="COLOR: rgb(0,0,153)">gcc -pedantic illcode.c -o illcode</span><br>
<br></div>
　　输出结果：<br>
<div style="MARGIN-LEFT: 80px"><span style="COLOR: rgb(0,0,153)">illcode.c: 在函数 ‘main’ 中：</span><br style="COLOR: rgb(0,0,153)">
<span style="COLOR: rgb(0,0,153)">illcode.c:5: 警告：ISO C90 不支持 ‘long long’</span><br style="COLOR: rgb(0,0,153)">
<span style="COLOR: rgb(0,0,153)">illcode.c:4: 警告：‘main’ 的返回类型不是 ‘int’</span><br>
<br></div>
　　-pedantic编译选项并不能保证被编译程序与ANSI/ISO C标准的完全兼容，它仅仅只能用来帮助Linux程序员离这个目标越来越近。或者换句话说，-pedantic选项能够帮助程序员发现一些不符合 ANSI/ISO C标准的代码，但不是全部，事实上只有ANSI/ISO C语言标准中要求进行编译器诊断的那些情况，才有可能被GCC发现并提出警告。<br>
<br>
　　如果采用默认的编译，即：<span style="COLOR: rgb(0,0,153)">gcc -pedantic illcode.c -o illcode</span>。输出：<br>
<div style="MARGIN-LEFT: 80px"><span style="COLOR: rgb(0,0,153)">test.c: 在函数 ‘main’ 中：</span><br style="COLOR: rgb(0,0,153)">
<span style="COLOR: rgb(0,0,153)">test.c:4: 警告：‘main’ 的返回类型不是 ‘int’</span><br>
<br></div>
　　上面的示例中，long long int是GNU C的扩展类型，表示64位整型数，这种类型没有纳入C/C++标准中，可见GCC默认的编译指令，无法完全检测出不符合标准C/C++的代码，但要比 Visual Stdio .net 2003一声都不吭要好一些。如果使用-pedantic选项，GCC就可以基本上按照标准C/C++进行代码检测了，不要挑剔什么，迄今为止没有任何一款编译器完全支持标准C/C++的。<br>
　　<br>
　　除了-pedantic之外，GCC还有一些其它编译选项也能够产生有用的警告信息。这些选项大多以-W开头，其中最有价值的当数-Wall了，使用它能够使GCC产生尽可能多的警告信息。<br>
<br>
　　GCC给出的警告信息虽然从严格意义上说不能算作错误，但却很可能成为错误的栖身之所。一个优秀的Linux程序员应该尽量避免产生警告信息，使自己的代码始终保持标准、健壮的特性。所以将警告信息当成编码错误来对待，是一种值得赞扬的行为！所以，在编译程序时带上-Werror选项，那么GCC会在所有产生警告的地方停止编译，迫使程序员对自己的代码进行修改，如下：<br>
<div style="MARGIN-LEFT: 80px"><span style="COLOR: rgb(0,0,153)">gcc -Werror test.c -o test</span><br></div>
　<br>
　　输出：<br>
<div style="MARGIN-LEFT: 80px"><span style="COLOR: rgb(0,0,153)">cc1: warnings being treated as errors</span><br style="COLOR: rgb(0,0,153)">
<span style="COLOR: rgb(0,0,153)">test.c: 在函数 ‘main’ 中：</span><br style="COLOR: rgb(0,0,153)">
<span style="COLOR: rgb(0,0,153)">test.c:4: 警告：‘main’ 的返回类型不是 ‘int’<br>
<br></span></div>
<h3>4、库文件连接</h3>
<hr style="WIDTH: 100%; HEIGHT: 2px">
<br>
　　人家已经发明了轮子，而且物美价廉，那么我们就实在没有必要浪费生命再去发明同样的轮子！开发软件时，完全不使用第三方函数库的情况是比较少见的，通常来讲都需要借助许多函数库的支持才能够完成相应的功能。从程序员的角度看，函数库实际上就是一些头文件（.h）和库文件（so、或lib、dll）的集合。虽然Linux下的大多数函数都默认将头文件放到/usr/include/目录下，而库文件则放到/usr/lib/目录下；Windows所使用的库文件主要放在Visual Stido的目录下的include和lib，以及系统文件夹下。但也有的时候，我们要用的库不再这些目录下，所以GCC在编译时必须用自己的办法来查找所需要的头文件和库文件。<br>
<br>
　　GCC采用搜索目录的办法来查找所需要的文件，-I选项可以向GCC的头文件搜索路径中添加新的目录。例如，如果在 /home/lyanry/include/目录下有编译时所需要的头文件，为了让GCC能够顺利地找到它们，就可以使用-I选项：<br>
<div style="MARGIN-LEFT: 80px"><span style="COLOR: rgb(0,0,153)"># gcc test.c -I /home/lyanry/include -o test</span><br></div>
<br>
　　同样，如果使用了不在标准位置的库文件，那么可以通过-L选项向GCC的库文件搜索路径中添加新的目录。例如，如果在 /home/lyanry/lib/目录下有链接时所需要的库文件libtest.so，为了让GCC能够顺利地找到它，可以使用下面的命令：<br>
<div style="MARGIN-LEFT: 80px"><span style="COLOR: rgb(0,0,153)"># gcc test.c -L /home/lyanry/lib -ltest -o test<br></span></div>
<span style="COLOR: rgb(0,0,0)"><br></span>　　上面这条命令中，值得好好解释一下的是-l选项，它指示GCC去连接库文件libfoo.so。Linux下的库文件在命名时有一个约定，那就是应该以lib三个字母开头，由于所有的库文件都遵循了同样的规范，因此在用-l选项指定链接的库文件名时可以省去lib三个字母，也就是说GCC在对-lfoo进行处理时，会自动去链接名为libfoo.so的文件。<span style="COLOR: rgb(255,0,0)">（注：至于在Windows下该怎样连接库文件，未做尝试，以后再谈）</span><br>
<br>
　　Linux下的库文件分为两大类分别是动态链接库（通常以.so结尾）和静态链接库（通常以.a结尾），二者的区别仅在于程序执行时所需的代码是在运行时动态加载的，还是在编译时静态加载的。动态加载，意味着内存中仅存在一份库代码，所调用的函数只是在调用程序中存在一个映像。而静态加载，意味着将库中所调用的函数代码复制到调用程序中。如果库中存在同名的静态库和动态库，则在默认情况下， GCC在链接时优先使用动态链接库，只有当动态链接库不存在时才考虑使用静态链接库，如果需要的话可以在编译时加上-static选项，强制使用静态链接库。例如，如果在 /home/xiaowp/lib/目录下有链接时所需要的库文件libtest.so和libtest.a，为了让GCC在链接时只用到静态链接库，可以使用下面的命令：<br>
<div style="MARGIN-LEFT: 80px"><span style="COLOR: rgb(0,0,153)"># gcc test.c -L /home/xiaowp/lib -static -ltest -o test<br></span></div>
<br>
<h3>5、优化</h3>
<hr style="WIDTH: 100%; HEIGHT: 2px">
<br>
　　代码优化指的是编译器通过分析源代码，找出其中尚未达到最优的部分，然后对其重新进行组合，目的是改善程序的执行性能。GCC 提供的代码优化功能非常强大，它通过编译选项-On来控制优化代码的生成，其中n是一个代表优化级别的整数。对于不同版本的GCC来讲，n的取值范围及其对应的优化效果可能并不完全相同，比较典型的范围是从0变化到2或3。<br>
<br>
　　编译时使用选项-O可以告诉GCC同时减小代码的长度和执行时间，其效果等价于-O1。在这一级别上能够进行的优化类型虽然取决于目标处理器，但一般都会包括线程跳转（Thread Jump）和延迟退栈（Deferred Stack Pops）两种优化。选项-O2告诉GCC除了完成所有-O1级别的优化之外，同时还要进行一些额外的调整工作，如处理器指令调度等。选项-O3则除了完成所有-O2级别的优化之外，还包括循环展开和其它一些与处理器特性相关的优化工作。通常来说，数字越大优化的等级越高，同时也就意味着程序的运行速度越快。许多Linux程序员都喜欢使用-O2选项，因为它在优化长度、编译时间和代码大小之间，取得了一个比较理想的平衡点。<br>
<br>
　　下面通过具体实例来感受一下GCC的代码优化功能，所用程序如下：<br>
<br>
<div style="MARGIN-LEFT: 80px"><span style="COLOR: rgb(0,0,153)">//testOpt.c<br>
#include &lt;stdio.h&gt;</span><br style="COLOR: rgb(0,0,153)">
<span style="COLOR: rgb(0,0,153)">int main(void)</span><br style="COLOR: rgb(0,0,153)">
<span style="COLOR: rgb(0,0,153)">{</span><br style="COLOR: rgb(0,0,153)">
<span style="COLOR: rgb(0,0,153)">&nbsp; double counter;</span><br style="COLOR: rgb(0,0,153)">
<span style="COLOR: rgb(0,0,153)">&nbsp; double result;</span><br style="COLOR: rgb(0,0,153)">
<span style="COLOR: rgb(0,0,153)">&nbsp; double temp;</span><br style="COLOR: rgb(0,0,153)">
<br style="COLOR: rgb(0,0,153)">
<span style="COLOR: rgb(0,0,153)">&nbsp; for (counter = 0; counter != 2000.0 * 2000.0 * 2000.0 / 20.0 + 2000;</span> <span style="COLOR: rgb(0,0,153)">counter += (5 - 1) / 4)</span><br style="COLOR: rgb(0,0,153)">
<span style="COLOR: rgb(0,0,153)">&nbsp; {</span><br style="COLOR: rgb(0,0,153)">
<span style="COLOR: rgb(0,0,153)">&nbsp;&nbsp;&nbsp; temp = counter / 1979;</span><br style="COLOR: rgb(0,0,153)">
<span style="COLOR: rgb(0,0,153)">&nbsp;&nbsp;&nbsp; result = counter;</span><br style="COLOR: rgb(0,0,153)">
<span style="COLOR: rgb(0,0,153)">&nbsp; }</span><br style="COLOR: rgb(0,0,153)">
<span style="COLOR: rgb(0,0,153)">&nbsp; printf("Result is %lf\n", result);</span><br style="COLOR: rgb(0,0,153)">
<span style="COLOR: rgb(0,0,153)">&nbsp; return 0;</span><br style="COLOR: rgb(0,0,153)">
<span style="COLOR: rgb(0,0,153)">}</span><br></div>
　　首先不加任何优化选项进行编译：<br>
<br>
<div style="MARGIN-LEFT: 80px"><span style="COLOR: rgb(0,0,153)">gcc -Wall</span> <span style="COLOR: rgb(0,0,153)">testOpt.c</span> <span style="COLOR: rgb(0,0,153)">-o testOpt<br></span></div>
<br>
　　借助Linux提供的time命令，可以大致统计出该程序在运行时所需要的时间：<br>
<div style="MARGIN-LEFT: 80px"><span style="COLOR: rgb(0,0,153)">$time ./testOpt</span><br></div>
<div style="MARGIN-LEFT: 80px"><span style="COLOR: rgb(0,0,153)">Result is 400001999.000000</span><br style="COLOR: rgb(0,0,153)">
<br style="COLOR: rgb(0,0,153)">
<span style="COLOR: rgb(0,0,153)">real&nbsp;&nbsp;&nbsp; 0m7.759s</span><br style="COLOR: rgb(0,0,153)">
<span style="COLOR: rgb(0,0,153)">user&nbsp;&nbsp;&nbsp; 0m7.444s</span><br style="COLOR: rgb(0,0,153)">
<span style="COLOR: rgb(0,0,153)">sys&nbsp;&nbsp;&nbsp;&nbsp; 0m0.008s</span><br></div>
<br>
　　接下去使用-O1优化选项来对代码进行优化处理：<br>
<br>
<div style="MARGIN-LEFT: 80px; COLOR: rgb(0,0,153)">gcc -Wall -O testOpt.c -o testOpt<br></div>
<div style="MARGIN-LEFT: 40px"></div>
<br>
　　测试运行时间：<br>
<div style="MARGIN-LEFT: 80px"><span style="COLOR: rgb(0,0,153)">$time ./testOPt<br>
Result is 400001999.000000<br>
<br>
real&nbsp;&nbsp;&nbsp; 0m2.445s<br>
user&nbsp;&nbsp;&nbsp; 0m2.436s<br>
sys&nbsp;&nbsp;&nbsp;&nbsp; 0m0.000s<br>
<br></span></div>
　　接下去使用-O2优化选项来对代码进行优化处理：<br>
<br>
<div style="MARGIN-LEFT: 80px"><span style="COLOR: rgb(0,0,153)">gcc -Wall -O2 testOpt.c -o testOpt<br></span></div>
<br>
　　测试运行时间：<br>
<div style="MARGIN-LEFT: 80px"><span style="COLOR: rgb(0,0,153)">$time ./testOPt<br>
Result is 400001999.000000<br>
<br>
real&nbsp;&nbsp;&nbsp; 0m2.338s<br>
user&nbsp;&nbsp;&nbsp; 0m2.320s<br>
sys&nbsp;&nbsp;&nbsp;&nbsp; 0m0.004s<br>
<br></span></div>
　　尽管GCC的代码优化功能非常强大，但作为一名优秀的Linux程序员，首先还是要力求能够手工编写出高质量的代码。如果编写的代码简短，并且逻辑性强，编译器就不会做更多的工作，甚至根本用不着优化。特别在以下一些场合中应该避免使用优化：<br>
<ol style="COLOR: rgb(0,0,0)">
<li>程序开发的时候优化等级越高，消耗在编译上的时间就越长，因此在开发的时候最好不要使用优化选项，只有到软件发行或开发结束的时候，才考虑对最终生成的代码进行优化。</li>
<li>资源受限的时候一些优化选项会增加可执行代码的体积，如果程序在运行时能够申请到的内存资源非常紧张（如一些实时嵌入式设备），那就不要对代码进行优化，因为由这带来的负面影响可能会产生非常严重的后果。</li>
<li>跟踪调试的时候对代码进行优化，容易导致某些代码可能会被删除或改写，或者为了取得更佳的性能而进行重组，从而使跟踪和调试变得异常困难。</li>
</ol>
<h3>6、程序性能分析　</h3>
<hr>
　GCC支持的其它调试选项还包括-p和-pg，它们会将剖析（Profiling）信息加入到最终生成的二进制代码中。剖析信息即包含了更为详细的调试信息（只是我这么觉得，由下面的例子可以证实），也对于找出程序的性能瓶颈很有帮助，是协助Linux程序员开发出高性能程序的有力工具。在编译时加入-p选项会在生成的代码中加入通用剖析工具（Prof）能够识别的统计信息，而 -pg选项则生成只有GNU剖析工具（Gprof）才能识别的统计信息。下面我们还是以crash.c程序的编译和调试，来看看使用-p选项对程序调试的好处吧。<br>
<br>
　　编译：<br>
<div style="MARGIN-LEFT: 80px"><span style="COLOR: rgb(0,0,153)">&nbsp;gcc -Wall -g -p crash.c -o crash</span><br></div>
<br>
　　调试：<br>
<div style="MARGIN-LEFT: 80px"><span style="COLOR: rgb(0,0,153)">[lyanry@lyanry crash]$ gdb -q crash</span><br style="COLOR: rgb(0,0,153)">
<span style="COLOR: rgb(0,0,153)">Using host libthread_db library "/lib/libthread_db.so.1".</span><br style="COLOR: rgb(0,0,153)">
<span style="COLOR: rgb(0,0,153)">(gdb) run</span><br style="COLOR: rgb(0,0,153)">
<span style="COLOR: rgb(0,0,153)">Starting program: /home/lyanry/program/c++/crash/crash</span><br style="COLOR: rgb(0,0,153)">
<span style="COLOR: rgb(0,0,153)">Reading symbols from shared object read from target memory...done.</span><br style="COLOR: rgb(0,0,153)">
<span style="COLOR: rgb(0,0,153)">Loaded system supplied DSO at 0x909000</span><br style="COLOR: rgb(0,0,153)">
<span style="COLOR: rgb(0,0,153)">Input an integer:11</span><br style="COLOR: rgb(0,0,153)">
<br style="COLOR: rgb(0,0,153)">
<span style="COLOR: rgb(0,0,153)">Program received signal SIGSEGV, Segmentation fault.</span><br style="COLOR: rgb(0,0,153)">
<span style="COLOR: rgb(0,0,153)">0x00971667 in _IO_vfscanf_internal () from /lib/libc.so.6</span><br style="COLOR: rgb(0,0,153)">
<span style="COLOR: rgb(0,0,153)">(gdb) backtrace</span><br style="COLOR: rgb(0,0,153)">
<span style="COLOR: rgb(0,0,153)">#0&nbsp; 0x00971667 in _IO_vfscanf_internal () from /lib/libc.so.6</span><br style="COLOR: rgb(0,0,153)">
<span style="COLOR: rgb(0,0,153)">#1&nbsp; 0x00979337 in scanf () from /lib/libc.so.6</span><br style="COLOR: rgb(0,0,153)">
<span style="COLOR: rgb(0,0,153)">#2&nbsp; 0x08048520 in main () at crash.c:8</span><br>
<br></div>
　　现在，可以从GDB输出结果中看到带有出错代码行号的backtrace结果了，即<span style="COLOR: rgb(0,0,153)">#2&nbsp; 0x08048520 in main () at crash.c:8<span style="COLOR: rgb(0,0,0)">，使用frame指令，查看出错代码，结果如下：<br></span></span>
<div style="MARGIN-LEFT: 80px"><span style="COLOR: rgb(0,0,153)">(gdb) frame 2</span><br style="COLOR: rgb(0,0,153)">
<span style="COLOR: rgb(0,0,153)">#2&nbsp; 0x08048520 in main () at crash.c:8</span><br style="COLOR: rgb(0,0,153)">
<span style="COLOR: rgb(0,0,153)">8&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; scanf("%d", input);</span><br>
<br></div>
<span style="COLOR: rgb(0,0,153)"><span style="COLOR: rgb(0,0,0)">　　现在有点清晰地知道问题发生在哪了吧！<br>
<br>
　　下面，来测试</span></span>-p或-pg选项用于分析程序的性能瓶颈，结合前面的叙述，看一下man手册上对-p和-pg选项的详细说明：<br>
<div style="MARGIN-LEFT: 80px"><span style="COLOR: rgb(0,0,153)">-p&nbsp; Generate extra code to write profile information suitable for the</span><br style="COLOR: rgb(0,0,153)">
<span style="COLOR: rgb(0,0,153)">&nbsp;&nbsp;&nbsp; analysis program prof.&nbsp; You must use this option when compiling the</span><br style="COLOR: rgb(0,0,153)">
<span style="COLOR: rgb(0,0,153)">&nbsp;&nbsp;&nbsp; source files you want data about, and you must also use it when</span><br style="COLOR: rgb(0,0,153)">
<span style="COLOR: rgb(0,0,153)">&nbsp;&nbsp;&nbsp; linking.</span><br style="COLOR: rgb(0,0,153)">
<br style="COLOR: rgb(0,0,153)">
<span style="COLOR: rgb(0,0,153)">-pg Generate extra code to write profile information suitable for the</span><br style="COLOR: rgb(0,0,153)">
<span style="COLOR: rgb(0,0,153)">&nbsp;&nbsp;&nbsp; analysis program gprof.&nbsp; You must use this option when compiling</span><br style="COLOR: rgb(0,0,153)">
<span style="COLOR: rgb(0,0,153)">&nbsp;&nbsp;&nbsp; the source files you want data about, and you must also use it when</span><br style="COLOR: rgb(0,0,153)">
<span style="COLOR: rgb(0,0,153)">&nbsp;&nbsp;&nbsp; linking.</span><br></div>
　　<br>
　　说明中所提及的prof和gprof，都是程序性能剖析工具，prof是通用的，gprof是GNU开发的。以例程profile.c来测试 gprof，在编译程序时要添加-gp选项。要注意，这个选项只是在连接期间产生作用的。profile.c代码清单如下：<br>
<div style="MARGIN-LEFT: 80px"><span style="COLOR: rgb(0,0,153)">//profile.c</span><br style="COLOR: rgb(0,0,153)">
<span style="COLOR: rgb(0,0,153)">#include &lt;stdio.h&gt;</span><br style="COLOR: rgb(0,0,153)">
<span style="COLOR: rgb(0,0,153)">void function1()</span><br style="COLOR: rgb(0,0,153)">
<span style="COLOR: rgb(0,0,153)">{</span><br style="COLOR: rgb(0,0,153)">
<span style="COLOR: rgb(0,0,153)">&nbsp; int i=0,j;</span><br style="COLOR: rgb(0,0,153)">
<span style="COLOR: rgb(0,0,153)">&nbsp; for(j=0;j&lt;100000;j++)</span><br style="COLOR: rgb(0,0,153)">
<span style="COLOR: rgb(0,0,153)">&nbsp;&nbsp;&nbsp; i+=j;</span><br style="COLOR: rgb(0,0,153)">
<span style="COLOR: rgb(0,0,153)">}</span><br style="COLOR: rgb(0,0,153)">
<span style="COLOR: rgb(0,0,153)">void function2()</span><br style="COLOR: rgb(0,0,153)">
<span style="COLOR: rgb(0,0,153)">{</span><br style="COLOR: rgb(0,0,153)">
<span style="COLOR: rgb(0,0,153)">&nbsp; int i,j;</span><br style="COLOR: rgb(0,0,153)">
<span style="COLOR: rgb(0,0,153)">&nbsp; function1();</span><br style="COLOR: rgb(0,0,153)">
<span style="COLOR: rgb(0,0,153)">&nbsp; for(j=0;j&lt;200000;j++)</span><br style="COLOR: rgb(0,0,153)">
<span style="COLOR: rgb(0,0,153)">&nbsp;&nbsp;&nbsp; i=j;</span><br style="COLOR: rgb(0,0,153)">
<span style="COLOR: rgb(0,0,153)">}</span><br style="COLOR: rgb(0,0,153)">
<span style="COLOR: rgb(0,0,153)">int main(void)</span><br style="COLOR: rgb(0,0,153)">
<span style="COLOR: rgb(0,0,153)">{</span><br style="COLOR: rgb(0,0,153)">
<span style="COLOR: rgb(0,0,153)">&nbsp; int i,j;</span><br style="COLOR: rgb(0,0,153)">
<span style="COLOR: rgb(0,0,153)">&nbsp; for(i=0;i&lt;100;i++)</span><br style="COLOR: rgb(0,0,153)">
<span style="COLOR: rgb(0,0,153)">&nbsp;&nbsp;&nbsp; function1();</span><br style="COLOR: rgb(0,0,153)">
<span style="COLOR: rgb(0,0,153)">&nbsp; for(j=0;i&lt;50000;i++)</span><br style="COLOR: rgb(0,0,153)">
<span style="COLOR: rgb(0,0,153)">&nbsp;&nbsp;&nbsp; function2();</span><br>
<br style="COLOR: rgb(0,0,153)">
<span style="COLOR: rgb(0,0,153)">&nbsp; return 0;</span><br style="COLOR: rgb(0,0,153)">
<span style="COLOR: rgb(0,0,153)">}</span><br style="COLOR: rgb(0,0,153)">
<br></div>
　　编译：<br>
<div style="MARGIN-LEFT: 80px"><span style="COLOR: rgb(0,0,153)">gcc -Wall -pg profile.c -o profile<br></span></div>
<span style="COLOR: rgb(0,0,153)"><br>
<span style="COLOR: rgb(0,0,0)">　　运行profile程序，会在当前目录中生成一个gmon.out文件，下面可以使用gprof工具对profile程序进行剖析了：<br></span></span>
<div style="MARGIN-LEFT: 80px"><span style="COLOR: rgb(0,0,153)">gprof profile &gt;gprof.txt</span><br>
<br></div>
<span style="COLOR: rgb(0,0,153)"><span style="COLOR: rgb(0,0,0)">　　上面指令执行时，gprof会自动使用gmon.out文件，将输出结果重定向到gprof.txt文件中。如果想知道gmon.out是什么，还是看看man手册里的描述吧：<br></span></span>
<div style="MARGIN-LEFT: 40px">
<div style="MARGIN-LEFT: 40px"><span style="COLOR: rgb(0,0,153)">&nbsp;"Gprof" reads the given object file (the default is "a.out") and　establishes the relation between its symbol table and&nbsp; the&nbsp; call&nbsp; graph　profile from gmon.out.&nbsp;</span><br></div>
</div>
<div style="MARGIN-LEFT: 40px"></div>
　　<br>
　　好了，现在要做的事情，就是在当前目录下打开gprof.txt，看看了，文件中，我们感兴趣的内容通常有两处：<br>
<div style="MARGIN-LEFT: 80px"><span style="COLOR: rgb(0,0,153)">Each sample counts as 0.01 seconds.</span><br style="COLOR: rgb(0,0,153)">
<span style="COLOR: rgb(0,0,153)">&nbsp; %&nbsp;&nbsp; cumulative&nbsp;&nbsp; self&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; self&nbsp;&nbsp;&nbsp;&nbsp; total&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><br style="COLOR: rgb(0,0,153)">
<span style="COLOR: rgb(0,0,153)">&nbsp;time&nbsp;&nbsp; seconds&nbsp;&nbsp; seconds&nbsp;&nbsp;&nbsp; calls&nbsp; us/call&nbsp; us/call&nbsp; name&nbsp;&nbsp;&nbsp;</span><br style="COLOR: rgb(0,0,153)">
<span style="COLOR: rgb(0,0,153)">&nbsp;66.92&nbsp;&nbsp;&nbsp;&nbsp; 24.44&nbsp;&nbsp;&nbsp; 24.44&nbsp;&nbsp;&nbsp; 49900&nbsp;&nbsp; 489.78&nbsp;&nbsp; 731.38&nbsp; function2</span><br style="COLOR: rgb(0,0,153)">
<span style="COLOR: rgb(0,0,153)">&nbsp;33.08&nbsp;&nbsp;&nbsp;&nbsp; 36.52&nbsp;&nbsp;&nbsp; 12.08&nbsp;&nbsp;&nbsp; 50000&nbsp;&nbsp; 241.60&nbsp;&nbsp; 241.60&nbsp; function1</span><br></div>
　　与<br>
<div style="MARGIN-LEFT: 80px"><span style="COLOR: rgb(0,0,153)">index % time&nbsp;&nbsp;&nbsp; self&nbsp; children&nbsp;&nbsp;&nbsp; called&nbsp;&nbsp;&nbsp;&nbsp; name</span><br style="COLOR: rgb(0,0,153)">
<span style="COLOR: rgb(0,0,153)">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;spontaneous&gt;</span><br style="COLOR: rgb(0,0,153)">
<span style="COLOR: rgb(0,0,153)">[1]&nbsp;&nbsp;&nbsp; 100.0&nbsp;&nbsp;&nbsp; 0.00&nbsp;&nbsp; 36.52&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; main [1]</span><br style="COLOR: rgb(0,0,153)">
<span style="COLOR: rgb(0,0,153)">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 24.44&nbsp;&nbsp; 12.06&nbsp;&nbsp; 49900/49900&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; function2 [2]</span><br style="COLOR: rgb(0,0,153)">
<span style="COLOR: rgb(0,0,153)">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0.02&nbsp;&nbsp;&nbsp; 0.00&nbsp;&nbsp;&nbsp;&nbsp; 100/50000&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; function1 [3]</span><br style="COLOR: rgb(0,0,153)">
<span style="COLOR: rgb(0,0,153)">-----------------------------------------------</span><br style="COLOR: rgb(0,0,153)">
<span style="COLOR: rgb(0,0,153)">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 24.44&nbsp;&nbsp; 12.06&nbsp;&nbsp; 49900/49900&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; main [1]</span><br style="COLOR: rgb(0,0,153)">
<span style="COLOR: rgb(0,0,153)">[2]&nbsp;&nbsp;&nbsp;&nbsp; 99.9&nbsp;&nbsp; 24.44&nbsp;&nbsp; 12.06&nbsp;&nbsp; 49900&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; function2 [2]</span><br style="COLOR: rgb(0,0,153)">
<span style="COLOR: rgb(0,0,153)">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 12.06&nbsp;&nbsp;&nbsp; 0.00&nbsp;&nbsp; 49900/50000&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; function1 [3]</span><br style="COLOR: rgb(0,0,153)">
<span style="COLOR: rgb(0,0,153)">-----------------------------------------------</span><br style="COLOR: rgb(0,0,153)">
<span style="COLOR: rgb(0,0,153)">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0.02&nbsp;&nbsp;&nbsp; 0.00&nbsp;&nbsp;&nbsp;&nbsp; 100/50000&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; main [1]</span><br style="COLOR: rgb(0,0,153)">
<span style="COLOR: rgb(0,0,153)">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 12.06&nbsp;&nbsp;&nbsp; 0.00&nbsp;&nbsp; 49900/50000&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; function2 [2]</span><br style="COLOR: rgb(0,0,153)">
<span style="COLOR: rgb(0,0,153)">[3]&nbsp;&nbsp;&nbsp;&nbsp; 33.1&nbsp;&nbsp; 12.08&nbsp;&nbsp;&nbsp; 0.00&nbsp;&nbsp; 50000&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; function1 [3]</span><br style="COLOR: rgb(0,0,153)">
<span style="COLOR: rgb(0,0,153)">-----------------------------------------------</span><br></div>
　　<br>
　　不想再细说下去，自己琢磨去吧。</div>
</div>
<div align="right">责任编辑 webmaster</div> ]]></description>
		<eb:creationDate>2008-04-03 11:08:46</eb:creationDate>
		<eb:modificationDate></eb:modificationDate>
    </item>
    <item>
		<title><![CDATA[ gcc和g++的区别 ]]></title>
		<link>http://blog.tom.com/tigerkings941220/article/9329.html</link>
		<description><![CDATA[ <div id="fontzoom">
<p>gcc和g++都是GNU(组织)的一个编译器。</p>
<p>误区一:gcc只能编译c代码,g++只能编译c++代码<br>
两者都可以，但是请注意：<br>
1.后缀为.c的，gcc把它当作是C程序，而g++当作是c++程序；后缀为.cpp的，两者都会认为是c++程序，注意，虽然c++是c的超集，但是两者对语法的要求是有区别的。C++的语法规则更加严谨一些。<br>
2.编译阶段，g++会调用gcc，对于c++代码，两者是等价的，但是因为gcc命令不能自动和C＋＋程序使用的库联接，所以通常用g++来完成链接，为了统一起见，干脆编译/链接统统用g++了，这就给人一种错觉，好像cpp程序只能用g++似的。<br>
&nbsp;<br>
误区二:gcc不会定义__cplusplus宏，而g++会<br>
实际上，这个宏只是标志着编译器将会把代码按C还是C++语法来解释，如上所述，如果后缀为.c，并且采用gcc编译器，则该宏就是未定义的，否则，就是已定义。<br>
&nbsp;<br>
误区三:编译只能用gcc，链接只能用g++<br>
严格来说，这句话不算错误，但是它混淆了概念，应该这样说：编译可以用gcc/g++，而链接可以用g++或者gcc -lstdc++。因为gcc命令不能自动和C＋＋程序使用的库联接，所以通常使用g++来完成联接。但在编译阶段，g++会自动调用gcc，二者等价。</p>
<div>
<table id="article49cc712301000722" cellspacing="0" cellpadding="0" border="0">
<tbody>
<tr>
<td align="middle">
<table cellspacing="0" cellpadding="0" border="0">
<tbody>
<tr>
<td id="articleTitle49cc712301000722">
<div id="commentText49cc712301000722">gcc和g++的区别</div>
</td>
</tr>
</tbody>
</table>
</td>
</tr>
<tr id="articleBody49cc712301000722">
<td cellspacing="0" cellpadding="0" align="center" border="0"></td>
</tr>
</tbody>
<tbody>
<tr>
<td class="time"></td>
</tr>
</tbody>
</table>
<table cellspacing="0" cellpadding="0" align="center" border="0">
<tbody>
<tr>
<td align="middle">
<div id="articleText49cc712301000722" align="left">
<div>我们在编译c/c++代码的时候，有人用gcc，有人用g++，于是各种说法都来了，譬如c代码用gcc，而c++代码用g++，或者说编译用gcc，链接用g++，一时也不知哪个说法正确，如果再遇上个extern "C"，分歧就更多了，这里我想作个了结，毕竟知识的目的是令人更清醒，而不是更糊涂。
<div>&nbsp;</div>
<div><strong>误区一:gcc只能编译c代码,g++只能编译c++代码</strong></div>
<div><br>
两者都可以，但是请注意：</div>
<div>1.后缀为.c的，gcc把它当作是C程序，而g++当作是c++程序；后缀为.cpp的，两者都会认为是c++程序，注意，虽然c++是c的超集，但是两者对语法的要求是有区别的，例如：</div>
<div>#include &lt;stdio.h&gt;</div>
<div>int main(int argc, char* argv[]) {<br>
&nbsp;&nbsp; if(argv == 0) return;</div>
<div>&nbsp;&nbsp; printString(<font color="#FF0000">argv</font>);</div>
<div>&nbsp;&nbsp; <font color="#FF0000">return</font>;<br>
}<br>
<font color="#FF0000">int printString(char* string) {<br>
&nbsp; sprintf(string, "This is a test.\n");<br>
}</font></div>
<div>如果按照C的语法规则，OK，没问题，但是，一旦把后缀改为cpp，立刻报三个错：“printString未定义”；</div>
<div>“cannot convert `char**' to `char*”；</div>
<div>”return-statement with no value“；</div>
<div>分别对应前面红色标注的部分。可见C++的语法规则更加严谨一些。</div>
<div>2.编译阶段，g++会调用gcc，对于c++代码，两者是等价的，但是因为gcc命令不能自动和C＋＋程序使用的库联接，所以通常用g++来完成链接，为了统一起见，干脆编译/链接统统用g++了，这就给人一种错觉，好像cpp程序只能用g++似的。</div>
<div>&nbsp;</div>
<div><strong>误区二:gcc不会定义__cplusplus宏，而g++会</strong></div>
<div><br>
实际上，这个宏只是标志着编译器将会把代码按C还是C++语法来解释，如上所述，如果后缀为.c，并且采用gcc编译器，则该宏就是未定义的，否则，就是已定义。</div>
<div>&nbsp;</div>
<div><strong>误区三:编译只能用gcc，链接只能用g++</strong></div>
<div><strong><br></strong>严格来说，这句话不算错误，但是它混淆了概念，<font color="#FF0000">应该这样说：编译可以用gcc/g++，而链接可以用g++或者gcc -lstdc++</font>。因为gcc命令不能自动和C＋＋程序使用的库联接，所以通常使用g++来完成联接。但在编译阶段，g++会自动调用gcc，二者等价。</div>
<div>&nbsp;</div>
<div><strong>误区四:extern "C"与gcc/g++有关系</strong></div>
<div><br>
实际上并无关系，无论是gcc还是g++，用extern "c"时，都是以C的命名方式来为symbol命名，否则，都以c++方式命名。试验如下：<br>
<em>me.h</em>：<br>
<font color="#FF0000">extern "C" void CppPrintf(void);</font></div>
<div>&nbsp;</div>
<div><em>me.cpp</em>:<br>
#include &lt;iostream&gt;<br>
#include "me.h"<br>
using namespace std;<br>
void CppPrintf(void)<br>
{<br>
&nbsp;&nbsp;&nbsp;&nbsp; cout &lt;&lt; "Hello\n";<br>
}</div>
<div>&nbsp;</div>
<div><em>test.cpp:</em><br>
#include &lt;stdlib.h&gt;<br>
#include &lt;stdio.h&gt;<br>
#include "me.h"&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br>
int main(void)<br>
{<br>
&nbsp;&nbsp;&nbsp; CppPrintf();<br>
&nbsp;&nbsp;&nbsp; return 0;<br>
}</div>
<div>&nbsp;</div>
<div><strong>1. 先给me.h加上extern "C"，看用gcc和g++命名有什么不同</strong></div>
<div><br>
[root@root G++]# g++ -S me.cpp<br>
[root@root G++]# less me.s<br>
.globl _Z9CppPrintfv&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //注意此函数的命名<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; .type&nbsp;&nbsp; CppPrintf, @function</div>
<div>[root@root GCC]# gcc -S me.cpp<br>
[root@root GCC]# less me.s<br>
.globl _Z9CppPrintfv&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //注意此函数的命名<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; .type&nbsp;&nbsp; CppPrintf, @function<br>
完全相同！<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br>
<strong>2. 去掉me.h中extern "C"，看用gcc和g++命名有什么不同</strong></div>
<div><strong><br></strong>[root@root GCC]# gcc -S me.cpp<br>
[root@root GCC]# less me.s<br>
.globl _Z9CppPrintfv&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //注意此函数的命名<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; .type&nbsp;&nbsp; _Z9CppPrintfv, @function</div>
<div>[root@root G++]# g++ -S me.cpp<br>
[root@root G++]# less me.s<br>
.globl _Z9CppPrintfv&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //注意此函数的命名<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; .type&nbsp;&nbsp; _Z9CppPrintfv, @function<br>
完全相同！</div>
<div><strong>【结论】</strong>完全相同，可见extern "C"与采用gcc/g++并无关系，以上的试验还间接的印证了前面的说法：在编译阶段，g++是调用gcc的。</div>
</div>
</div>
</td>
</tr>
</tbody>
</table>
</div>
</div>
<div align="right">责任编辑 webmaster</div> ]]></description>
		<eb:creationDate>2008-04-03 11:06:59</eb:creationDate>
		<eb:modificationDate></eb:modificationDate>
    </item>
    <item>
		<title><![CDATA[ 跟我一起写 Makefile ]]></title>
		<link>http://blog.tom.com/tigerkings941220/article/9328.html</link>
		<description><![CDATA[ <p>选择自 <a id="ArticleTitle1_ArticleTitle1_AuthorLink" href="http://dev.csdn.net/user/haoel" name="ArticleTitle1_ArticleTitle1_AuthorLink">haoel</a> 的 Blog</p>
<p>&nbsp;</p>
<p align="left"><font face="Courier New" size="4"><strong>概述<br>
——</strong></font></p>
<p><font face="Courier New">什么是makefile？或许很多Winodws的程序员都不知道这个东西，因为那些Windows的IDE都为你做了这个工作，但我觉得要作一个好的和professional的程序员，makefile还是要懂。这就好像现在有这么多的HTML的编辑器，但如果你想成为一个专业人士，你还是要了解HTML的标识的含义。特别在Unix下的软件编译，你就不能不自己写makefile了，会不会写makefile，从一个侧面说明了一个人是否具备完成大型工程的能力。</font></p>
<p><font face="Courier New">因为，makefile关系到了整个工程的编译规则。一个工程中的源文件不计数，其按类型、功能、模块分别放在若干个目录中，makefile定义了一系列的规则来指定，哪些文件需要先编译，哪些文件需要后编译，哪些文件需要重新编译，甚至于进行更复杂的功能操作，因为makefile就像一个Shell脚本一样，其中也可以执行操作系统的命令。</font></p>
<p><font face="Courier New">makefile带来的好处就是——“自动化编译”，一旦写好，只需要一个make命令，整个工程完全自动编译，极大的提高了软件开发的效率。make是一个命令工具，是一个解释makefile中指令的命令工具，一般来说，大多数的IDE都有这个命令，比如：Delphi的make，Visual C++的nmake，Linux下GNU的make。可见，makefile都成为了一种在工程方面的编译方法。</font></p>
<p><font face="Courier New">现在讲述如何写makefile的文章比较少，这是我想写这篇文章的原因。当然，不同产商的make各不相同，也有不同的语法，但其本质都是在“文件依赖性”上做文章，这里，我仅对GNU的make进行讲述，我的环境是RedHat Linux 8.0，make的版本是3.80。必竟，这个make是应用最为广泛的，也是用得最多的。而且其还是最遵循于IEEE 1003.2-1992 标准的（POSIX.2）。</font></p>
<p><font face="Courier New">在这篇文档中，将以C/C++的源码作为我们基础，所以必然涉及一些关于C/C++的编译的知识，相关于这方面的内容，还请各位查看相关的编译器的文档。这里所默认的编译器是UNIX下的GCC和CC。</font></p>
<p>&nbsp;</p>
<p><font face="Courier New"><strong>关于程序的编译和链接<br>
——————————</strong></font></p>
<p><font face="Courier New">在此，我想多说关于程序编译的一些规范和方法，一般来说，无论是C、C++、还是pas，首先要把源文件编译成中间代码文件，在Windows下也就是 .obj 文件，UNIX下是 .o 文件，即 Object File，这个动作叫做编译（compile）。然后再把大量的Object File合成执行文件，这个动作叫作链接（link）。</font></p>
<p><font face="Courier New">编译时，编译器需要的是语法的正确，函数与变量的声明的正确。对于后者，通常是你需要告诉编译器头文件的所在位置（头文件中应该只是声明，而定义应该放在C/C++文件中），只要所有的语法正确，编译器就可以编译出中间目标文件。一般来说，每个源文件都应该对应于一个中间目标文件（O文件或是OBJ文件）。</font></p>
<p><font face="Courier New">链接时，主要是链接函数和全局变量，所以，我们可以使用这些中间目标文件（O文件或是OBJ文件）来链接我们的应用程序。链接器并不管函数所在的源文件，只管函数的中间目标文件（Object File），在大多数时候，由于源文件太多，编译生成的中间目标文件太多，而在链接时需要明显地指出中间目标文件名，这对于编译很不方便，所以，我们要给中间目标文件打个包，在Windows下这种包叫“库文件”（Library File)，也就是 .lib 文件，在UNIX下，是Archive File，也就是 .a 文件。</font></p>
<p><font face="Courier New">总结一下，源文件首先会生成中间目标文件，再由中间目标文件生成执行文件。在编译时，编译器只检测程序语法，和函数、变量是否被声明。如果函数未被声明，编译器会给出一个警告，但可以生成Object File。而在链接程序时，链接器会在所有的Object File中找寻函数的实现，如果找不到，那到就会报链接错误码（Linker Error），在VC下，这种错误一般是：Link 2001错误，意思说是说，链接器未能找到函数的实现。你需要指定函数的Object File.</font></p>
<p><font face="Courier New">好，言归正传，GNU的make有许多的内容，闲言少叙，还是让我们开始吧。</font></p>
<p>&nbsp;</p>
<p><font face="Courier New"><strong>Makefile 介绍<br>
———————</strong></font></p>
<p><font face="Courier New">make命令执行时，需要一个 Makefile 文件，以告诉make命令需要怎么样的去编译和链接程序。</font></p>
<p><font face="Courier New">首先，我们用一个示例来说明Makefile的书写规则。以便给大家一个感兴认识。这个示例来源于GNU的make使用手册，在这个示例中，我们的工程有8个C文件，和3个头文件，我们要写一个Makefile来告诉make命令如何编译和链接这几个文件。我们的规则是：<br>
&nbsp;&nbsp;&nbsp; 1）如果这个工程没有编译过，那么我们的所有C文件都要编译并被链接。<br>
&nbsp;&nbsp;&nbsp; 2）如果这个工程的某几个C文件被修改，那么我们只编译被修改的C文件，并链接目标程序。<br>
&nbsp;&nbsp;&nbsp; 3）如果这个工程的头文件被改变了，那么我们需要编译引用了这几个头文件的C文件，并链接目标程序。</font></p>
<p><font face="Courier New">只要我们的Makefile写得够好，所有的这一切，我们只用一个make命令就可以完成，make命令会自动智能地根据当前的文件修改的情况来确定哪些文件需要重编译，从而自己编译所需要的文件和链接目标程序。</font></p>
<p><br>
<font face="Courier New"><strong>一、Makefile的规则</strong></font></p>
<p><font face="Courier New">在讲述这个Makefile之前，还是让我们先来粗略地看一看Makefile的规则。</font></p>
<p><font face="Courier New">&nbsp;&nbsp;&nbsp; target ... : prerequisites ...<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; command<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ...<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ...</font></p>
<p><font face="Courier New">&nbsp;&nbsp;&nbsp; target也就是一个目标文件，可以是Object File，也可以是执行文件。还可以是一个标签（Label），对于标签这种特性，在后续的“伪目标”章节中会有叙述。</font></p>
<p><font face="Courier New">&nbsp;&nbsp;&nbsp; prerequisites就是，要生成那个target所需要的文件或是目标。</font></p>
<p><font face="Courier New">&nbsp;&nbsp;&nbsp; command也就是make需要执行的命令。（任意的Shell命令）</font></p>
<p><font face="Courier New">这是一个文件的依赖关系，也就是说，target这一个或多个的目标文件依赖于prerequisites中的文件，其生成规则定义在command中。说白一点就是说，prerequisites中如果有一个以上的文件比target文件要新的话，command所定义的命令就会被执行。这就是Makefile的规则。也就是Makefile中最核心的内容。</font></p>
<p><font face="Courier New">说到底，Makefile的东西就是这样一点，好像我的这篇文档也该结束了。呵呵。还不尽然，这是Makefile的主线和核心，但要写好一个Makefile还不够，我会以后面一点一点地结合我的工作经验给你慢慢到来。内容还多着呢。：）</font></p>
<p><br>
<font face="Courier New"><strong>二、一个示例</strong></font></p>
<p><font face="Courier New">正如前面所说的，如果一个工程有3个头文件，和8个C文件，我们为了完成前面所述的那三个规则，我们的Makefile应该是下面的这个样子的。</font></p>
<p><font face="Courier New">&nbsp;&nbsp;&nbsp; edit : main.o kbd.o command.o display.o \<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; insert.o search.o files.o utils.o<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; cc -o edit main.o kbd.o command.o display.o \<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; insert.o search.o files.o utils.o</font></p>
<p><font face="Courier New">&nbsp;&nbsp;&nbsp; main.o : main.c defs.h<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; cc -c main.c<br>
&nbsp;&nbsp;&nbsp; kbd.o : kbd.c defs.h command.h<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; cc -c kbd.c<br>
&nbsp;&nbsp;&nbsp; command.o : command.c defs.h command.h<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; cc -c command.c<br>
&nbsp;&nbsp;&nbsp; display.o : display.c defs.h buffer.h<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; cc -c display.c<br>
&nbsp;&nbsp;&nbsp; insert.o : insert.c defs.h buffer.h<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; cc -c insert.c<br>
&nbsp;&nbsp;&nbsp; search.o : search.c defs.h buffer.h<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; cc -c search.c<br>
&nbsp;&nbsp;&nbsp; files.o : files.c defs.h buffer.h command.h<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; cc -c files.c<br>
&nbsp;&nbsp;&nbsp; utils.o : utils.c defs.h<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; cc -c utils.c<br>
&nbsp;&nbsp;&nbsp; clean :<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; rm edit main.o kbd.o command.o display.o \<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; insert.o search.o files.o utils.o</font></p>
<p><font face="Courier New">反斜杠（\）是换行符的意思。这样比较便于Makefile的易读。我们可以把这个内容保存在文件为“Makefile”或“makefile”的文件中，然后在该目录下直接输入命令“make”就可以生成执行文件edit。如果要删除执行文件和所有的中间目标文件，那么，只要简单地执行一下“make clean”就可以了。</font></p>
<p><font face="Courier New">在这个makefile中，目标文件（target）包含：执行文件edit和中间目标文件（*.o），依赖文件（prerequisites）就是冒号后面的那些 .c 文件和 .h文件。每一个 .o 文件都有一组依赖文件，而这些 .o 文件又是执行文件 edit 的依赖文件。依赖关系的实质上就是说明了目标文件是由哪些文件生成的，换言之，目标文件是哪些文件更新的。</font></p>
<p><font face="Courier New">在定义好依赖关系后，后续的那一行定义了如何生成目标文件的操作系统命令，一定要以一个Tab键作为开头。记住，make并不管命令是怎么工作的，他只管执行所定义的命令。make会比较targets文件和prerequisites文件的修改日期，如果prerequisites文件的日期要比targets文件的日期要新，或者target不存在的话，那么，make就会执行后续定义的命令。</font></p>
<p><font face="Courier New">这里要说明一点的是，clean不是一个文件，它只不过是一个动作名字，有点像C语言中的lable一样，其冒号后什么也没有，那么，make就不会自动去找文件的依赖性，也就不会自动执行其后所定义的命令。要执行其后的命令，就要在make命令后明显得指出这个lable的名字。这样的方法非常有用，我们可以在一个makefile中定义不用的编译或是和编译无关的命令，比如程序的打包，程序的备份，等等。</font></p> ]]></description>
		<eb:creationDate>2008-04-03 10:01:40</eb:creationDate>
		<eb:modificationDate></eb:modificationDate>
    </item>
    <item>
		<title><![CDATA[ 制作自己的静态链接库 ]]></title>
		<link>http://blog.tom.com/tigerkings941220/article/9327.html</link>
		<description><![CDATA[ <font size="2">这是第八章，八这个数字很多人都喜欢。我也一样。（感觉好迷信啊。）这一章我们要来制作静态链接库，我相信各位一定会很高兴的。<br>
&nbsp; 首先我先简单介绍一下静态链接库的作用。我们知道，C/C++程序生成目标代码的过程有，编写代码，编译代码，连接代码，生成目标代码。在连接代码的时候，会将编译后的二进制代码连接成目标代码。但是，有些时候。我们想让程序导入必要的代码，而不想导入无用的代码到我们的程序中。我们该如何呢？很简单，使用静态连接库。使用它我们就可以实现将在程序中使用的函数导入的目的。<br>
&nbsp; 下面我们来一步步的学做静态链接库并且学会如何使用。<br>
&nbsp; 我们现在先写一段代码，你可以用Dev也可以用记事本也可以用任何你喜欢的文本编辑器。像我就比较喜欢vi或者emacs作为平时的文本编辑器。话说远了，现在言规正传。我们写下下面的代码。<br></font>
<p><font size="2">//HelloWorld.c</font></p>
<br>
<p><font size="2">#include &lt;stdio.h&gt;</font></p>
<br>
<p><font size="2">void HelloWorld(void)<br>
{<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; printf("Hello World");<br>
}</font></p>
<br>
<p><font size="2">上面那段C代码各位应该很熟悉的吧，那么经典的Hello World好让我怀念啊。保存为HelloWorld.c以后我们就开始生成了。<br>
首先，我们先编译HelloWorld.c</font></p>
<br>
<p><font size="2">gcc -c Hell