专题 - 系统程序员成长计划
文章平均质量分 60
普通网友
这个作者很懒,什么都没留下…
展开
-
系统程序员成长计划-文本处理(三)
系统程序员成长计划-文本处理(三)管道过滤器(Pipe-And-Filter)模式按照《POSA(面向模式的软件架构)》里的说法,管道过滤器(Pipe-And-Filter)应该属于架构模式,因为它通常决定了一个系统的基本架构。管道过滤器和生产流水线类似,在生产流水线上,原材料在流水线上经一道一道的工序,最后形成某种有用的产品。在管道过滤器中,数据经过一个一个的过滤器,最后得到需要的数据。原创 2009-07-06 17:21:00 · 2003 阅读 · 15 评论 -
系统程序员成长计划-组合的威力(三)
栈栈是一种后进先出(LIFO, last in firstout)的数据结构,与队列的先进先出(FIFO)相比,这种规则似乎不太公平,计算机可不管这个。事实上,栈是最重要的数据结构之一:没有栈,基于下推自动机的编译器不能工作,我们只能写汇编程序。没有栈,无法实现递归/多级函数调用,程序根本就无法工作。栈主要的接口函数有:o 创建栈 stack_createo 取栈顶元素 s原创 2009-03-27 01:18:00 · 1650 阅读 · 0 评论 -
系统程序员成长计划-动态数组(三)
排序大多数高级排序算法都是针对数组实现的,接下来我们一起学习一下几种排序算法,学习算法本身只是我们的目标之一,最重要的是要从中学习一些思考问题的方法。对比不同算法的特点,也有助于我们在设计时做出正确的选择。这里我们请读者实现冒泡排序、快速排序和归并排序。要求如下:o 算法同时支持升序和降序。按排序结果来看,有升序和降序两种。在升序排列中,前面的元素总是小于/等于后面的元素。在降序中,前原创 2009-03-26 23:37:00 · 1448 阅读 · 0 评论 -
系统程序员成长计划-动态数组(二)
对比双向链表和动态数组在C语言中,数组的长度是事先确定的,不能在运行时动态调整。所谓动态数组就是它的长度可以根据存储数据多少自动调整,这需要我们用程序来实现。对比双向链表和动态数组,我们会发现:o 动态数组本身占用一块连续的内存,而双向链表的每个结点要占用一块内存。在频繁增删数据的情况下,双向链表更容易造成内存碎片,具体影响与内存管理器的好坏有关。o 动态数组的增删操作需要移动后面的元原创 2009-03-26 23:00:00 · 1571 阅读 · 0 评论 -
系统程序员成长计划-并发(四)(下)
读写锁读写锁在加锁时,要区分是为了读而加锁还是为了写而加锁,所以和递归锁不同的是,它无法兼容Locker接口了。不过为了做到不依赖于特定平台,我们可以利用Locker的接口来抽象锁的实现。利用现有的锁来实现读写锁。读写锁的可变的部分已经被Locker隔离了,所以读写锁本身不需要做成接口。它只是一个普通对象而已:struct _RwLocker;typedef struct _RwL原创 2009-03-26 22:38:00 · 1444 阅读 · 0 评论 -
系统程序员成长计划-并发(四)(上)
读写锁在前面的实现中,像dlist_length这类的查询函数也要加锁,那样才能保证在查询过程中对象的状态不会被其它线程所改变。加锁阻止了其它线程修改对象,也阻止其它线程查询对象。如果大多数情况下,线程只是查询对象的状态而不修改它,这种设计不是一种高效的方法,因为它不允许多个线程同时查询。我们能不能实现一种锁,它能串行化对数据结构的修改,而同时支持并行的查询呢?这就是所谓的读写锁,也称为共原创 2009-03-26 18:23:00 · 1374 阅读 · 0 评论 -
系统程序员成长计划-并发(三)(下)
嵌套锁与装饰模式嵌套锁的实现算法加锁: o如果没有任何线程加锁,就直接加锁,并且记录下当前线程的ID。 o如果是当前线程加过锁了,就不用加锁了,只是将加锁的计数增加一。 o如果其它线程加锁了,那就等待直到加锁成功,后继步骤与第一种情况相同。解锁: o如果不是当前线程加的锁或者没有人加锁,那这是错误的调用,直接返回。 o如果是当前线程加锁了,将加锁的计数减一。如果计数大于0原创 2009-03-26 18:17:00 · 1314 阅读 · 0 评论 -
系统程序员成长计划-并发(二)(上)
在生产者-消费者的练习中,大部分人选择了由调用者来加锁:作为生产者,往双向链表里插入数据时,先加锁,插入数据,然后解锁。作为消费者,从双向链表里取数据时,先加锁,删除数据,然后解锁。这是合理的,不过有点麻烦:每个调用者都要做这些动作,如果其中一个调用者忘记了解锁的步骤,就会造成死锁。而且调用者必须要清楚自己是在多线程下工作,这些代码放到单线程的环境中就不能使用了。在很多情况下由实现者来加锁是比原创 2009-03-26 16:27:00 · 1483 阅读 · 0 评论 -
系统程序员成长计划-并发(一)(上)
这几年并发技术受到前所未有的关注:CPU进入多核时代,连手机芯片都使用三核的CPU(AP+BP+DSP集成到一颗芯片)了。天生具有并发能力的语言ErLang逐渐成为热点。网格和云计算开始进入实用阶段。还有一些新技术更是让我闻所未闻,初学者也不用被这些铺天盖地的名词吓倒。据笔者的经验来看,这些技术或许能够改变产业的格局,对人类生活造成重大影响,但从实现角度来看并不无多少革命,相反大部分都是传统技术的原创 2009-03-26 15:02:00 · 1527 阅读 · 0 评论 -
系统程序员成长计划-写得又快又好的秘诀(三)
代码阅读法软件工程实践已经证明Code Review是提高代码质量最有效的手段之一,极限编程(XP)更是把Code Review推向极致,形成著名的结对编程工作方式,两个程序员在一台电脑前面工作,一个人编写程序,另一个Review输入每一行代码,写程序人的专注于目前细节上的工作,Review的人同时要从高层次考虑如何改进代码质量,两个人的角色会经常互换。可惜我即没有结对编程的经验,也没有在原创 2009-03-26 14:03:00 · 1498 阅读 · 0 评论 -
系统程序员成长计划-谁动了你的隐私(上)
需求简述:或许你还在欣赏用良好代码风格重新编写的双向链表,看起来不错,不是吗?不过这还远远不够,专业程序员要有精益求精的精神。至于要精到什么程度,与具体需求有关,如果只是写个小程序验证一下某个想法,那完成需要的功能就行了,如果是开发一个基础程序库,那就要考虑更多了。侯捷先生说过,学从难处学,用从易处用。这里我们是学习,就要精得不能再精为止,精到钻牛角尖为止。在后面的几章中,我们将对这个原创 2009-03-26 00:52:00 · 1461 阅读 · 0 评论 -
系统程序员成长计划-工程管理(三)
系统程序员成长计划-工程管理(三)函数库现在我们用automake来管理我们前面所建立的函数库,这是一个基础的函数库,我们就把它命名为base吧。o 目录结构base 根目录base/src 源代码目录o 创建Makefile模板base/Makefile.am内容为:SUBDIRS=srcbase/src/Makefile.am内容为:lib_LTLIBRARIES=li原创 2009-04-18 17:24:00 · 1564 阅读 · 0 评论 -
系统程序员成长计划-工程管理(四)
应用程序前面我们创建的helloworld是一个应用程序工程,它很简单,只使用了标准C的函数。现在我们要建立一个应用程序工程,它将使用前面所写的libbase函数库。o目录结构最顶层目录名用模块名称,这里用appdemo。源文件放在模块下的src子目录里,即appdemo/src。o 创建源文件在src下创建源文件main.c,内容只是简单的调用一下libbase里的函数。原创 2009-04-18 17:25:00 · 1567 阅读 · 0 评论 -
系统程序员成长计划-文本处理(二)
Builder模式前面我们学习了状态机,并利用它来解析各种格式的文本数据。解析过程把线性的文本数据转换成一些基本的逻辑单元,但这通常只是任务的一部分,接下来我们还要对这些解析出来的数据进一步处理。对于特定格式的文本数据,它的解析过程是一样的,但是对解析出来的数据的处理却是多种多样的。为了让解析过程能被重用,就需要把数据的解析和数据的处理分开。现在我们回过头来看一下前面写的函数parse_t原创 2009-06-26 11:16:00 · 1652 阅读 · 11 评论 -
系统程序员成长计划-文本处理(一)
系统程序员成长计划-文本处理(一)状态机(1)o 有穷状态机的形式定义有穷状态机是一个五元组 (Q,Σ,δ,q0,F),其中:Q是一个有穷集合,称为状态集。Σ是一个有穷集合,称为字母表。δ: Q xΣQ称为状态转移函数。q0 是初始状态。F 是接受状态集。教科书上是这样定义有穷自动机的,这个形式定义精确的描述了有穷状态机的含义。但是大部分人(包括我自己)第一次看到它时,反复的读上原创 2009-06-12 14:10:00 · 1981 阅读 · 6 评论 -
系统程序员成长计划-像机器一样思考(三)
系统程序员成长计划-像机器一样思考(三)hello world的密秘hello world是最经典的入门程序,该程序因Brian Kernighan 和DennisRitchie编写的《C语言程序设计》(The C Programming Language)而广泛流传。helloworld同样也是深入研究计算机的极好题材,可以说我对计算机的理解,很大程度上归功于对hellowo原创 2009-05-31 21:38:00 · 1855 阅读 · 0 评论 -
系统程序员成长计划-像机器一样思考(二)
谁在call我-backtrace的实现原理显示函数调用关系(backtrace/callstack)是调试器必备的功能之一,比如在gdb里,用bt命令就可以查看backtrace。在程序崩溃的时候,函数调用关系有助于快速定位问题的根源,了解它的实现原理,可以扩充自己的知识面,在没有调试器的情况下,也能实现自己backtrace。更重要的是,分析backtrace的实现原理很有意思。现在我们一原创 2009-05-24 10:38:00 · 2005 阅读 · 0 评论 -
系统程序员成长计划-内存管理(四)
惯用手法 《POSA(面向模式的软件架构)》中根据模式粒度把模式分为三类:架构模式、设计模式和惯用手法。其中把分层模式、管道过滤器和微内核模式等归为架构模式,把代理模式、命令模式和出版-订阅模式等归为设计模式,而把引用计数等归为惯用手法。这三类模式间的界限比较模糊,在特定的情况下,有的设计模式可以作为架构模式来用,有的把架构模式也作为设计模式来用。 一般来说,我们可以认为架构模式、设计模式和惯用手原创 2009-05-08 23:24:00 · 1451 阅读 · 0 评论 -
系统程序员成长计划-分离用户界面与内部实现(一)
系统程序员成长计划-分离用户界面与内部实现(一)用户界面就是与用户交互的接口,通常包括输入和输出(显示)两个部分。用户使用键盘等输入设备把数据输入给程序,程序做相应处理后,输出结果到显示器或其它设备上。所谓的内部实现(也称为内部逻辑)就是负责数据处理的这一部分功能,它占的比例最大,其实现也最复杂。分离用户界面与内部实现有很多好处:1.用户界面与内部实现是相对独立的功能,从分而治之的思想原创 2009-07-16 17:31:00 · 17262 阅读 · 16 评论 -
系统程序员成长计划-内存管理(三)
内存管理器在前面学习共享内存的时候,我们重新实现了循环队列,两个实现的不同之处只是在于内存分配和释放上。对比一下 fifo_ring_create的实现:第一种实现用malloc分配内存。FifoRing* fifo_ring_create(size_t length){ FifoRing* thiz = NULL; return_val_if_fail(length >原创 2009-04-29 02:13:00 · 1636 阅读 · 0 评论 -
系统程序员成长计划-内存管理(一)
共享内存 大家都知道,进程的地址空间是独立的,它们之间互不影响。比如同样地址为0xabcd1234的内存,在不同的进程中,它们的数据是完全不同的。这样做的好处有:首先是每个进程的地址空间变大了,让编写程序更为容易。其次是一个进程崩溃了,不会影响其它进程,提高了系统的稳定性。 要做到进程的地址空间独立,光靠软件是难以实现的,通常还依赖于硬件的帮助。这种硬件称为MMU(Memory Man原创 2009-04-29 02:07:00 · 1627 阅读 · 0 评论 -
系统程序员成长计划-内存管理(二)
线程局部存储(TLS) 同一个进程中的多个线程,它们的内存空间是共享的(栈除外),一个线程对内存的修改,对所有线程都有效。这是一个优点也是一个缺点。说它是优点,线程间的数据交换快捷高效。说它是缺点,一个线程死掉了,其它线程也性命不保。 在unix下,大家一直都对线程不太感兴趣,直到很晚以后才引入真正的线程。像X Sever要同时处理N个客户端的连接,每秒钟要响应上百万个请求,开发人转载 2009-04-29 02:10:00 · 1710 阅读 · 0 评论 -
系统程序员成长计划-工程管理(二)
HelloWorldautomake比起IDE要复杂很多,这里我们先写一个Hello World例子,明白其中的基本概念后,再用它来管理实际的工程。o目录结构最顶层目录名用模块名称,这里是helloworld。源文件放在模块下的src子目录里,即helloworld/src。这是惯例。有多个子模块时,各个子模块的源代码放在各自的目录里。o 创建源文件在src下创建源文件原创 2009-04-06 01:38:00 · 1558 阅读 · 0 评论 -
系统程序员成长计划-工程管理(一)
到目前为止本书的上半部分已经完成了。在上半部分中,我们学习了基本的数据结构、算法和设计思想。在进行深入学习之前,我们把前面所写的代码整理成一个通用的函数库,这个函数可能在以后的工作中用得着。前面我们写的Makefile非常简单,大概类似下面的内容:all: gcc -Wall -g -DDARRAY_TEST darray.c -o darray_test gcc -Wall原创 2009-04-06 01:37:00 · 1834 阅读 · 0 评论 -
系统程序员成长计划-走近专业程序员(下)
当你读到这里的时候,相信你已经独立写出了一个双向链表。恭喜你!迈出这一步可是值得庆祝的,现在你已经走在通往程序员的光明大道上了。不过你还是个业余程序员,那当然了,你才写出第一个程序呢!什么时候才能成为一个专业程序员呢?三年还是五年工作经验?其实不用的,你马上就可以了,我没有骗你,因为专业程序员与业余程序员之分主要在于一种态度,如果缺乏这种态度,拥有十年工作经验也还是业余的。什么态度?专原创 2009-03-26 00:41:00 · 1555 阅读 · 0 评论 -
系统程序员成长计划-走近专业程序员(上)
需求简述用C语言编写一个双向链表。如果你有一定的C语言编程经验,这自然是小菜一碟。有的读者可能连一个小程序都没有写过,那也不用害怕,可以参考任何一本《数据结构》和C语言的书籍。先弄明白基本概念,把书上的代码看明白,再把代码抄到电脑里,保证编译过去,调试它到正常运行。反复这个过程,直到你能独立完成它为止。写第一行代码是很痛苦的,我培训过好几个同事,他们不是计算机系毕业的,开始在电脑前原创 2009-03-26 00:33:00 · 1554 阅读 · 0 评论 -
系统程序员成长计划-序
写作背景在经历过几个大型的,失败的项目之后,我终于明白没有什么比高素质的程序员更能决定项目的成功了,无论什么过程,什么编程语言和开发工具,离开了高素质的程序员,什么都是白费力气。毫无疑问,人是软件开发中最重要的因素,但不是每个人都重要,不是什么样的人都重要,只有那些高素质的程序员和那些对项目有突出贡献的人才是重要的。不过高素质的程序员并不多见,所以从我开始带人之际,就在思考团队原创 2009-03-26 00:08:00 · 1905 阅读 · 2 评论 -
系统程序员成长计划-写得又快又好的秘诀(四)
避免常见错误在C语言中,内存错误是最为人诟病的。这些错误让项目延期或者被取消,引发无数的安全问题,甚至出现人命关天的灾难。抛开这些大道理不谈,它们确实浪费了我们大量时间,这些错误引发的是随机现象,即使有一些先进工具的帮助,为了找到重现的路径,花上几天时间也不足为怪。如果能够在编写代码的时候避免这些错误,开发效率至少提高一倍以上,质量可以提高几倍了。这里列举一些常见的内存错误,供新手参考。o原创 2009-03-26 14:28:00 · 1480 阅读 · 0 评论 -
系统程序员成长计划-写得又快又好的秘诀(二)
1.好与快的关系几年前和一个朋友聊天时,他抱怨他的上司说,要我写得好又要写快,那怎么可能呢?我当时一愣,反问到,写不好怎么可能写得快?他也一愣。传统观点认为在功能、成本(人*时间)和质量这个铁三角中,提高质量就意味投入更多成本或者减少一些功能。在功能不变的情况下,不可能在提高质量的同时降低开成成本(对个人来讲就是缩短开发时间)。我的朋友持的正是这种传统观点。而根据我的经验来看,结论恰恰原创 2009-03-26 13:43:00 · 1334 阅读 · 0 评论 -
系统程序员成长计划-写得又快又好的秘诀(一)
“快”是指开发效率高,“好”是指软件质量高。呵呵,写得又快又好的人就是高手了。记得这是林锐博士下的定义,读他那篇著名的《C/C++高质量编程》时,我还是个初学者,印象特别深。我现在仍然赞同他的观点,不过这里标题改为成为高手的秘诀,感觉就有点像标题党了,所以还是用比较通俗的说法吧。废话少说,请读者回顾一下这段时间的编程经验,回答下面两个问题:1.快与好是什么关系?写得快就不能写得好?写得好就不能原创 2009-03-26 13:38:00 · 1389 阅读 · 0 评论 -
系统程序员成长计划-你的数据放在哪里
需求简述这里我们请读者实现下列功能:对一个存放字符串的双向链表,把存放在其中的字符串转换成大写字母。 对于初学者来说这道题有点难度,很少有人能完全做对的。不过没关系,我们的目标并不是要难倒读者,而是要刺激读者去思考,加深学习的印象。有了前面两次的经验,我想没有人再去写一个dlist_to_upper的函数了,大家都会调用dlist_fore原创 2009-03-26 13:30:00 · 1505 阅读 · 0 评论 -
系统程序员成长计划-Don’t Repeat Yourself(DRY)(下)
实现这两个函数并不是件难事,但真正写好的人并不多。初学者通常的做法有两种:1.各写一个独立的函数。dlist_find_max用来找出最大值,dlist_sum用来求和。这种做法和前面写dlist_print时所犯的错误一样,会造成重复的代码,让dlist的实现随着应用环境的变化而变化。2.采用回调函数法。细心的初学者会发现,这两个函数的实现与dlist_print的实现很类似,无非是pr原创 2009-03-26 13:27:00 · 1747 阅读 · 0 评论 -
系统程序员成长计划-Don’t Repeat Yourself(DRY)(上)
需求简述这里我们请读者实现下列功能:对一个存放整数的双向链表,找出链表中的最大值。对一个存放整数的双向链表,累加链表中所有整数。多写多练,不要偷懒,写完之后请仔细思考一下有无改进的余地。原创 2009-03-26 13:26:00 · 1659 阅读 · 0 评论 -
系统程序员成长计划-拥抱变化(下)
在专用双向链表中,dlist_printf的实现非常简单,如果里面存放的是整数,用”%d”打印,存放的字符串,用”%s”打印。现在的麻烦在于双向链表是通用的,我们无法预知其中存在的数据类型,也就是我们要面对数据类型的变化。怎么办呢?初学者常见的做法有:1.实现多个函数,需要哪个就用哪个。比如实现的有dlist_print_int用来打印存放整数的双向链表,dlist_print_string用原创 2009-03-26 13:21:00 · 1413 阅读 · 0 评论 -
系统程序员成长计划-拥抱变化(上)
需求简述大部分初学者在编写双向链表时,为了验证相关函数工作是否正常,都会编写一个dlist_print的函数,它的功能是在屏幕上打印出整个双向链表中的数据。从客观上讲,用dlist_print输出的信息来判断dlist的正确性不是最好的办法,不过脑袋里有质量概念总是值得表扬的。当把专用的双向链表演化成通用的双向链表时,编写一个dlist_print已经不那么简单了。这里我们请读者写一个dlis原创 2009-03-26 13:07:00 · 1363 阅读 · 0 评论 -
系统程序员成长计划-Write once, run anywhere(WORA)(下)
1.专用链表和通用链表各自的特点与适用范围。专用链表在这里是指它的实现和调用耦合在一起,只能被一个调用者使用,而不能单独在其它地方被重用。通用链表则相反,它具有通用性,可以在多处被重复使用。尽管通用链表相对专用链表来说有很多优越之处,不过简单的断定通用链表比专用链表好也是不公正的,因为它们都有自己的优点和适用范围:专用链表的优点:更高性能。专用链表的实现和调用在一起,可以直接访问数据成原创 2009-03-26 13:03:00 · 1337 阅读 · 0 评论 -
系统程序员成长计划-Write once, run anywhere(WORA)(上)
需求简述Write Once, Debug Everywhere。据说这是流传于JAVA程序员中间的一句笑话,Sun公司用来形容JAVA的跨平台性的原话是Write once, run anywhere(WORA) 。后者是理想的,前者才是现实。如果我们的双向链表可以到处运行,那就太好了。Write once, run anywhere(WORA)是我们的目标,不过我们先要面对现实,回到双向链原创 2009-03-26 13:01:00 · 1575 阅读 · 0 评论 -
系统程序员成长计划-谁动了你的隐私(下)
1.什么封装?人有隐私,程序也有隐私。有隐私不是什么坏事,没有隐私人就不是人了,程序也不成其为程序了。问题是隐私不应该让别人知道,否则伤害的不仅仅是自己,相关人物也会跟着倒霉,“艳照门”就是个典型的例子。程序隐私的暴露,造成的伤害不一定有“艳照门”大,也不一定比它小,反正不要小看它就行了。封装就是要保护好程序的隐私,不该让调用者知道的事,就坚决不要暴露出来。2.为什么要封装?总原创 2009-03-26 00:54:00 · 1527 阅读 · 0 评论 -
系统程序员成长计划-写得又快又好的秘诀(五)
自动测试手工测试比没有测试强一点,但是它存在的问题让它很难在实践中应用:手工输入数据的过程单调乏味,很难长期坚持。每次都要重新输入数据,浪费大量时间。测试用例不能累积,测试往往不完整。用人脑判断输出的正误,浪费人力也存在误差。要写得好测试自然不能省,要写得快就需要更好的测试方法。更好的测试方法当然是自动测试了。幸运的是,刚进入这个行业我就接触了自动的测试 (呵,读本文的初学者就更幸运了),原创 2009-03-26 14:45:00 · 1397 阅读 · 0 评论 -
系统程序员成长计划-写得又快又好的秘诀(六)
Save your work“Ernst和Young所在的小组决定使用正规的开发理论—他们常用削减法,分阶段进行开发并具有中途交付能力。他们的步骤包括细致的分析和设计—正如本章描写的基本原则一样。而其他竞争者径直开始了编码,在开始几个小时里,Ernst和Young小组落后了。但到中午时Ernst和Young小组却是遥遥领先了,而到了这一天的最后,他们却失败了。导致失败的原因不是因为他们的正规方原创 2009-03-26 14:53:00 · 1391 阅读 · 0 评论