﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>BlogJava-coolfiry-随笔分类-C/C++</title><link>http://www.blogjava.net/coolfiry/category/14261.html</link><description>认认真真做人,兢兢业业做事!</description><language>zh-cn</language><lastBuildDate>Tue, 25 Sep 2007 09:27:20 GMT</lastBuildDate><pubDate>Tue, 25 Sep 2007 09:27:20 GMT</pubDate><ttl>60</ttl><item><title>转Windows快速创建大文件的三种方法</title><link>http://www.blogjava.net/coolfiry/archive/2007/09/25/148051.html</link><dc:creator>Coolfiry</dc:creator><author>Coolfiry</author><pubDate>Tue, 25 Sep 2007 06:30:00 GMT</pubDate><guid>http://www.blogjava.net/coolfiry/archive/2007/09/25/148051.html</guid><wfw:comment>http://www.blogjava.net/coolfiry/comments/148051.html</wfw:comment><comments>http://www.blogjava.net/coolfiry/archive/2007/09/25/148051.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/coolfiry/comments/commentRss/148051.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/coolfiry/services/trackbacks/148051.html</trackback:ping><description><![CDATA[ <img src ="http://www.blogjava.net/coolfiry/aggbug/148051.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/coolfiry/" target="_blank">Coolfiry</a> 2007-09-25 14:30 <a href="http://www.blogjava.net/coolfiry/archive/2007/09/25/148051.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Web搜索引擎设计和实现分析[转载]</title><link>http://www.blogjava.net/coolfiry/archive/2006/11/11/80621.html</link><dc:creator>Coolfiry</dc:creator><author>Coolfiry</author><pubDate>Sat, 11 Nov 2006 13:37:00 GMT</pubDate><guid>http://www.blogjava.net/coolfiry/archive/2006/11/11/80621.html</guid><wfw:comment>http://www.blogjava.net/coolfiry/comments/80621.html</wfw:comment><comments>http://www.blogjava.net/coolfiry/archive/2006/11/11/80621.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/coolfiry/comments/commentRss/80621.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/coolfiry/services/trackbacks/80621.html</trackback:ping><description><![CDATA[
		<span style="font-size: 10pt;">一、引言 <br /><br />　　随着Internet的飞速发展，人们越来越依靠网络来
查找他们所需要的信息，但是，由于网上的信息源多不胜数，也就是我们经常所说的"Rich Data, Poor
Information"。所以如何有效的去发现我们所需要的信息，就成了一个很关键的问题。为了解决这个问题，搜索引擎就随之诞生。 <br /><br />　
　现在在网上的搜索引擎也已经有很多，比较著名的有AltaVista, Yahoo, InfoSeek, Metacrawler,
SavvySearch等等。国内也建立了很多的搜索引擎，比如：搜狐、新浪、北极星等等,当然由于它们建立的时间不长，在信息搜索的取全率和取准率上都
有待于改进和提高。 <br /><br />　　Alta
Vista是一个速度很快的搜索引擎，由于它强大的硬件配置，使它能够做及其复杂的查询。它主要是基于关键字进行查询，它漫游的领域有Web和
Usenet。支持布尔查询的"AND"，"OR"和"NOT"，同时还加上最相近定位"NEAR"，允许通配符和"向后"搜索（比如：你可以查找链接到
某一页的所有Web站点）。你可以决定是否对搜索的短语加上权值，在文档的什么部位去查找它们。能够进行短语查询而不是简单的单词查询的优点是很明显的，
比如，我们想要查找一个短语"to be or not to be"，如果只是把它们分解成单词的话，这些单词都是属于Stop
Word，这样这个查询就不会有任何结果，但是把它当作一个整体来查询，就很容易返回一些结果，比如关于哈姆雷特或者是莎士比亚等等的信息。系统对查询结
果所得到的网页的打分是根据在网页中所包含的你的搜索短语的多少，它们在文档的什么位置以及搜索短语在文档内部之间的距离来决定的。同时可以把得到的搜索
结果翻译成其他的语言。 <br /><br />　　Exite是称为具有"智能"的搜索引擎，因为它建立了一个基于概念的索引。当然，它所谓的"智能"是基
于对概率统计的灵活应用。它能够同时进行基于概念和关键字的索引。它能够索引Web,Usenet和分类的广告。支持"AND"，"OR"，"NOT"等
布尔操作，同时也可以使用符号"+"和"-"。缺点是在返回的查询结果中没有指定网页的尺寸和格式。 <br /><br />　　InfoSeek是一个简单
但是功能强大的索引，它的一个优点是有一个面向主题搜索的可扩展的分类。你可以把你的搜索短语和相似的分类目录的主题短语相互参照，而那些主题短语会自动
加到你的查询中去。使你的搜索有更好的主题相关性。同时它也支持对图象的查询。它能够漫游Web,Usenet,Usenet
FAQs等等。不支持布尔操作，但是可以使用符号"+"和"-"（相当于"AND"和"NOT"） <br /><br />　　Yahoo实际上不能称为是一
个搜索引擎站点，但是它提供了一个分层的主题索引，使你能够从一个通常的主题进入到一个特定的主题，Yahoo对Web进行了有效的组织和分类。比如你想
要建立一个网页，但是你不知道如何操作，为了在Yahoo上找到关于建立网页的信息，你可以先在Yahoo上选择一个主题：计算机和Internet，然
后在这个主题下，你可以发现一些子主题，比如：Web网页制作，CGI编程，JAVA，HTML，网页设计等，选择一个和你要找的相关的子主题，最终你就
可以得到和该子主题相关的所有的网页的链接。也就是说，如果你对要查找的内容属于哪个主题十分清楚的话，通过目录查询的方法要比一般的使用搜索引擎有更好
的准确率。你可以搜索Yahoo的索引，但是事实上，你并没有在搜索整个Web。但是Yahoo提供了选项使你可以同时搜索其他的搜索引擎，比如：
Alta Vista。但是要注意的是Yahoo实际上只是对Web的一小部分进行了分类和组织，而且它的实效性也不是很好。 <br /><br />　　搜索引擎的基本原理是通过网络机器人定期在web网页上爬行，然后发现新的网页，把它们取回来放到本地的数据库中，用户的查询请求可以通过查询本地的数据库来得到。如yahoo每天会找到大约500万个新的网页。 <br /><br />　
　搜索引擎的实现机制一般有两种，一种是通过手工方式对网页进行索引，比如yahoo的网页是通过手工分类的方式实现的，它的缺点是Web的覆盖率比较
低，同时不能保证最新的信息。查询匹配是通过用户写入的关键字和网页的描述和标题来进行匹配，而不是通过全文的匹配进行的。第二种是对网页进行自动的索
引，象AltaVista则是完全通过自动索引实现的。这种能实现自动的文档分类，实际上采用了信息提取的技术。但是在分类准确性上可能不如手工分类。
<br /><br />　　搜索引擎一般都有一个Robot定期的访问一些站点，来检查这些站点的变化，同时查找新的站点。一般站点有一个robot.txt文
件用来说明服务器不希望Robot访问的区域，Robot
都必须遵守这个规定。如果是自动索引的话，Robot在得到页面以后，需要对该页面根据其内容进行索引，根据它的关键字的情况把它归到某一类中。页面的信
息是通过元数据的形式保存的，典型的元数据包括标题、IP地址、一个该页面的简要的介绍，关键字或者是索引短语、文件的大小和最后的更新的日期。尽管元数
据有一定的标准，但是很多站点都采用自己的模板。文档提取机制和索引策略对Web搜索引擎的有效性有很大的关系。高级的搜索选项一般包括：布尔方法或者是
短语匹配和自然语言处理。一个查询所产生的结果按照提取机制被分成不同的等级提交给用户。最相关的放在最前面。每一个提取出来的文档的元数据被显示给用
户。同时包括该文档所在的URL地址。 <br /><br />　　另外有一些关于某一个主题的专门的引擎，它们只对某一个主题的内容进行搜索和处理，这样信息的取全率和精度相对就比较高。 <br /><br />　
　同时，有一类搜索引擎，它本身不用Robot去定期的采集网页。象SavvySearch 和
MetaCrawler是通过向多个搜索引擎同时发出询问并对结果进行综合返回给用户实现搜索功能。当然实际上象SavvySearch能够对各个搜索引
擎的功能进行分析和比较，根据不同的用户查询提交给不同的搜索引擎进行处理，当然用户自己也可以指定利用哪一个搜索引擎。 <br /><br />　　一个优秀的搜索引擎必须处理以下几个问题：1 网页的分类2 自然语言的处理3 搜索策略的调度和协作 4 面向特定用户的搜索。所以很多搜索引擎不同程度的使用了一些人工智能的技术来解决这些方面的问题。 <br /><br />　　二、网络Spider的实现描述 <br /><br />　
　现在有很多文章对Web引擎做了大量的介绍和分析，但是很少有对它们的实现做一个详细的描述，这里我们主要来介绍一个具有基本功能的Web引擎的实现。
本文，我们以类C++语言的形式来描述Web引擎如何采集网页并存放到数据库中的过程。同时描述了如何根据用户输入的关键字查询数据库并得到相关网页的过
程。 <br /><br />　　2.1数据库结构 <br /><br />　　首先，我们要建立一个数据库表用来存放我们得到的网页。这里一般需要建立如下的表： <br /><br />　　1.字典表的建立，事实上这里是用文档中有意义的单词和它们的出现频率来代表一个文档。 <br /><br />　　该表（WordDictionaryTbl）主要要包括三个字段，主要是用来存放和一个网页相关的单词的情况 <br /><br />    url_id    对每一个URL的唯一的ID号<br />    word      该URL中的经过stem的单词<br />    intag    该单词在该网页中的出现的次数<br /><br />　　2.存储每一个URL信息的表 <br /><br />　　该表(URLTbl)中主要的关键字段有：<br /><br />  rec_id        每一条记录的唯一的ID号<br />  status    得到该URL内容的状态，比如HTTP_STATUS_TIMEOUT表示<br />            下载网页的最大允许超时<br />  url        URL的字符串名称<br />  content_type      内容的类型<br />  last_modified    最新的更改时间<br />  title            该URL的标题<br />  docsize          该URL的文件的尺寸<br />  last_index_time  最近一次索引的时间<br />  next_index_time  下一次索引的时间<br />  tag    对于网页，用来表示它的类型，比如：是text，或者是html，<br />                    或者是图片等等<br />  hops              得到文件时候的曾经失败的次数<br />  keywords          对于网页，和该网页相关的关键字<br />  description      对于网页，指网页的内容的描述<br />  lang              文档所使用的语言<br /><br />　
　3.因为网页中有很多单词是一些介词和语气助词或者是非常常用的常用词，它们本身没有多少意义。比如：英语中的about,in,at,we,this
等等。中文中的如"和"，"一起"，"关于"等等。我们统一的把它们称为停止词（stop
word）。所以我们要建立一个表，来包括所有这些停止词。该表(StopWordTbl)主要有两个字段。 <br />word char(32)    表示那些停止词<br />lang char(2)      表示所使用的语言<br /><br />　　4.我们要建立一个关于robot的表，我们在前面说过，所有的网站一般都有一个robot.txt文件用来表示网络上的robot可以访问的权限。该表(RobotTbl)主要有以下字段。 <br />    hostinfo          Web站点主机的信息<br />    path              不允许robot访问的目录<br /><br />　　5.建立我们需要屏蔽的那些网页(比如一些内容不健康的或者没有必要去搜索的站点)的一张表(ForbiddenWWWTbl)，主要的字段就是网页的URL。 <br /><br />　
　6.另外我们需要建立一个我们所要得到的文件类型的表(FileTypeTbl)，比如，对于一个简单的Web搜索引擎，我们可能只需要得到后缀为.
html，htm，.shtml和txt的类型文件。其他的我们只是简单的忽略它们。主要的字段就是文件的类型和说明。 <br /><br />　　其中关于停止词的表的内容是我们要实现要根据各种语言的统计结果，把那些意义不大的单词放进去。关于文档单词、URL和Robot的表的内容都是在获取Web网页的时候动态增加记录的。 <br /><br />　　2.2 具体网页获取算法描述 <br /><br />　　具体的网页的获取步骤是这样的： <br /><br />　
　我们可以设定我们的搜索程序最大可以开的线程的数目，然后这些线程可以同时在网上进行搜索，它们根据数据库中已有的关于网页的信息，找出那些需要更新的
网页（如何判断哪些网页需要更新是一个值得研究的过程，现在有很多启发式和智能的算法，基本上是基于统计规律进行建模。最简单的当然是设定一个时间范围，
在某个时间范围以前的网页被重新去搜索一遍），然后判断那些网页是否在屏蔽表中，如果是的话，就从关于URL的表中删除该条记录。否则，我们就到相应的
WWW站点去得到URL指定的文件(这里需要注意的是根据不同的URL的特点，需要使用不同的协议，比如对于FTP站点要采用FTP协议，对于HTTP站
点要采用HTTP协议，新闻站点要采用NNTP协议等等)事实上，我们先得到关于该网页的头信息，如果该网页的最新修改时间和我们最近提取的时间是一样的
话，表示该网页内容没有任何更新，则我们就不必去得到它的内容，只需要修改最近一次更新它的时间为当前的时间就可以了。如果该网页最近做了修改，我们就要
得到该网页，并对它的内容进行分析，主要要包括和它相关的链接，把它们加到相应的数据库中，同时判断网页所包含的各种其他的文件，如文本文件、图形文件、
声音文件和其他多媒体文件是否是我们所需要的文件，如果是的话，就把它加到我们响应的数据库中。同时要根据网页的内容提取所有的有意义的单词和它们的出现
的次数，放到相应的数据库中。为了更好的描述这个过程，我们来看跟这个过程相关的主要的几个对象和数据结构。对象主要是针对三个层次来讲的。第一层是针对
WWW服务器，第二层是针对每一个页面，第三层是针对每一个页面的全文的索引。 <br /><br />　　2.3 和实现相关的主要类对象和功能描述下面的结构是针对一个站点来说的。 <br /><br />    Class  CServer {<br />    主要的属性有：<br />    char *url;            //WWW站点的URL名称<br />    char *proxy;          //使用的代理的名称<br />    char *basic_auth;      //进行基本的HTTP认证<br />    int  proxy_port;      //代理的端口号<br />    int  period;          //再次索引的周期<br />    int  net_errors;      //网络连接不通的次数<br />    int  max_net_errors;  //可以允许的最大的网络错误<br />    int  read_timeout;    //下载文件允许的最大的延迟<br />    int  maxhops;          //表示URL可以最大跳转的深度<br />    int  userobots;        //是否遵守robot.txt中的约定<br />    int  bodyweight;  // 在&lt; body &gt;....&lt; /body &gt;之间的单词的权重<br />    int  titleweight; // 在&lt; title &gt;....&lt; /title &gt;之间的单词的权重<br />    int  urlweight;  // 在文档的URL中的单词的权重<br />    int descweight;//在    &lt; META <br />NAME="Description"        Content="..." &gt;之间单词的权重<br />    int  keywordweight; //在&lt; META NAME="Keywords" Content="..." &gt;<br />  之间的单词的权重<br /><br />　　主要方法有： <br />FindServer();//用来查找该服务器是否存在并可以连接<br />FillDefaultAttribute() //用来针对所有的WWW服务器填写默认的属}； <br /><br />以上的对象中的成员变量是和一个站点相关的参数的设置，我们对所有的站点有一个默认的设置，但是可以对某些站点做一些特殊的设置。这些设置可以在配置文件中设定。 <br />　　下面是关于文档的结构的主要的数据成员： <br /><br />Class CNetDocument<br />    主要属性有：<br />    int    url_id; //该URL的ID号<br />    int    status;  //获取该文档时候的状态<br />    int    size;  //文档的尺寸<br />int    tag;  //和该文档相关的标签，表示该文档是<br />HTML，TEXT或者是其他类型<br />    int    hops;    //URL跳转的次数<br />    char    *url; //和该文档相关的URL的名称<br />    char    *content_type;      //该内容的类型<br />    char    *last_modified;    //最近一次的更新时间<br />    char    *title;            //该文档的标题<br />    char    *last_index_time;  //上次索引的时间<br />    char    *next_index_time;  //下次索引的时间<br />    char    *keywords;          //该文档中的关键字<br />    char    *description;      //该文档的描述<br /><br />  主要方法有：<br />  FillDocInfo(…) //根据数据库，得到该文档相关信息<br />  AddHerf(…)    //加入网页中存在的新的链接的网址<br />  DeleteURL(…)  //删除一个存在的网址<br />  CanGetThisURL(…) //根据配置决定是否去得到该网页<br />  //下面三个方法是根据不同的URL，用不同的协议去获得文档<br />  NNTPGet(…)      <br />  FTPGet(….)<br />  HTTPGet(….)<br />  ParseHead(…)  //如果是HTTP协议得到的话，分析头信息<br />  ParseMainBody(…)    //对获得的文档的主体进行分析<br />  ServerResponseType (….)  //得到服务器端的响应消息<br />  UpdateURLDB(….)  //更新的数据入库<br />} ；<br /><br />　　事实上，我们在要提取一个网页的时候，都要建立一个CNetDocument对象，然后再对这个网页进行分析的时候，把相关的内容放到这个CNetDocument的成员变量里面。下面是关于页面全文索引的结构的主要数据成员： <br />Class CIndexer {<br />主要属性有：<br />  char    *url;      //我们要处理的文档相关的URL的名称<br />  int mwords;      //  我们事先设定的一个网页的最大的单词数目<br />    int nwords;          // 实际的得到的单词的数目<br />    int swords;          // 我们已经排序的单词的数目<br />    WORD *Word;      //所有单词的内容<br />    char *buf;      //我们为文档所分配的空间<br />主要方法有：<br />  InitIndexer(…)    //进行初始设置和分配<br />  ParseGetFile(…)  //对得到的网页进行全文索引<br />  AddWord(…)    //把网页的可以索引的单词加到Word数组中去<br />  InToDB(….)    //关于网页全文索引的信息入库<br />}；<br /><br />　　进行网页提取前，我们要建立一个CIndexer对象，它主要是用来对网页进行全文的索引。一般来说我们只对两种类型的URL进行全文索引，一个是text/html，另外一个是text/plain。其中WORD的数据结构如下： <br />        typedef struct word_struct {<br />    int count;  //该单词出现的次数<br />    int code;  //该单词的正常的形式，<br />比如单词可能为 encouraging,它的正常的形式应该为<br />encourage,这其实是一种对单词的stem。<br />即我们只取单词的主干部分。<br />    char *word;  //该单词的内容<br />} WORD;<br /><br />　　以下的结构是和网页中的一些链接的对象相关的一个数据结构 <br />    typedef struct href_struct {<br />    char *href;    //该链接的名称<br />    int hops;      //发生的跳转次数<br />    int stored;    //是否已经存储到数据库中<br />} HREF;<br />  <br /><br />　　所有需要更新的和新产生的URL都被放到这个结构中，当它的数量超过一定的范围以后，被一次性的存入数据库。 <br />　　关于URL的一个数据结构如下： <br /><br />typedef struct url {<br />char *schema; //表示该URL是通过什么协议得到的，比如HTTP， <br />              FTP，NNTP等。<br />char *specific;    //主机的名称加上路径<br />char *hostinfo;    //主机的名称加上相关的协议端口<br />char *hostname;    //主机的名称<br />char *path;        //在主机的具体的路径<br />char *filename;    //文件的名称<br />char *anchor;      //相关的anchor<br />int  port;        //协议相关的端口<br />} URL;<br /><br />　　这是针对URL的一些相关的属性的描述的一个数据结构。事实上在数据库中，我们存储的只是对网页的描述和对一些文本和HTML页面的关键词的索引信息。我们并不存储网页的实际的内容。 
<p>　　三、用户查询实现描述 <br /><br />　　关于对用户提交的查询请求的实现分析： <br /><br />　　用户想要查询某一方面的信息一般都是通过提供和该领域相关的几个关键字来进行的。 <br /><br />　　我们来看一下关于用户查询的相关的数据结构和类： <br /><br />　　下面是一个关于单词和它的权值的基本结构： <br /><br />  typedef struct word_weight_pair<br />    {<br />      char word[WORD_LEN];<br />      int weight;<br />    }word_weight_pair;<br />    <br /><br />　　下面的类主要是用来对用户的查询进行处理和分析： <br />    Class CUserQuery<br />    {<br />char m_UserQuery[MAX_QUERYLEN];  //用户的查询表达式<br />CPtrArray word_weight_col; <br />//是关于结构word_weight_pair的动态数组<br />int m_maxReturnSum;  //用户希望返回的最多的网页数<br />int search_mode;<br />CObArray m_returnDoc;  //是关于CNetDocument对象的一个动态数组<br />NormalizeWord（char* OneWord）;  //对单词进行归整化，即Stem.<br />Find(char* odbcName);  //进行数据库查找和匹配<br />}；<br /><br />　　系统实现的基本的步骤如下： <br /><br />　　1.对用户输入的查询表达式进行分析。事实上，我们在前面的Spider搜索过程中对文档的表示是通过关键字形式描述的，每一个文档可以表示为这样的一个集合 <br /><br />    其中 ::=&lt; 单词或短语名称 &gt;&lt; 单词或短语的权值 &gt;<br /><br />　　实际上就是采用矢量空间的表示方法来表示的文档。 <br /><br />　
　我们对用户输入的查询表达式也采用矢量空间的表示方法。我们认为用户输入的关键字的顺序代表了它的重要性的程度，所以对于位置靠前的单词有相对比较高的
优先级，同时我们对所有的内容以短语或者是单词为最小原子，进行Stem操作，即象前面所提到的：比如单词Encouraging就转化成
Encourage的格式。然后去掉那些Stop Word，比如is ,as等等的单词，这些单词存放在StopWordTbl表中。
然后把所有归整化后的内容放入动态数组word_weight_col中去。 <br /><br />　　2.对于动态数组word_weight_col中
的每一个元素，即结构word_weight_pair（包括单词和该单词的权重），我们从表WordDictionaryTbl中可以找到和这些单词相
关的记录，这些记录应该是包括了所有的在word_weight_col中的单词。 <br /><br />　　进行网页是否和查询相匹配的计算。匹配计算的
过程如下：首先我们对所有的记录按URL地址进行排序。因为可能好几条记录对应的是一个URL，然后对每一个网页进行打分，每一条记录的单词权值为
INITSCORE*WEIGHT+（TOTALTIMES-1）*WEIGHT*
INCREMENT。其中INITSCORE为每一个单词的基准分数，TOTALTIMES为该单词在网页中的出现的次数，WEIGHT是该单词在不同的
内容段出现有不同的权值（比如在KEYWORD段，或者是标题段，或者是内容段等等）。INCREMENT是该单词每多出现一次所增加的分数。 <br /><br />　　3.根据用户指定的m_maxReturnSum，显示匹配程度最高的前m_maxReturnSum页。 <br /><br />　　四、结束语 <br /><br />　
　我们利用上面所讨论的机制，在WINDOWS NT操作系统下，用VC++和SQL
SERVER实现了一个Web搜索引擎的网页搜集过程。在建立了一个基本的搜索引擎的框架以后，我们可以基于这个框架，实现一些我们自己设计的算法，比如
如何更好的进行Spider的调度，如何更好的进行文档的归类，如何更好的理解用户的查询，用来使Web搜索引擎具有更好的智能性和个性化的特点。</p></span>
<img src ="http://www.blogjava.net/coolfiry/aggbug/80621.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/coolfiry/" target="_blank">Coolfiry</a> 2006-11-11 21:37 <a href="http://www.blogjava.net/coolfiry/archive/2006/11/11/80621.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>c++中char * 和 char []的区别 </title><link>http://www.blogjava.net/coolfiry/archive/2006/10/16/75467.html</link><dc:creator>Coolfiry</dc:creator><author>Coolfiry</author><pubDate>Mon, 16 Oct 2006 11:06:00 GMT</pubDate><guid>http://www.blogjava.net/coolfiry/archive/2006/10/16/75467.html</guid><wfw:comment>http://www.blogjava.net/coolfiry/comments/75467.html</wfw:comment><comments>http://www.blogjava.net/coolfiry/archive/2006/10/16/75467.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/coolfiry/comments/commentRss/75467.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/coolfiry/services/trackbacks/75467.html</trackback:ping><description><![CDATA[
		<div class="postTitle">
				<a href="http://blog.csdn.net/cqcmdwym/archive/2006/10/15/1335614.aspx">
						<img height="13" src="http://blog.csdn.net/images/authorship.gif" width="15" border="0" /> c++中char * 和 char []的区别</a>
		</div>
		<div class="postText">
				<p>问题引入：<br />在实习过程中发现了一个以前一直默认的错误，同样char *c = "abc"和char c[]="abc",前者改变其内</p>
				<p>容程序是会崩溃的，而后者完全正确。<br />程序演示：<br />测试环境Devc++<br />代码<br />#include &lt;iostream&gt;<br />using namespace std;</p>
				<p>main()<br />{<br />   char *c1 = "abc";<br />   char c2[] = "abc";<br />   char *c3 = ( char* )malloc(3);<br />   c3 = "abc";<br />   printf("%d %d %s\n",&amp;c1,c1,c1);<br />   printf("%d %d %s\n",&amp;c2,c2,c2);<br />   printf("%d %d %s\n",&amp;c3,c3,c3);<br />   getchar();<br />}   <br />运行结果<br />2293628 4199056 abc<br />2293624 2293624 abc<br />2293620 4199056 abc</p>
				<p>参考资料：<br />首先要搞清楚编译程序占用的内存的分区形式：<br />一、预备知识—程序的内存分配<br />一个由c/C++编译的程序占用的内存分为以下几个部分<br />1、栈区（stack）—由编译器自动分配释放，存放函数的参数值，局部变量的值等。其操作方式类似于</p>
				<p>数据结构中的栈。<br />2、堆区（heap）—一般由程序员分配释放，若程序员不释放，程序结束时可能由OS回收。注意它与数据</p>
				<p>结构中的堆是两回事，分配方式倒是类似于链表，呵呵。<br />3、全局区（静态区）（static）—全局变量和静态变量的存储是放在一块的，初始化的全局变量和静态</p>
				<p>变量在一块区域，未初始化的全局变量和未初始化的静态变量在相邻的另一块区域。程序结束后由系统</p>
				<p>释放。<br />4、文字常量区—常量字符串就是放在这里的。程序结束后由系统释放。<br />5、程序代码区<br />这是一个前辈写的，非常详细<br />//main.cpp<br />  int a=0;    //全局初始化区<br />  char *p1;   //全局未初始化区<br />  main()<br />  {<br />   int b;栈<br />   char s[]="abc";   //栈<br />   char *p2;         //栈<br />   char *p3="123456";   //123456\0在常量区，p3在栈上。<br />   static int c=0；   //全局（静态）初始化区<br />   p1 = (char*)malloc(10);<br />   p2 = (char*)malloc(20);   //分配得来得10和20字节的区域就在堆区。<br />   strcpy(p1,"123456");   //123456\0放在常量区，编译器可能会将它与p3所向"123456"优化成一个</p>
				<p>地方。<br />}<br />二、堆和栈的理论知识<br />2.1申请方式<br />stack:<br />由系统自动分配。例如，声明在函数中一个局部变量int b;系统自动在栈中为b开辟空间<br />heap:<br />需要程序员自己申请，并指明大小，在c中malloc函数<br />如p1=(char*)malloc(10);<br />在C++中用new运算符<br />如p2=(char*)malloc(10);<br />但是注意p1、p2本身是在栈中的。<br />2.2<br />申请后系统的响应<br />栈：只要栈的剩余空间大于所申请空间，系统将为程序提供内存，否则将报异常提示栈溢出。<br />堆：首先应该知道操作系统有一个记录空闲内存地址的链表，当系统收到程序的申请时，<br />会遍历该链表，寻找第一个空间大于所申请空间的堆结点，然后将该结点从空闲结点链表中删除，并将</p>
				<p>该结点的空间分配给程序，另外，对于大多数系统，会在这块内存空间中的首地址处记录本次分配的大</p>
				<p>小，这样，代码中的delete语句才能正确的释放本内存空间。另外，由于找到的堆结点的大小不一定正</p>
				<p>好等于申请的大小，系统会自动的将多余的那部分重新放入空闲链表中。<br />2.3申请大小的限制<br />栈：在Windows下,栈是向低地址扩展的数据结构，是一块连续的内存的区域。这句话的意思是栈顶的地</p>
				<p>址和栈的最大容量是系统预先规定好的，在WINDOWS下，栈的大小是2M（也有的说是1M，总之是一个编译</p>
				<p>时就确定的常数），如果申请的空间超过栈的剩余空间时，将提示overflow。因此，能从栈获得的空间</p>
				<p>较小。<br />堆：堆是向高地址扩展的数据结构，是不连续的内存区域。这是由于系统是用链表来存储的空闲内存地</p>
				<p>址的，自然是不连续的，而链表的遍历方向是由低地址向高地址。堆的大小受限于计算机系统中有效的</p>
				<p>虚拟内存。由此可见，堆获得的空间比较灵活，也比较大。<br />2.4申请效率的比较：<br />栈:由系统自动分配，速度较快。但程序员是无法控制的。<br />堆:是由new分配的内存，一般速度比较慢，而且容易产生内存碎片,不过用起来最方便.<br />另外，在WINDOWS下，最好的方式是用Virtual Alloc分配内存，他不是在堆，也不是在栈,而是直接在进</p>
				<p>程的地址空间中保留一块内存，虽然用起来最不方便。但是速度快，也最灵活。<br />2.5堆和栈中的存储内容<br />栈：在函数调用时，第一个进栈的是主函数中后的下一条指令（函数调用语句的下一条可执行语句）的</p>
				<p>地址，然后是函数的各个参数，在大多数的C编译器中，参数是由右往左入栈的，然后是函数中的局部变</p>
				<p>量。注意静态变量是不入栈的。<br />当本次函数调用结束后，局部变量先出栈，然后是参数，最后栈顶指针指向最开始存的地址，也就是主</p>
				<p>函数中的下一条指令，程序由该点继续运行。<br />堆：一般是在堆的头部用一个字节存放堆的大小。堆中的具体内容由程序员安排。<br />2.6存取效率的比较<br />char s1[]="aaaaaaaaaaaaaaa";<br />char *s2="bbbbbbbbbbbbbbbbb";<br />aaaaaaaaaaa是在运行时刻赋值的；<br />而bbbbbbbbbbb是在编译时就确定的；<br />但是，在以后的存取中，在栈上的数组比指针所指向的字符串(例如堆)快。<br />比如：<br />#include<br />voidmain()<br />{<br />char a=1;<br />char c[]="1234567890";<br />char *p="1234567890";<br />a = c[1];<br />a = p[1];<br />return;<br />}<br />对应的汇编代码<br />10:a=c[1];<br />004010678A4DF1movcl,byteptr[ebp-0Fh]<br />0040106A884DFCmovbyteptr[ebp-4],cl<br />11:a=p[1];<br />0040106D8B55ECmovedx,dwordptr[ebp-14h]<br />004010708A4201moval,byteptr[edx+1]<br />004010738845FCmovbyteptr[ebp-4],al<br />第一种在读取时直接就把字符串中的元素读到寄存器cl中，而第二种则要先把指针值读到edx中，在根据</p>
				<p>edx读取字符，显然慢了。<br />2.7小结：<br />堆和栈的区别可以用如下的比喻来看出：<br />使用栈就象我们去饭馆里吃饭，只管点菜（发出申请）、付钱、和吃（使用），吃饱了就走，不必理会</p>
				<p>切菜、洗菜等准备工作和洗碗、刷锅等扫尾工作，他的好处是快捷，但是自由度小。<br />使用堆就象是自己动手做喜欢吃的菜肴，比较麻烦，但是比较符合自己的口味，而且自由度大。</p>
				<p>自我总结：<br />char *c1 = "abc";实际上先是在文字常量区分配了一块内存放"abc",然后在栈上分配一地址给c1并指向</p>
				<p>这块地址，然后改变常量"abc"自然会崩溃</p>
				<p>然而char c2[] = "abc",实际上abc分配内存的地方和上者并不一样，可以从<br />4199056<br />2293624 看出，完全是两块地方，推断4199056处于常量区，而2293624处于栈区</p>
				<p>2293628<br />2293624<br />2293620 这段输出看出三个指针分配的区域为栈区，而且是从高地址到低地址</p>
				<p>2293620 4199056 abc 看出编译器将c3优化指向常量区的"abc"</p>
				<p>
						<br />继续思考：<br />代码：<br />#include &lt;iostream&gt;<br />using namespace std;</p>
				<p>main()<br />{<br />   char *c1 = "abc";<br />   char c2[] = "abc";<br />   char *c3 = ( char* )malloc(3);<br />   //  *c3 = "abc" //error<br />   strcpy(c3,"abc");<br />   c3[0] = 'g';<br />   printf("%d %d %s\n",&amp;c1,c1,c1);<br />   printf("%d %d %s\n",&amp;c2,c2,c2);<br />   printf("%d %d %s\n",&amp;c3,c3,c3);<br />   getchar();<br />}   <br />输出：<br />2293628 4199056 abc<br />2293624 2293624 abc<br />2293620 4012976 gbc<br />写成注释那样，后面改动就会崩溃<br />可见strcpy(c3,"abc");abc是另一块地方分配的，而且可以改变，和上面的参考文档说法有些不一定，</p>
				<p>而且我不能断定4012976是哪个区的，可能要通过算区的长度，希望高人继续深入解释，谢谢<br /> </p>
		</div>
<img src ="http://www.blogjava.net/coolfiry/aggbug/75467.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/coolfiry/" target="_blank">Coolfiry</a> 2006-10-16 19:06 <a href="http://www.blogjava.net/coolfiry/archive/2006/10/16/75467.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>VC++内部COM插件的编程实现</title><link>http://www.blogjava.net/coolfiry/archive/2006/09/30/73101.html</link><dc:creator>Coolfiry</dc:creator><author>Coolfiry</author><pubDate>Sat, 30 Sep 2006 14:58:00 GMT</pubDate><guid>http://www.blogjava.net/coolfiry/archive/2006/09/30/73101.html</guid><wfw:comment>http://www.blogjava.net/coolfiry/comments/73101.html</wfw:comment><comments>http://www.blogjava.net/coolfiry/archive/2006/09/30/73101.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/coolfiry/comments/commentRss/73101.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/coolfiry/services/trackbacks/73101.html</trackback:ping><description><![CDATA[
		<strong>一、闲聊<br /><br /></strong>　　今天要谈的话题是COM，稍微深入一点，不知道大家用过C++Test或者Visual Assistant之类的软件没有，它们都有个非常引人注目的功能，那就是把它们自身嵌入到VC开发环境中去。这个功能让我痴迷不已，原因只有一个：我想做一个可以嵌入VC开发环境的VC工程解析器，这样用户在VC开发环境中就可以直接对当前或所有工程进行各种分析，统计。那么实现它简单吗？简单，Next和Copy即可轻松完成；仅仅这些吗？不是，它的背后还有博大精深的COM做支撑。不管困难与否，还是让我们先试为快。 <br /><br />　　<b>二、效果图</b><br /><br /><table width="90%" align="center" border="0"><tbody><tr><td><div align="center"><img height="121" alt="" src="http://dev.yesky.com/imagelist/06/04/081714w5577v.gif" width="671" /> </div></td></tr></tbody></table><br />　　<b>三、实现步骤：</b><br /><br />　　&lt;3.1&gt;新建一个&lt;DevStudio Add-in Wizard&gt;类型工程,输入工程名称"CodeAnalyser". <br /><br />　　&lt;3.2&gt;进入第二个画面，系统要求用户输入插件的名称和描述信息。并且要求用户选择是否需要生成工具栏以及是否自动添加VC事件响应代码。<br /><br /><table width="90%" align="center" border="0"><tbody><tr><td><div align="center"><img height="357" alt="" src="http://dev.yesky.com/imagelist/06/04/zx439ozgkc13.gif" width="609" /></div></td></tr></tbody></table><br />　　&lt;3.3&gt;点击"Finish"结束向导，进入代码编辑窗口。<br /><br />　　在这里我们要说的一点是：该工程引用了ICommands接口，并从该接口上派生出 CCommands类。该类完成了所有用户自定义函数接口，VC应用程序消息响应和VC调试动作的消息响应工作。当我们真正为CCommands类添加成员函数之前我们必须先为ICommands接口添加相应的函数接口声明。在本工程中我总共为ICommands接口添加了两个函数接口，它们名字分别为：GetCurDirCommandMethod和QuitCommandMethod声明如下：(在CodeAnalyer.odl文件中)<br /><br /><table bordercolor="#cccccc" width="90%" align="center" bgcolor="#e3e3e3" border="1"><tbody><tr><td>interface ICommands : IDispatch<br />{<br />　// methods<br />　[id(1)] //在Vtable中的函数索引号<br />　HRESULT GetCurDirCommandMethod(); //得到VC当前工作目录<br /><br />　[id(2)] //在Vtable中的函数索引号 <br />　HRESULT QuitCommandMethod (); //退出VC编辑器<br />};</td></tr></tbody></table><br />　　在接口ICommands添加接口函数，那么相应的我们也要在类CCommands中声明和实现ICommands接口函数，函数的内部代码和普通工程代码没什么区别。<br /><br /><table bordercolor="#cccccc" width="90%" align="center" bgcolor="#e3e3e3" border="1"><tbody><tr><td>//Implement(CCommands类内部接口函数的声明)<br />public:<br />STDMETHOD(GetCurDirCommandMethod)(THIS);<br />STDMETHOD(QuitCommandMethod)(THIS);<br /><br />//Function Code(Ccommands类内部接口函数的实现)<br />//得到当前VC开发环境的工作目录[您也可以让它成为你想要实现的功能代码]<br />STDMETHODIMP CCommands::GetCurDirCommandMethod() <br />{<br />　AFX_MANAGE_STATE(AfxGetStaticModuleState());<br />　VERIFY_OK(m_pApplication-&gt;EnableModeless(VARIANT_FALSE));<br />　BSTR bstrCurDir;<br />　m_pApplication-&gt;get_CurrentDirectory(&amp;bstrCurDir);<br />　CString str(bstrCurDir);<br />　::MessageBox(NULL, str, "VC工作目录", MB_OK | MB_ICONINFORMATION);<br />　VERIFY_OK(m_pApplication-&gt;EnableModeless(VARIANT_TRUE));<br />　return S_OK;<br />}<br /><br />//退出VC开发环境<br /><br />STDMETHODIMP CCommands::QuitCommandMethod()<br />{<br />　AFX_MANAGE_STATE(AfxGetStaticModuleState());<br />　VERIFY_OK(m_pApplication-&gt;EnableModeless(VARIANT_FALSE));<br />　if(::MessageBox(NULL,"您想退出VC++编辑器吗(Y/N)?","询问信息...", MB_YESNO | MB_ICONQUESTION) == IDYES)<br />　　m_pApplication-&gt;Quit();<br />　　VERIFY_OK(m_pApplication-&gt;EnableModeless(VARIANT_TRUE));<br />　return S_OK;<br />}</td></tr></tbody></table><br />　　&lt;3.4&gt; 创建工具栏，连接工具栏按钮事件<br /><br />　　所有的幕后工作已经准备就绪，只差个工具栏界面就一切OK了。打开类CDSAddIn,它里面有三个成员函数，其中OnConnection和OnDisconnection成员函数的意义非常重要。它们的意义如下：<br /><br />　　&lt;1&gt;OnConnection：插件的初始化任务都在这里完成。如COM服务的启动，工具栏/菜单栏的创建，工具栏按钮/菜单项的添加与修改等等。<br /><br />　　&lt;2&gt;OnDisconnection：插件的卸载工作都在这里完成。如COM服务的卸载，工具栏/菜单栏的销毁，释放等等。 <br /><br />　　了解了它们各自的用途之后我们就可以在相应的消息事件中添加代码了。很显然工具栏的初始化应该在OnConnection事件中完成。<br /><br />　　在OnConnection事件中系统首先获得了VC应用程序接口，然后调用一个接口函数:AddCommand来为插件添加命令和命令影射函数。然后再使用另外一个接口函数AddCommandBarButton向工具栏中添加工具栏按钮，其中每个工具栏按钮会和一个命令标志符号相连接，这样就能实现按钮和命令（消息）之间的一一对应。下面是添加一个命令和一个工具栏按钮的代码（如果你要添加多个工具栏按钮只要重复此步骤即可）： <br /><br /><table bordercolor="#cccccc" width="90%" align="center" bgcolor="#e3e3e3" border="1"><tbody><tr><td>LPCTSTR szCommand = _T("GetCurDirCommand"); <br />VARIANT_BOOL bRet;<br />CString strCmdString;<br />strCmdString.LoadString(IDS_CMD_STRING);<br />strCmdString = szCommand + strCmdString;<br />CComBSTR bszCmdString(strCmdString);<br />CComBSTR bszMethod(_T("GetCurDirCommandMethod"));<br /><br />CComBSTR bszCmdName(szCommand); //和下面添加工具栏按钮对应<br /><br />VERIFY_OK(pApplication-&gt;AddCommand(bszCmdString,bszMethod,0,dwCookie,&amp;bRet));<br />//AddCommand 参数含义：<br />//bszCmdString：命令字符串。<br />//bszMethod：Icommands接口函数名。<br />//第三个参数代表位图偏移量。<br />//第四和第五个参数分贝为系统参数和返回值（参照MSDN的IApplication介绍） <br /><br />if (bRet == VARIANT_FALSE)<br />{<br />　*OnConnection = VARIANT_FALSE;<br />　return S_OK;<br />}<br /><br />//添加工具栏按钮<br />if (bFirstTime == VARIANT_TRUE)<br />{<br />　VERIFY_OK(pApplication-&gt;AddCommandBarButton(dsGlyph, bszCmdName, m_dwCookie));<br />}</td></tr></tbody></table><br />　　&lt;3.5&gt; 编译，连接及在VC中引入插件<br /><br />　　以上就是我们所有的代码工作，接下来赶快Build以下吧。编译通过的话，在你的工程Debug目录下会有个dll文件。然后打开VC编辑器，在VC任何一个工具栏上点击鼠标右键，弹出如下图所示菜单。然后选择”Customize”子菜单,打开如下图所示的工具栏定制窗口：<br /><br /><table width="90%" align="center" border="0"><tbody><tr><td><div align="center"><img height="345" alt="" src="http://dev.yesky.com/imagelist/06/04/nf483785wv6z.gif" width="118" /><img height="357" alt="" src="http://dev.yesky.com/imagelist/06/04/fna9ccqwmu5a.gif" width="581" /></div></td></tr></tbody></table><br />　　接着选择该窗口的最后一页"Add-Ins and Macro Files"出现下图所示窗口。<br /><br /><table width="90%" align="center" border="0"><tbody><tr><td><div align="center"><img height="357" alt="" src="http://dev.yesky.com/imagelist/06/04/u19m0a685dxl.gif" width="582" /></div></td></tr></tbody></table><br />　　然后点击”Browse...”按钮，这时打开你工程下的Debug目录中的DLL文件，这样你就可以看到你制作的工具栏了。同样你再次打开上面的菜单，这次可以看到多了一个工具栏，并且名字乱七八糟的，怎么改变工具栏的名字呢？方法很简单：打开上面窗口中的”Toolbars”选项页，在工具栏列表框中找到你的工具栏，然后在”Toolbar name”编辑框中输入你想要的名字即可。再打开上面的菜单看看名字是不是变了，哈哈！ <br /><br /><table width="90%" align="center" border="0"><tbody><tr><td><div align="center"><img height="322" alt="" src="http://dev.yesky.com/imagelist/06/04/h9ar3t98rs66.gif" width="581" /></div></td></tr></tbody></table><br />　　OK，今天的话题就聊到这里。 <img src ="http://www.blogjava.net/coolfiry/aggbug/73101.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/coolfiry/" target="_blank">Coolfiry</a> 2006-09-30 22:58 <a href="http://www.blogjava.net/coolfiry/archive/2006/09/30/73101.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Google Desktop插件开发之建立开发环境</title><link>http://www.blogjava.net/coolfiry/archive/2006/09/30/73098.html</link><dc:creator>Coolfiry</dc:creator><author>Coolfiry</author><pubDate>Sat, 30 Sep 2006 14:55:00 GMT</pubDate><guid>http://www.blogjava.net/coolfiry/archive/2006/09/30/73098.html</guid><wfw:comment>http://www.blogjava.net/coolfiry/comments/73098.html</wfw:comment><comments>http://www.blogjava.net/coolfiry/archive/2006/09/30/73098.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/coolfiry/comments/commentRss/73098.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/coolfiry/services/trackbacks/73098.html</trackback:ping><description><![CDATA[
		<div class="guanggao">
				<span id="ad3">
				</span>
		</div>
		<div class="guanggao">
				<span id="contentAdv">
				</span>
		</div>　　Google推荐的开发环境是VS 2003，GoogleDesktop的插件是基于COM的，而COM是语言无关的，所以你可以用任何能开发COM的工具（语言）开发。 <br /><br />　　如果你使用的VS 2003或者VS 2005，建立开发环境非常容易。不过，如果你像我一样恋旧，还是喜爱VC6的简洁快速，排斥庞大缓慢的VS 2003或者VS 2005，可能就要费一点周折了。<br /><br />　　这里只讨论VC6的环境设置。<br /><br />　　Google没有为VC6 提供开发向导，也就是说，所有代码你都得手工就编写。如果是出于学习的目的，手工去写这些代码，付出的劳动会有所回报的。另外，VC6所带的ATL版本也有点老，一些类只有在新版本中才有，在VC6中无法使用，所以有时你不得不面对一些COM的细节问题。同样，同样如果出于学习的目的，所花费的时间也是值得的。<br /><br />　　建立开发环境的第一步就是下载GoogleDesktop的SDK，下载地址为http://desktop.google.com/。<br /><br />　　解开之后，GD_SDK\api目录下有下面几个目录：<br /><br /><table bordercolor="#cccccc" width="90%" align="center" bgcolor="#e1e1e1" border="1"><tbody><tr><td>documentation<br />samples<br />tools<br />wizards</td></tr></tbody></table><br />　　建议先大概看一下documentation中的文档，然后阅读samples中的部分代码，找一下感觉。<br /><br />　　GoogleDesktop提供全部接口都在三个IDL文件中声明：<br /><br /><table bordercolor="#cccccc" width="90%" align="center" bgcolor="#e1e1e1" border="1"><tbody><tr><td>GoogleDesktopActionAPI.idl<br />GoogleDesktopAPI.idl<br />GoogleDesktopDisplayAPI.idl</td></tr></tbody></table><br />　　开发GoogleDesktop的插件，有以上文件已经足够（当然你要安装GoogleDesktop本身）了。但是C++中不能直接使用idl文件，要通过midl.exe编译成头文件，才能使用。其实不用这么麻烦，GD_SDK\api\samples\common目录中已经有相关头文件了：<br /><br />GoogleDesktopDisplayAPI.h<br /><br />GoogleDesktopComponentRegistration.h<br /><br />GoogleDesktopAPI.h<br /><br />GoogleDesktopActionAPI.h<br /><br />　　直接使用这几个头文件，可以省去用midl编译步骤。只要修改VC6的设置，让它可以找到上述头文件就行了。有两种方式可以做到这一点。一种方式是针对当前项目的：<br /><br />　　1. 打开菜单Project-&gt;Settings<br /><br />　　2. 打开属性页的C/C++标签<br /><br />　　3. 选择Categary的Preprocessor项<br /><br />　　4. 在Additional Include directories一栏加入上述文件所在的目录<br /><br />　　另一种方式是针对VC6所有的项目的：<br /><br />　　1. 打开菜单Tool-&gt;Options…<br /><br />　　2. 打开属性页的Directories标签<br /><br />　　3. 选择Show directories for中的include files项<br /><br />　　4. 在Directories中加上述文件所在的目录<br /><br />　　至于选择哪一种方式，完全看你个人爱好，后者会方便一点，对懒人比较适用，但它会影响所有的VC6项目，或许会有某些副作用。<br /><img src ="http://www.blogjava.net/coolfiry/aggbug/73098.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/coolfiry/" target="_blank">Coolfiry</a> 2006-09-30 22:55 <a href="http://www.blogjava.net/coolfiry/archive/2006/09/30/73098.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>N皇后问题 </title><link>http://www.blogjava.net/coolfiry/archive/2006/09/27/72397.html</link><dc:creator>Coolfiry</dc:creator><author>Coolfiry</author><pubDate>Wed, 27 Sep 2006 14:20:00 GMT</pubDate><guid>http://www.blogjava.net/coolfiry/archive/2006/09/27/72397.html</guid><wfw:comment>http://www.blogjava.net/coolfiry/comments/72397.html</wfw:comment><comments>http://www.blogjava.net/coolfiry/archive/2006/09/27/72397.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/coolfiry/comments/commentRss/72397.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/coolfiry/services/trackbacks/72397.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: N皇后问题是一个典型的需要用回溯算法来解决的问题。回溯算法可以用递归方法来实现，也可以用非递归方法来实现。用递归的方法来解决回溯的问题思路很清晰，但是耗费的内存资源较多，速度也较慢；非递归方法具有速度快和耗费较少内存资源的优点，但是程序的逻辑结构却很复杂——不过搞懂之后觉得也很简单。   在写非递归算法之前，参考了网上的一些文章，但是觉得那些程序都很晦涩难懂，而且存在一些问题，我索性自己写了一个，...&nbsp;&nbsp;<a href='http://www.blogjava.net/coolfiry/archive/2006/09/27/72397.html'>阅读全文</a><img src ="http://www.blogjava.net/coolfiry/aggbug/72397.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/coolfiry/" target="_blank">Coolfiry</a> 2006-09-27 22:20 <a href="http://www.blogjava.net/coolfiry/archive/2006/09/27/72397.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>多线程与一个端口的问题</title><link>http://www.blogjava.net/coolfiry/archive/2006/08/19/64568.html</link><dc:creator>Coolfiry</dc:creator><author>Coolfiry</author><pubDate>Sat, 19 Aug 2006 14:49:00 GMT</pubDate><guid>http://www.blogjava.net/coolfiry/archive/2006/08/19/64568.html</guid><wfw:comment>http://www.blogjava.net/coolfiry/comments/64568.html</wfw:comment><comments>http://www.blogjava.net/coolfiry/archive/2006/08/19/64568.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.blogjava.net/coolfiry/comments/commentRss/64568.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/coolfiry/services/trackbacks/64568.html</trackback:ping><description><![CDATA[多线程与一个端口的问题<br />不知道在一个进程中的多线程同时运行,使用同一端口会出现什么样的情况?<br /><br />比如:开5个线程下载,下载的时候,每一个线程是否是能正确的接收到数据.如果能,那么Socket是针对线程级的,但那样的话,这几个线程就不能同时使用一个端口了,应该是会报差的哈.<br /><br />还有一点,在下的时候好像可不指明接入的端口号哈?<br /><br /><img src ="http://www.blogjava.net/coolfiry/aggbug/64568.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/coolfiry/" target="_blank">Coolfiry</a> 2006-08-19 22:49 <a href="http://www.blogjava.net/coolfiry/archive/2006/08/19/64568.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>