如何阅读源代码

5星(超过95%的资源)
所需积分/C币:16 2011-11-08 23:22:29 355KB PDF
9
收藏 收藏
举报

由于工作的关系,我常常需要读一些源代码,并在上面做一些修改并且拿来使用,或者是借鉴其中的某些部分。可以说,open source对于程序员来说,是很有意义的事情。根据我的经验,读源代码,至少有3个好处。第一个好处是可以学习到很多编程的方法,看好的源代码,对于提高自己的编程水平,比自己写源代码的帮助更大。当然不是说不用自己写,而是说,自己写代码的同时,可以从别人写的好的源代码中间学习到更多的编程方法和技巧。第二个好处是,可以提高自己把握大规模源代码的能力。一个比较大型的程序,往往都是经过了很多个版本很长的时间,有很多人参与开发,修正错误,添加功能而发展起来的。所以往往源代码的规模都比较大,少则10-100多k, 多的有好几十个MB. 在阅读大量源代码的时候,能够提高自己对大的软件的把握能力,快速了解脉络,熟悉细节,不仅仅是编程技巧,还能在程序的架构,设计方面提高自己的能力。
S(CC)S CFLAGSI SDEFS)-c parserc hashtab o: hashtab. c hashtab. h dns resolv h webalizer. h lang h S(CC)$(CFLAGS] SDEFSI-c hashtabc linklist. o: linklist. c linklist. h webalizer h lang. h S(CC)S[CFLAGS] SDEFS)-c linklistc output o: output c output. h webalizer h preserve. h hashtab h graphs h lang h S(CC)S(CFLAGS) SDEFS)-c output c preserve. o: preserve. c preserve. h webalizer h parser. h hashtab h graphs h lang h S(CC)$CFLAGS]$DEFS)-c preserve c dns resolvo: dns resolv.c dns resolv. h lang h webalizer. h S(CC)S(CFLAGS) SDEFSI-c dns_resolv.c graphs. o: graphs. c graphs h webalizer h lang h S(CC)S(CFLAGS)$DEFS)-ISGDLIB)-c graphs c 好了,不用再往下看了,这些就已经足够了。从这里我们可以看到这个软件的几个源代码文件和他们的结构。 webalizer. c是主程序所在的文件,其他的是一些辅助程序模块。对比一下日录里面的文件, sIs *c*h dns resolv. c graphs h lang h output c parser. h webalizer.c dns resolv h hashtab. c linklist. c output. h preserve. c webalizer. h graphs. c hashtab h linklist. h parser.c preserve. h webalizer lang h 于是,让我们从 webalizer. c开始吧。 作为一个C程序,在头文件里面,和C文件里面定义的 extern变量,结构等等肯定不会少,但是,单独看这些 东西我们不可能对这个程序有什么认识。所以,从main函数入手,逐步分析,在需要的时候再回头来看这些数据 结构定义才是好的方法。(顺便说一句, Visual c++,等 windows下的IDE工具提供了很方便的方法米获取函数列表, C++的类列表以及资源文件,对于阅读源代码很有帮助。∪ni/ Linux也有这些工只,但是,我们在这里暂时不说,而 只是通过最简单的文本编辑器ⅵ来训)。跳过 webalizer.c开头的版杈说明部分(GPL的),和数据结构定义,全局变 量声明部分,直接进入main数。在凼数廾头,我们看到: initalize epoch epoch=jdate (1, 1, 1970); /*used for timestamp adj * /*add default index. alias/ add_list("index. &index_ _alias); 这两个函数暂时吋不用仔细看,后面会提到,咯过。 sprintf(tmp buf, %s/webalizer. conf , ETCDIR) /*check for default config file * if(laccess (webalizer. conf", F OK)) get config( webalizer. conf); else if ( laccess(tmp buf, ok)) get configltmp buf); 从注释和稈序本身可以看出,这是査找是否存在一个叫做 webalizer conf的配置文件,如果当前目录下有,则用 get config米读入其中内容,如果没有,则查找ETCD|R∧ webalizer. conf是否存在。如果都没有,则进入下一部分。(注 意: ETCDIR=@ EcDIs@在 makefile中有定义) get command line options * opterr=0; /*disable parser errors * while((i=getopt(argc argv, a: A: C: C d D: e: E: fF g: GhHil: Lm: M: n: N: o pP: qQr: R: S: S: t: Tu: U: VVX: XY"))=EOF switch (i) case'a': add nist(optarg, &hidden agents); break; /*Hide agents * case 'A: ntop agents -atoiloptarg); break, / Top agents * case 'c': get config optarg); break; Config file * caseC: ntop ctrys=atoiloptarg); break; / Top countries */ cased: debug_ mode=1; break; Debug caseD: dns cache=optarg break /* DNS Cache filename " casee ntop entry=atoi(optarg); break; Top entry pages * caseE: ntop exit=atoiloptarg); break; / Top exit pages/ case'f': fold seq err=1; break; / Fold sequence errs*/ caseF: log type=(optarg[0==f)? LOG FTP optarg[o==s? LOG SQUID: LOG CLF, break / define log type * case g: group domains=atoiloptarg); break; GroupDomains(0=no case'G': hourly graph=0; break; no hourly grap case'h' print opts( argv[o]; break / help * case'H: hourly_ stats=0; break / no hourly stats * case i: ignore hist=l; break; / ignore history caseI: add list(optarg, &index alias ); break; / Index alias*/ case' ': graph lines=atoiloptarg); break / Graph Lines*/ caseL: graph legend=0; break; Graph Legends/ case 'm: visit timeout=atoi(optarg); break; / Visit Timeout * caseM: mangle agent=atoi(); break; mangle user agents*/ case'n hname=optarg; break / Hostname case 'N': dns children=atoi(optarg); break; /* of DNs children * case'o: out dir=optarg; break /*Output directory/ case'p: incremental=l; break; Incremental run * caseP: add list(optarg, &page type); break; page view types * case'q': verbose=1; break; /体* Quiet(verbose=1)* case Q: verbose=0; break /* Really Quiet */ case'r': add list(optarg, &hidden refs) break / Hide referrer case'R': ntop refs=atoi(optarg); break; /*K Top referrers case's': add_ nist(optarg,&hiddensites); break; /*K Hide site */ caseS: ntop sites=atoiloptarg ) break; / Top sites * caset: msg title=optarg break /* Report title米/ caseT: time me=l; break;/* TimeMe*/ case'u': add nist(optarg, &hidden urls); break; / hide UrL case'U: ntop urls-atoiloptargi break / Top urls * case v case V: print version(; break / Version case x: html ext=optarg; break; / HTML file extension * case ' x, hide sites=1 break Hide ind sites case ' Y: ctry_ graph=0; break; Supress ctry graph / if (argc-optind !=o)log fname argvloptind]; if log_ fname && (log fname [0]==')log fname=NULl; force STDIN? K / check for gzipped file -gz/ if (log_ fname if strcmp((log_ fname +strlen log fname)-3),gz) gz log=1; 这一段是分析命令行参数及开关。( getopt(的用法我在另外一篇文章中讲过,这里就不再重复了。)可以看到, 这个软件虽然功能不太复杂,但是开关选项还是不少。大多数的unix/inux程序的开头部分都是这个套路,初始化 配置文件,并且读入分析命令行。在这段程序中,我们需要注意一个函数:add_nist(). print opts(, get_ config(等等 看就明白,就不用多讲了。这里我们已经是第二次遇到ad_nlst这个函数了,就仔细看看吧。 S grep add list*h linklist. h: extern int add list(char * NLISTPtr*) / add list item * 可以发现它定义在 linklist. h中。 在这个h文件中,当然会有一些数据结构的定义,比如: struct nlisti char string [80] / list struct for HIDE items * struct nist *next ty pedef struct nist*NLISTPTR struct glist char string[8] list struct for GROUP items*/ char name[80]; struct glist * next; 1 ty pedef struct glist* GLISTPTR 这是两个链表结构。还有 extern GLISTPTR group sites; /*"group"lists " extern GLISTPTR group urls extern GLIStPtR group refs i 这些都是链表,太多了,不用一一看得很仔细,因为目前也看不出来什么东西。当然要注意它们是 extern的 也就是说,可以在其他地方(文件)看刭它们的数值(类似于C+中的pubc变量)。这里还定义了4个函数: extern char*isinlist(NLISTPTR, char * ) / scan list for str */ extern char *isinglist(GLISTPTR, char*); scan glist for str/ extern int add_ list(char * NLISTPtr); / add list item * extern int add glist(char * GLISTPTR / add group list item * 注意,这些都是 extern的,也就是说,可以在其他地方见到它们的调用(有点相当于C+中的 public函数)。再 来看看 linklist.C NLISTPTR new list(char *) /new list node "/ void del nist(NLISTPTR *); /*del list*/ GLISTPTR new_glist(char * char *) /*new group list node * void del glist(GLISTPTR*); /*del group list int isinstr(char *, char * 这5个函数是内韶使用的(相当」C++中的 private,也就是说,这些凶数只被 sinist( NLISTPTR,char*), isinglist(GL| STPTR,char*),add_ list(char*,NL| STPTR*),ad_ glist(char*, GLISTPTR*)调用,而不会出现在其他地方。所 以,我们先米看这几个内部函数。举例米说, add nist(char *) NLISTPTR new_ nist(char*str) NLISTPTR newptr; (sizeof(newptr->string)< strlen(str) if (verbose) fprintf(stderr, [new list]%s",msg big_one) if ( newptr= malloc(sizeof(struct nist))!= NULL istrncpy newptr->string, str, sizeof(newptr->string); newptr->next=NULL, J return newptr 这个函数分配了一个 struct list,并且把其中的 string赋值为str,next赋值为NUL这实际上是创建了链表中的 个节点。 verbose是一个全局变量,定义了输出信息的类型,如果 verbose为1,则输出很详细的信息,否则输出 简略信息。这是亼了调试或者使用者详细了解程序情况来用的。不是重要内容,虽然我们常常可以在这个源程序的 其他地方看到它。另外一个函数 void del nist(NLISTPTR * list) NLISTPTR cptr, nptr; cptr= list while(cptr =NULL) nptr=cptr->next; free(cptr); cptr=nptr; 这个函数删除了一个nist(也可能是list所指向的那一个部分开始知道链表结尾),比较简单。看完了这两个内 部函数,可以来看 水水水水水水水木本水水水水水木水水水本木水水水水水本水*本*水水水水水水水水水水水木水水 / ADD NLIST-add item to fifo linked list 本木水木本本水水水水本水水木本*不水本冰木水冰木水水水木本水木水水木水木本水木本水 int add nist(char*str, NLISTPtR"list) NLISTPTR newptr, cptr, pptr; if((newptr= new nist(str))l= NULL if *list==NULL*list=newptr else cptr=pptr=*list while(cptr! =NULL)i pptr=cptr; cptr=cptr->next; ] pptr->next newptr: return newptr==NULL 这个函数是建立了·个新的节点,把参数str赋值给新节点的 string,并把它连接到list所指向链表的结尾。另 外的三个函数: new glist(, del glist(, add glist(完成的功能和上述三个差不多,所不同的只是它们所处理的数据结 构不同。看完了这几个函数,我们回到main程序。接下来是, setup our internal variables * init counters(; /*initalize main counters * 我们所阅读的这个软件是用来分析日志并且做出统计的,那么这个函数的名字已经告诉了我们,这是一个初始 化计数器的函数。简略的看看吧! sgrep init_counters*h webalizer. h: extern void init counters 在 webalizer. c中找到: void init counters( int i: for (i=0; i 根据在最开始读过的 README文件,这个 page type是用米定义处理的页面的类型的。在 README文件中 P name Page type. This is the extension of files you consider to be pages for pages calculations sometimes called 'pageviews) The default is 'htm* and ' cgi(plus whatever HTMLExtension you specified if it is different). Dont use a period! 我们在程序中也可以看到,如果没有在命令行中或者 config文件中指定,则根据处理的日志文件的类型米添加 缺省的文件类型。比如对于CLF文件(WWW日志),处理htm,htm,cgi文件 if (log_ type ==LOG FTP) disable stuff for ftp logs * ntop entry=ntop exit=O ntop search=0 else 这一段是对于FTP的日志格式,设置搜索列表。 for (i=0; i 清空哈西衣,为下面即将进行的排序工作做好准备。关于哈西表,这是数据结构中常用的·种用来快速排序的 结构,如果不清楚,可以参考相关书籍,比如清华的<κ数据结构≯教材或者κ<数据结构的¢+-实现≯等书。 if (verbose>1) uname &system_info); printf("Webalizer V%s-%s(%s %S)%S version, editlvl system info sysname system info. release, language); 这一段,是打印有关系统的信息和 webalizer程序的信息(可以参考 uname的函数说明) #ifndef use dns if (strstr(argy[o], webazolver )!=0) printf( DNs support not present, aborting."; exit(1) #endif /*USE DNS/ 这一段,回忆我们在看 README文件的时候,曾经提到过可以在编译的时候设置选项开关来设定DNS支持,在 源代码中可以看到多次这样的代码段出现,如果不指定DNS支持,这些代码段则会出现(ifde)或者不出现( ifndef) 下面略过这些代码段,不再重复。 /* open log file * if (gz_ log gzlog_fp= gzopen(log fname, rb; if gzlog fp==Z NULL / Error: Can't open log file fprintf(stderr, %s %s", msg log err, log fname); else if (log_ fname) log fp fopen(log fname "r); if (log_ fp==NULL

...展开详情
试读 26P 如何阅读源代码
立即下载 低至0.43元/次 身份认证VIP会员低至7折
一个资源只可评论一次,评论内容不能少于5个字
Alicewooo 不明觉厉,不适合我这种小白人物看
2014-03-31
回复
albertlee8901 看了之后感觉很多疑惑解开了 谢谢
2013-11-11
回复
he1109he 很好的建议和参考资料,对于理解源码构成和思想有帮助,谢谢。
2013-08-04
回复
ccgxing 虽然是c语言的,但还好有学过,看得懂。。。。
2013-06-27
回复
a60853660 大概的看了一下,主要是用一个例子来说明的,个人感觉还没有边调试边看代码有效率~
2012-12-26
回复
eastlaugher 嗯,虽然是c语言的且是linux,但感觉还是很有用
2012-10-23
回复
lzb4616 嗯,虽然是c语言的,但感觉有用的地方还是可以借鉴的
2012-08-10
回复
您会向同学/朋友/同事推荐我们的CSDN下载吗?
谢谢参与!您的真实评价是我们改进的动力~
上传资源赚钱or赚积分
最新推荐
如何阅读源代码 16积分/C币 立即下载
1/26
如何阅读源代码第1页
如何阅读源代码第2页
如何阅读源代码第3页
如何阅读源代码第4页
如何阅读源代码第5页
如何阅读源代码第6页

试读结束, 可继续读2页

16积分/C币 立即下载