博文

如何阅读源代码之三(转帖)(2006-12-06 22:51:00)

摘要:在上面曾经提到过,这是DNS解析的代码部分,可以略过不看,不会影响对整个程序的理解。
/* prep hostname */
if (!hname)
{
if (uname(&system_info)) hname="localhost";
else hname=system_info.nodename;
}
这一段继续处理参数做准备工作。如果在命令行中指定了hostname(机器名)则采用指定的名称,否则调用uname查找机器名,如果没有,则用localhost来作为机器名。(同样在README中说得很详细)
/* get past history */
if (ignore_hist) {if (verbose>1) printf("%s ",msg_ign_hist); }
else get_history();
如果在命令行中指定了忽略历史文件,则不读取历史文件,否则调用get_history()来读取历史数据。在这里,我们可以回想在README文件中同样说过这一细节,在命令行或者配置文件中都能指定这一开关。需要说明的是,我们在这里并不一定需要去看get_history这一函数,因为从函数的名称,README文件和程序注释都能很清楚的得知这一函数的功能,不一定要去看代码。而如果要猜想的话,也可以想到,history是webalizer在上次运行的时候记录下来的一个文件,而这个文件则是去读取它,并将它的数据包括到这次的分析中去。不信,我们可以来看看。
void get_history()
{
int i,numfields;
FILE *hist_fp;
char buffer[BUFSIZE];

/* first initalize internal array */
for (i=0;i<12;i++)
{
hist_month[i]=......

阅读全文(2898) | 评论:0

如何阅读源代码之二(转帖)(2006-12-06 22:50:00)

摘要:作为一个C程序,在头文件里面,和C文件里面定义的extern变量,结构等等肯定不会少,但是,单独看这些东西我们不可能对这个程序有什么认识。所以,从main函数入手,逐步分析,在需要的时候再回头来看这些数据结构定义才是好的方法。(顺便说一句,Visual C++, 等windows下的IDE工具提供了很方便的方法来获取函数列表,C++的类列表以及资源文件,对于阅读源代码很有帮助。Unix/Linux也有这些工具,但是,我们在这里暂时不说,而只是通过最简单的文本编辑器vi来讲)。跳过webalizer.c开头的版权说明部分(GPL的),和数据结构定义,全局变量声明部分,直接进入main()函数。在函数开头,我们看到:
/* initalize epoch */
epoch=jdate(1,1,1970); /* used for timestamp adj. */

/* add default index. alias */
add_nlist("index.",&index_alias);
这两个函数暂时不用仔细看,后面会提到,略过。
sprintf(tmp_buf,"%s/webalizer.conf",ETCDIR);
/* check for default config file */
if (!access("webalizer.conf",F_OK))
get_config("webalizer.conf");
else if (!access(tmp_buf,F_OK))
get_config(tmp_buf);
从注释和程序本身可以看出,这是查找是否存在一个叫做webalizer.conf的配置文件,如果当前目录下有,则用get_config来读入其中内容,如果没有,则查找ETCDIR/webalizer.conf是否存在。如果都没有,则进入下一部分。(注意:ETCDIR = @ETCDIR@在mak......

阅读全文(3062) | 评论:0

如何阅读源代码之一(转帖)(2006-12-06 22:47:00)

摘要:作者:ariesram  来源:不详
写在前面的话:
自从我在linuxaid.com.cn上发表一些文章开始,就不断的有网友发来电子邮件,或者是就其中某些问题进行探讨,或者是查询其他文章的地址(往往这些网友看的是其他网站转载的我的文章),我很高兴自己写出的文章有这么多人回应,因为这是对我最好的赞赏,也很高兴有这么多人对我的文章感兴趣。但是常常因为工作关系。有很多邮件是询问我的其他文章在哪里能够找到,我不一定能够及时回复,也觉得回复同样的问题比较麻烦,所以在这里重复一下,我为linuxaid.com.cn写的文章都能在www.linuxaid.com.cn的应用开发栏目中找到,我的一部分文章收集在bambi.may10.ca/~ariesram/articles下面(这是一个很简陋的网页,只有文本格式的文章,也欢迎有兴趣的网友帮我设计一下网页),我的邮件地址:ariesram@linuxaid.com.cn, 或者ariesram@may10.ca。请转载文章的网站保留这一说明,欢迎网友写email给我探讨问题,虽然我不能保证能及时回复。
正文:

由于工作的关系,我常常需要读一些源代码,并在上面做一些修改并且拿来使用,或者是借鉴其中的某些部分。可以说,open source对于程序员来说,是很有意义的事情。根据我的经验,读源代码,至少有3个好处。第一个好处是可以学习到很多编程的方法,看好的源代码,对于提高自己的编程水平,比自己写源代码的帮助更大。当然不是说不用自己写,而是说,自己写代码的同时,可以从别人写的好的源代码中间学习到更多的编程方法和技巧。第二个好处是,可以提高自己把握大规模源代码的能力。一个比较大型的程序,往往都是经过了很多个版本很长的时间,有很多人参与开发,修正错误,添加功能而发展起来的。所以往往源代码的规模都比较大,少则10-100多k, 多的有好几十个MB. 在阅读大量源代码的时候,能够提高自己对大的软件的把握能力,快速了解脉络,熟悉细节,不仅仅是编程技巧,还能在程序的架构,设计方面提高自己的能力。(这里说一句题外话,<<设计模式>>这本书相信很多人都看过,而且很多人对它推崇备至,奉为经典。现在也出了不少书,都是冠以"设计模式"这一名称......

阅读全文(2910) | 评论:0

明晰C++内存分配的五种方法的区别(转帖)(2006-11-21 14:17:00)

摘要:在C++中,内存分成5个区,他们分别是堆、栈、自由存储区、全局/静态存储区和常量存储区。

  栈,就是那些由编译器在需要的时候分配,在不需要的时候自动清楚的变量的存储区。里面的变量通常是局部变量、函数参数等。

  堆,就是那些由new分配的内存块,他们的释放编译器不去管,由我们的应用程序去控制,一般一个new就要对应一个delete。如果程序员没有释放掉,那么在程序结束后,操作系统会自动回收。

  自由存储区,就是那些由malloc等分配的内存块,他和堆是十分相似的,不过它是用free来结束自己的生命的。

  全局/静态存储区,全局变量和静态变量被分配到同一块内存中,在以前的C语言中,全局变量又分为初始化的和未初始化的,在C++里面没有这个区分了,他们共同占用同一块内存区。

  常量存储区,这是一块比较特殊的存储区,他们里面存放的是常量,不允许修改(当然,你要通过非正当手段也可以修改,而且方法很多,在《const的思考》一文中,我给出了6种方法)

  明确区分堆与栈

  在bbs上,堆与栈的区分问题,似乎是一个永恒的话题,由此可见,初学者对此往往是混淆不清的,所以我决定拿他第一个开刀。

  首先,我们举一个例子:

void f() { int* p=new int[5]; }

  这条短短的一句话就包含了堆与栈,看到new,我们首先就应该想到,我们分配了一块堆内存,那么指针p呢?他分配的是一块栈内存,所以这句话的意思就是:在栈内存中存放了一个指向一块堆内存的指针p。在程序会先确定在堆中分配内存的大小,然后调用operator new分配内存,然后返回这块内存的首地址,放入栈中,他在VC6下的汇编代码如下:

00401028 push 14h
0040102A call operator new (00401060)
0040102F add esp,4
00401032 mov dword ptr [ebp-8],eax
00401035 mov eax,dword ptr [ebp-8]
00401038 mov dword ptr [ebp-4],e......

阅读全文(1936) | 评论:0

C++ STL轻松导学(6)(2006-06-05 17:51:00)

摘要:2.3 历史的评价 历史的车轮总是滚滚向前的,工业时代的文明较之史前时代,当然是先进并且发达的。回顾那两个时代的C++程序,你会真切的感受到这种差别。简洁易用,具有工业强度,较好的可移植性,高效率,加之第三个令人目眩的绝版程序所体现出来的高度抽象性,高度灵活性和组件化特性,使你对STL背后所蕴含的泛型化思想都有了些微的感受。 真幸运,你可以横跨两个时代,有机会目睹这种"文明"的差异。同时,这也应该使你越加坚定信念,使自己顺应时代的潮流。 2.4 如何运行 在你还没有真正开始运行前面后两个程序之前,最好先浏览一下本节。这里简单介绍了在特定编译器环境下运行STL程序的一些细节,并提供了一些可能遇到的问题的解决办法。 此处,我选用了目前在Windows平台下较为常见的Microsoft Visual C++ 6.0和Borland C++ Builder 6.0作为例子。尽管Visual C++ 6.0对最新的ANSI/ISO C++标准支持的并不是很好。不过据称Visual C++ .NET(也就是VC7.0)在这方面的性能有所改善。 你可以选用多种方式运行前面的程序,比如在Visual C++下,你可以直接在DOS命令行状态下编译运行,也可以在VC的IDE下采用控制台应用程序(Console Application)的方式运行。对于C++ Builder,情况也类似。 对于Visual C++而言,如果是在DOS命令行状态下,你首先需要找到它的编译器。假定你的Visual C++装在C:\Program Files\Microsoft Visual Studio\VC98下面,则其编译器所在路径应该是C:\Program Files\Microsoft Visual Studio\VC98\Bin,在那里你可以找到cl.exe文件。编译时请加上/GX和/MT参数。如果一切正常,结果就会产生一个可执行文件。如下所示: cl /GX /MT example2_2.cpp 前一个参数用于告知编译器允许异常处理(Exception Handling)。在P. J. Plauger STL中的很多地方使用了异常处理机制(即try…throw…catch语法),所以应该加上这个参数,否则会有如下警告信息: warning C4530: C++......

阅读全文(2588) | 评论:0

C++ STL轻松导学(5)(2006-06-05 17:50:00)

摘要:2.2.3 第三版:唯美主义的杰作 事态的发展有时候总会趋向极端,这在那些唯美主义者当中犹是如此。首先声明,我并不是一个唯美主义者,提供第二版程序的改进版,完全是为了让你更深刻的感受到STL的魅力所在。在看完第三版之后,你会强烈感受到这一点。或许你也会变成一个唯美主义者了,至少在STL方面。这应该不是我的错,因为决定权在你手里。下面我们来看看这个绝版的C++程序。 // name:example2_3.cpp // alias:aesthetic version #include <iostream> #include <vector> #include <algorithm> #include <iterator> using namespace std; void main(void) { typedef vector<int> int_vector; typedef istream_iterator<int> istream_itr; typedef ostream_iterator<int> ostream_itr; typedef back_insert_iterator< int_vector > back_ins_itr; // STL中的vector容器 int_vector num; // 从标准输入设备读入整数, // 直到输入的是非整型数据为止 copy(istream_itr(cin), istream_itr(), back_ins_itr(num)); // STL中的排序算法 sort(num.begin(), num.end()); // 将排序结果输出到标准输出设备 copy(num.begin(), num.end(), ostream_itr(cout, "\n")); } 在这个程序里几乎每行代码都是和STL有关的(除了main和那对花括号,当然还有注释),并且它包含了STL中几乎所有的各大部件(容器container,迭代器iterator, 算法algorithm, 适配器adaptor)......

阅读全文(3377) | 评论:1

C++ STL轻松导学(4)(2006-06-05 17:49:00)

摘要:2.2.2 第二版:工业时代--组件化大生产 我们应该庆幸自己所生活的年代。工业时代,科技的发展所带来的巨大便利已经影响到了我们生活中的每个细节。如果你还在以原始人类的方式生活着,那我真该怀疑你是否属于某个生活在非洲或者南美丛林里的原始部落中的一员了,难道是玛雅文明又重现了? STL便是这个时代的产物,正如其他科技成果一样,C++程序员也应该努力使自己适应并充分利用这个"高科技成果"。让我们重新审视第一版的那个破烂不堪的程序。试着使用一下STL,看看效果如何。 // name:example2_2.cpp // alias:The first STL program #include <iostream> #include <vector> #include <algorithm> using namespace std; void main(void) { vector<int> num; // STL中的vector容器 int element; // 从标准输入设备读入整数, // 直到输入的是非整型数据为止 while (cin >> element) num.push_back(element); // STL中的排序算法 sort(num.begin(), num.end()); // 将排序结果输出到标准输出设备 for (int i = 0; i < num.size(); i ++) cout << num[i] << "\n"; } 这个程序的主要部分改用了STL的部件,看起来要比第一个程序简洁一点,你已经找不到那个讨厌的compare函数了。它真的能很好的运行吗?你可以试试,因为程序的运行结果和前面的大致差不多,所以在此略去。我可以向你保证,这个程序是足够健壮的。不过,可能你还没有完全看明白程序的代码,所以我需要为你解释一下。毕竟,这个戏法变得太快了,较之第一个程序,一眨眼的功夫,那些老的C++程序员所熟悉的代码都不见了,取而代之的是一些新鲜玩意儿。 程序的前三行是包含的头文件,它们提供了程序所要用到的所有C++特性(包括输入输出处理,S......

阅读全文(2433) | 评论:0

C++ STL轻松导学(3)(2006-06-05 17:48:00)

摘要:2 牛刀小试:且看一个简单例程 2.1 引子 如果你是一个纯粹的实用主义者,也许一开始就可以从这里开始看起,因为此处提供了一个示例程序,它可以带给你有关使用STL的最直接的感受。是的,与其纸上谈兵,不如单刀直入,实际操作一番。但是,需要提醒的是,假如你在兴致昂然地细细品味本章内容的时候,能够同时结合前面章节作为佐餐,那将是再好不过的。你会发现,前面所提到的有关STL的那些优点,在此处得到了确切的应证。本章的后半部分,将为你演示在一些主流C++编译器上,运行上述示例程序的具体操作方法,和需要注意的事项。 2.2 例程实作 非常遗憾,我不得不舍弃"Hello World"这个经典的范例,尽管它不只一次的被各种介绍计算机语言的教科书所引用,几乎成为了一个默认的“标准”。其原因在于它太过简单了,以至于不具备代表性,无法展现STL的巨大魅力。我选用了一个稍稍复杂一点的例子,它的大致功能是:从标准输入设备(一般是键盘)读入一些整型数据,然后对它们进行排序,最终将结果输出到标准输出设备(一般是显示器屏幕)。这是一种典型的处理方式,程序本身具备了一个系统所应该具有的几乎所有的基本特征:输入 + 处理 + 输出。你将会看到三个不同版本的程序。第一个是没有使用STL的普通C++程序,你将会看到完成这样看似简单的事情,需要花多大的力气,而且还未必没有一点问题(真是吃力不讨好)。第二个程序的主体部分使用了STL特性,此时在第一个程序中所遇到的问题就基本可以解决了。同时,你会发现采用了STL之后,程序变得简洁明快,清晰易读。第三个程序则将STL的功能发挥到了及至,你可以看到程序里几乎每一行代码都是和STL相关的。这样的机会并不总是随处可见的,它展现了STL中的几乎所有的基本组成部分,尽管这看起来似乎有点过分了。 有几点是需要说明的: 这个例程的目的,在于向你演示如何在C++程序中使用STL,同时希望通过实践,证明STL所带给你的确确实实的好处。程序中用到的一些STL基本组件,比如:vector(一种容器)、sort(一种排序算法),你只需要有一个大致的概念就可以了,这并不影响阅读代码和理解程序的含义。 很多人对GUI(图形用户界面)的运行方式很感兴趣,这也难怪,漂亮的界面总是会令人赏心悦目的。但是很可惜,在这里没有加入这些功能。这很容易解释,对于所提供的这个简单示例程序而言,加......

阅读全文(2324) | 评论:0

C++ STL轻松导学(2)(2006-06-05 17:46:00)

摘要:1.3 千丝万缕的联系 在你了解了STL的过去之后,一些名词开始不断在你的大脑中浮现,STL、C++、C++标准函数库、泛型程序设计、面向对象程序设计……,这些概念意味着什么?他们之间的关系又是什么?如果你想了解某些细节,这里也许有你希望得到的答案。 1.3.1 STL和C++ 没有C++语言就没有STL,这么说毫不为过。一般而言,STL作为一个泛型化的数据结构和算法库,并不牵涉具体语言(当然,在C++里,它被称为STL)。也就是说,如果条件允许,用其他语言也可以实现之。这里所说的条件,主要是指类似于"模板"这样的语法机制。如果你没有略过前一节内容的话,应该可以看到,Alexander Stepanov在选择C++语言作为实现工具之前,早以采用过多种程序设计语言。但是,为什么最终还是C++幸运的承担了这个历史性任务呢?原因不仅在于前述那个条件,还在于C++在某些方面所表现出来的优越特性,比如:高效而灵活的指针。但是如果把C++作为一种OOP(Object-Oriented Programming,面向对象程序设计)语言来看待的话(事实上我们一般都是这么认为的,不是吗?),其功能强大的继承机制却没有给STL的实现帮上多大的忙。在STL的源代码里,并没有太多太复杂的继承关系。继承的思想,甚而面向对象的思想,还不足以实现类似STL这样的泛型库。C++只有在引入了"模板"之后,才直接导致了STL的诞生。这也正是为什么,用其他比C++更纯的面向对象语言无法实现泛型思想的一个重要原因。当然,事情总是在变化之中,像Java在这方面,就是一个很好的例子,jdk1.4中已经加入了泛型的特性。 此外,STL对于C++的发展,尤其是模板机制,也起到了促进作用。比如:模板函数的偏特化(template function partial specialization),它被用于在特定应用场合,为一般模板函数提供一系列特殊化版本。这一特性是继STL被ANSI/ISO C++标准委员会通过之后,在Bjarne和Stepanov共同商讨之下并由Bjarne向委员会提出建议的,最终该项建议被通过。这使得STL中的一些算法在处理特殊情形时可以选择非一般化的方式,从而保证了执行的效率。 1.3.2 STL和C++标准函数库 STL是最新的C++标准函数库中的一个子集,这个庞大的子集占据了整个......

阅读全文(3242) | 评论:0

C++ STL轻松导学(1)(2006-06-05 17:45:00)

摘要:1 初识STL:解答一些疑问 1.1 一个最关心的问题:什么是STL "什么是STL?",假如你对STL还知之甚少,那么我想,你一定很想知道这个问题的答案,坦率地讲,要指望用短短数言将这个问题阐述清楚,也决非易事。因此,如果你在看完本节之后还是觉得似懂非懂,大可不必着急,在阅读了后续内容之后,相信你对STL的认识,将会愈加清晰、准确和完整。不过,上述这番话听起来是否有点像是在为自己糟糕的表达能力开脱罪责呢?:) 不知道你是否有过这样的经历。在你准备着手完成数据结构老师所布置的家庭作业时,或者在你为你所负责的某个软件项目中添加一项新功能时,你发现需要用到一个链表(List)或者是映射表(Map)之类的东西,但是手头并没有现成的代码。于是在你开始正式考虑程序功能之前,手工实现List或者Map是不可避免的。于是……,最终你顺利完成了任务。或许此时,作为一个具有较高素养的程序员的你还不肯罢休(或者是一个喜欢偷懒的优等生:),因为你会想到,如果以后还遇到这样的情况怎么办?没有必要再做一遍同样的事情吧! 如果说上述这种情形每天都在发生,或许有点夸张。但是,如果说整个软件领域里,数十年来确实都在为了一个目标而奋斗--可复用性(reusability),这看起来似乎并不夸张。从最早的面向过程的函数库,到面向对象的程序设计思想,到各种组件技术(如:COM、EJB),到设计模式(design pattern)等等。而STL也在做着类似的事情,同时在它背后蕴涵着一种新的程序设计思想--泛型化设计(generic programming)。 继续上面提到的那个例子,假如你把List或者map完好的保留了下来,正在暗自得意。且慢,如果下一回的List里放的不是浮点数而是整数呢?如果你所实现的Map在效率上总是令你不太满意并且有时还会出些bug呢?你该如何面对这些问题?使用STL是一个不错的选择,确实如此,STL可以漂亮地解决上面提到的这些问题,尽管你还可以寻求其他方法。 说了半天,到底STL是什么东西呢? STL(Standard Template Library),即标准模板库,是一个具有工业强度的,高效的C++程序库。它被容纳于C++标准程序库(C++ Standard Library)中,是ANSI/ISO C++标准中最新的也是极具革命性的一部分。该库包含了诸多在计算......

阅读全文(2523) | 评论:0