开发者如何开始学习(环境\工具\调试[理论\工具]\代码剖析\计划 versus 功能 versus 质量\睡觉\书籍)

61 阅读21分钟
Merc Release 2.1
Sunday 01 August 1993
Merc Release 2.1 星期日 01 八月 1993

Furey	mec@shell.portal.com
Hatchet	hatchet@uclink.berkeley.edu
Kahn	michael@uclink.berkeley.edu
奥利弗 mec@shell.portal.com 霍奇赫特 hatchet@uclink.berkeley.edu 凯恩 michael@uclink.berkeley.edu



=== 'I'm running a Mud so I can learn C programming!'
=== 我在运行一个 Mud 以便学习 C 编程!

Yeah, right.  嗯,是的。

The purpose of this document is to record some of our knowledge, experience and
philosophy.  No matter what your level, we hope that this document will help
you become a better software engineer.
本文件的目的是记录我们的一些知识、经验和理念。无论你的水平如何,我们希望这份文件能帮助你成为一名更好的软件工程师。

Remember that engineering is work, and NO document will substitute for your
own thinking, learning and experimentation.
请记住,工程是工作,没有任何文档能替代你自己的思考、学习和实验。



=== How to Learn in the First Place
=== 如何开始学习

(1) Play with something.
(2) Read the documentation on it.
(3) Play with it some more.
(4) Read documentation again.
(5) Play with it some more.
(6) Read documentation again.
(7) Play with it some more.
(8) Read documentation again.
(9) Get the idea?
(1) 玩弄一些东西。 (2) 查看它的文档。 (3) 再玩弄一些。 (4) 再次查看文档。 (5) 再玩弄一些。 (6) 再次查看文档。 (7) 再玩弄一些。 (8) 再次查看文档。 (9) 你明白了吗?

The idea is that your mind can accept only so much 'new data' in a single
session.  Playing with something doesn't introduce very much new data, but it
does transform data in your head from the 'new' category to the 'familiar'
category.  Reading documentation doesn't make anything 'familiar', but it
refills your 'new' hopper.
想象一下,你的大脑在同一会话中只能接受一定量的“新数据”。玩某样东西并不会引入很多新数据,但它确实会将你头脑中的数据从“新”类别转换为“熟悉”类别。阅读文档并不会使任何东西变得“熟悉”,但它会补充你的“新”数据池。

Most people, if they even read documentation in the first place, never return
to it.  They come to a certain minimum level of proficiency and then never
learn any more.  But modern operating systems, languages, networks, and even
applications simply cannot be learned in a single session.  You have to work
through the two-step learning cycle MANY times to master it.
大多数人,即使他们阅读了文档,也不会再回去查看。他们达到一定的熟练程度后就不再学习更多了。但现代操作系统、语言、网络,甚至应用程序是无法在一个会话中学会的。你必须多次经历这个两步学习循环才能掌握它们。



=== The Environment  === 环境

Computer: the big or little box that you're using to run Merc.  Computers come
    from a _manufacturer_ and have a _model_ name.  Here is a list of common
    manufacturers and models that you're likely to encounter:
计算机:你用来运行 Merc 的大或小盒子。计算机来自一个“制造商”并有一个“型号”名称。以下是一些你可能会遇到的常见制造商和型号列表:

	Manufacturer	Model
	------------	-----
制造商型号 ------------ -----

	Sun		Sun-2
	Sun		Sun-3
	Sun		Sun-4
	DEC		Vax 5000
	DEC		Vax 5900
	IBM		RS/6000
	NeXT		NextCube
	Sequent		Symmetry
	Sequent		Balance

    As far as hardware goes, Merc will run on any 32-bit hardware.
就硬件而言,Merc 可以运行在任何 32 位硬件上。

Operating system: the lowest level program running on your computer.  Most
    common computers run Unix or some variant of it, such as SunOS, Ultrix,
    AIX, Mach, or Dynix.  Notice that many of these variants end in 'IX'.
操作系统:计算机上运行的最低级程序。大多数常见的计算机运行 Unix 或其变体,如 SunOS、Ultrix、AIX、Mach 或 Dynix。请注意,许多这些变体以 'IX' 结尾。

    The two major 'families' of Unix are Berkeley Unix (developed at the
    illustrious University of California, Berkeley) and System 5 Unix
    (developed by Bell Laboratories, the progenitors of Unix).
Unix 的两大“家族”是伯克利 Unix(由著名的加利福尼亚大学伯克利分校开发)和 System 5 Unix(由贝尔实验室开发,贝尔实验室是 Unix 的先驱)。

    The most common non-Unix operating system is VMS (a proprietary operating
    system from DEC for their VAX computers).  In the personal computer world,
    you'll find MS-DOS, OS/2 for IBM PC's and compatibles, and MacOS for Apple
    Macintosh'es.
最常见的非 Unix 操作系统是 VMS(DEC 为他们的 VAX 计算机开发的专有操作系统)。在个人电脑领域,你会发现 MS-DOS、OS/2(用于 IBM PC 及其兼容机)和 MacOS(用于苹果 Macintosh)。

    GET THIS STRAIGHT: 'VAX' IS NOT AN OPERATING SYSTEM.  It's the name of a
    family of computers from DEC.  There are plenty of Vax'es running VMS, and
    there are even more Vax'es running Berkeley Unix or Ultrix.  The Vax'es
    running Unix have a lot more in common with other machines running
    Unix than they have with Vax'es running VMS.
注意:VAX 不是一个操作系统。它是 DEC 开发的一系列计算机的名字。运行 VMS 的 Vax 有很多,而运行伯克利 Unix 或 Ultrix 的 Vax 则更多。运行 Unix 的 Vax 与运行 VMS 的 Vax 相比,与运行 Unix 的其他机器有更多的共同点。

    As far as operating systems go, Merc will run on Unix or Unix variants with
    TCP/IP networking compatible with Berkeley Unix.  It will also run, in
    single-user mode only, on MS-DOS.  With a reasonable amount of work, Merc
    can be ported to any operating system that provides TCP service for telnet
    connections.
就操作系统而言,Merc 可以在兼容伯克利 Unix 的 Unix 或 Unix 变体上运行,这些变体具有 TCP/IP 网络。它还可以在单用户模式下运行在 MS-DOS 上。通过合理的努力,Merc 可以移植到任何提供 telnet 连接 TCP 服务的操作系统上。

Languages: Merc is written in C.  ANSI (the American National Standards
    Institute) has a specification for the C language, and Merc is written in
    Ansi Standard C.
语言:Merc 是用 C 语言编写的。美国国家标准学会(ANSI)为 C 语言制定了规范,Merc 是用 ANSI 标准 C 编写的。

    The most popular compiler for Ansi Standard C is the Gnu 'gcc' compiler
    produced by the Free Software Foundation.  It's available by anonymous
    ftp from prep.ai.mit.edu.  Merc compiles just fine with Gcc 1.38, so
    you can probably use 1.42 and skip the much larger 2.X versions.
最流行的 ANSI 标准 C 编译器是自由软件基金会(Free Software Foundation)生产的 Gnu 'gcc'编译器。可以从 prep.ai.mit.edu 通过匿名 FTP 获取。Merc 使用 Gcc 1.38 可以正常编译,所以你可以使用 1.42 版本并跳过更大的 2.X 版本。

    You don't have to use gcc.  IBM RS/6000's running the AIX operating system
    come with an Ansi C compiler already.  So do NeXT machines (the standard
    'cc' on NeXT happens to be the Gnu C compiler).  Any Ansi compiler will
    work.
你不需要使用 gcc。运行 AIX 操作系统的 IBM RS/6000 机器和 NeXT 机器都自带 ANSI C 编译器(NeXT 的标准'cc'实际上是 Gnu C 编译器)。任何 ANSI 编译器都可以使用。

    Unfortunately, there are still many machines out there without an Ansi
    standard C compiler.  (Sun is the worst offender in this regard).  You
    can attempt to compile Merc with a non-Ansi (traditional) C compiler by
    using the 'mktrad' script.  See trad.txt for details.
不幸的是,仍然有很多机器没有 ANSI 标准 C 编译器。(Sun 在这方面是最糟糕的)。你可以尝试使用非 ANSI(传统)C 编译器通过使用'mktrad'脚本来编译 Merc。详情请参见 trad.txt。

If you don't know what the manufacturer and model of your computer is, as well
as its operating system, and whether the C compiler is Ansi or non-Ansi, then
you need to find out.
如果不知道你的电脑制造商、型号、操作系统,以及 C 编译器是 ANSI 还是非 ANSI 的,那么你需要查一查。



=== Basic Unix Tools  === 基本的 Unix 工具

'man'	-- gives you online manual pages
'man' -- 给你在线手册页

'grep'	-- stands for 'global regular expression print'
'grep' -- 代表'全局正则表达式打印'

'vi'
'emacs'
'jove'	-- use whatever editor floats your boat
	   but learn the hell out of it
	   you should know EVERY command in your editor
'vi' 'emacs' 'jove' -- 使用你喜欢的编辑器,但要彻底掌握它,你应该知道你编辑器中的每一个命令

'ctags'	-- makes 'tags' for your editor
	   allows you to goto functions by name in any source file
'ctags' -- 为你的编辑器创建标签,允许你在任何源文件中通过名称跳转到函数

'>'
'>>'
'<'
'|'	-- input and output redirection
	   get someone to show you, or dig it out of 'man csh'
'>' '>>' '<' '|' -- 输入和输出重定向,请找人教你,或者从'man csh'中查找

These are the basic day-in day-out development tools.  Developing without
knowing how to use ALL of these well is like driving a car without knowing how
to change gears.
这些是基本的日常开发工具。不了解如何很好地使用这些工具就像开车时不换挡一样。



=== Debugging: Theory  === 调试:理论

Debugging is a science.  You formulate a hypothesis, make predictions based on
the hypothesis, run the program and provide it experimental input, observe its
behavior, and confirm or refute the hypothesis.
调试是一门科学。你提出一个假设,根据假设做出预测,运行程序并提供实验输入,观察其行为,然后验证或反驳假设。

A good hypothesis is one which makes surprising predictions which then come
true; predictions that other hypotheses don't make.
一个好的假设是那些做出令人惊讶的预测,而其他假设没有做出的预测。

The first step in debugging is not to write bugs in the first place.  This
sounds obvious, but sadly, is all too often ignored.
调试的第一步不是一开始就写入错误。这听起来显而易见,但不幸的是,往往被忽视。

If you build a program, and you get ANY errors or ANY warnings, you should fix
them before continuing.  C was designed so that many buggy ways of writing code
are legal, but will draw warnings from a suitably smart compiler (such as 'gcc'
with the '-Wall' flag enabled).  It takes only minutes to check your warnings
and to fix the code that generates them, but it takes hours to find bugs
otherwise.
如果你编写了一个程序,并且得到了任何错误或任何警告,你应该在继续之前修复它们。C 语言的设计使得许多错误的代码编写方式是合法的,但会被一个足够智能的编译器(例如启用了'-Wall'标志的'gcc')警告。检查警告并修复它们只需要几分钟,但如果不这样做,找到错误则需要花费数小时。

'Desk checking' (proof reading) is almost a lost art in 1993.  Too bad.  You
should desk check your code before even compiling it, and desk-check it again
periodically to keep it fresh in mind and find new errors.  If you have someone
in your group whose ONLY job it is to desk-check other people's code, that
person will find and fix more bugs than everyone else combined.
在 1993 年,"桌面检查"(代码校对)几乎是一种失传的艺术。太糟糕了。你应该在编译代码之前对其进行桌面检查,并定期再次进行桌面检查以保持记忆并发现新的错误。如果你团队中有一个人的唯一工作就是对其他人的代码进行桌面检查,那么这个人发现并修复的错误会比其他人加起来还要多。

One can desk-check several hundred lines of code per hour.  A top-flight
software engineer will write, roughly, 99% accurate code on the first pass,
which still means one bug per hundred lines.  And you are not top flight.
So ... you will find several bugs per hour by desk checking.  This is a very
rapid bug fixing technique.  Compare that to all the hours you spend screwing
around with broken programs trying to find ONE bug at a time.
一个人每小时可以检查几百行代码。顶级的软件工程师在第一次编写时大约会写出 99%准确的代码,这意味着每一百行代码中大约有一个错误。而你并不顶级。因此……你通过代码检查每小时会发现几个错误。这是一种非常快速的错误修复技术。将其与你花费在尝试一次找到一个错误的损坏程序上的所有时间相比。

The next technique beyond desk-checking is the time-honored technique of
inserting 'print' statements into the code, and then watching the logged
values.  Within Merc code, you can call 'printf' or 'fprintf' to dump
interesting values at interesting times.  Where and when to dump these values
is an art, which you will learn only with practice.
代码检查之后的下一个技术是历史悠久的在代码中插入'print'语句,然后观察记录的值。在 Merc 代码中,你可以调用'printf'或'fprintf'在特定时间输出有趣的值。在何处以及何时输出这些值是一门艺术,你只有通过实践才能学会。

If you don't already know how to redirect output in your operating system, now
is the time to learn.  On Unix, type the command 'man csh', and read the part
about the '>' operator.  You should also learn the difference between
'standard output' (e.g. output from 'printf') and 'error output' (e.g. output
from 'fprintf').
如果你还不知道如何在操作系统中重定向输出,现在是学习的时候了。在 Unix 中,输入命令'type man csh',并阅读关于'>'操作符的部分。你还应该了解'标准输出'(例如'printf'的输出)和'错误输出'(例如'fprintf'的输出)之间的区别。

Ultimately, you cannot fix a program unless you understand how it's operating
in the first place.  Powerful debugging tools will help you collect data, but
they can't interpret it, and they can't fix the underlying problems.  Only you
can do that.
从根本上说,除非你首先了解程序是如何运行的,否则你无法修复它。强大的调试工具可以帮助你收集数据,但它们无法解释这些数据,也无法解决根本问题。只有你能做到这一点。

When you find a bug ... your first impulse will be to change the code, kill the
manifestation of the bug, and declare it fixed.  Not so fast!  The bug you
observe is often just the symptom of a deeper bug.  You should keep pursuing
the bug, all the way down.  You should grok the bug and cherish it in fullness
before causing its discorporation.
当你发现一个错误时……你的第一反应可能是修改代码,消灭错误的表现形式,并宣布问题已经解决。等等!你观察到的错误通常是更深层次错误的症状。你应该继续追踪这个错误,直到找到根本原因。你应该彻底理解这个错误,并在消除它之前珍惜它。

Also, when finding a bug, ask yourself two questions: 'what design and
programming habits led to the introduction of the bug in the first place?'
And: 'what habits would systematically prevent the introduction of bugs like
this?'
当发现错误时,问自己两个问题:‘是什么设计和编程习惯导致了这个错误的引入?’以及:‘有哪些习惯可以系统地防止这种错误的引入?’



=== Debugging: Tools  === 调试:工具

When a Unix process accesses an invalid memory location, or (more rarely)
executes an illegal instruction, or (even more rarely) something else goes
wrong, the Unix operating system takes control.  The process is incapable of
further execution and must be killed.  Before killing the process, however, the
operating system does something for you: it opens a file named 'core' and
writes the entire data space of the process into it.
当一个 Unix 进程访问无效的内存位置,或者(更罕见地)执行非法指令,或者(更罕见地)其他事情出错时,Unix 操作系统会接管。该进程无法继续执行并必须被终止。然而,在终止进程之前,操作系统会为你做一件事:它会打开一个名为'core'的文件,并将该进程的数据空间写入其中。

Thus, 'dumping core' is not a cause of problems, or even an effect of problems.
It's something the operating system does to help you find fatal problems which
have rendered your process unable to continue.
因此,“dump 核心”并不是问题的原因,甚至也不是问题的结果。这是操作系统为了帮助你找出导致进程无法继续的致命问题而做的事情。

One reads a 'core' file with a debugger.  The two most popular debuggers on
Unix are 'adb' and 'gdb', although occasionally one finds 'dbx'.  Typically
one starts a debugger like this: 'adb merc' or 'gdb merc core'.
人们用调试器读取“core”文件。在 Unix 中最常用的两个调试器是“adb”和“gdb”,偶尔也会用到“dbx”。通常启动调试器的方式是:‘adb merc’ 或 ‘gdb merc core’。

The first thing, and often the only thing, you need to do inside the debugger
is take a stack trace.   In 'adb', the command for this is '$c'.  In gdb,
the command is 'backtrace'.  The stack trace will tell you what function your
program was in when it crashed, and what functions were calling it.  The
debugger will also list the arguments to these functions.  Interpreting these
arguments, and using more advanced debugger features, requires a fair amount of
knowledge about assembly language programming.
调试器中你通常需要做的第一件事,也是唯一一件事,就是获取堆栈跟踪。在 'adb' 中,该命令是 '$c'。在 gdb 中,该命令是 'backtrace'。堆栈跟踪会告诉你程序崩溃时所在的函数,以及调用它的函数。调试器还会列出这些函数的参数。解释这些参数以及使用更高级的调试功能需要对汇编语言编程有一定的了解。

If you have access to a program named 'Purify' ... learn how to use it.
如果你可以使用一个名为 'Purify' 的程序 ... 请学习如何使用它。



=== Profiling  === 代码剖析

Here is how to profile a program:
这是如何剖析一个程序:

(1) Remove all the .o files and the 'merc' executable:
删除所有的.o 文件和'merc'可执行文件:

	rm *.o 'merc'

(2) Edit your makefile, and change the PROF= line:
(2) 编辑你的 Makefile,并将 PROF=行改为:

	PROF    = -p

(3) Remake merc:  (3) 重制 merc:

	make

(4) Run merc as usual.  Shutdown the game with shutdown when you have run long
    enough to get a good profiling base.  If you crash the game, or kill the
    process externally, you won't get profiling information.
(4) 正常运行 merc。使用 shutdown 命令关闭游戏,以获得良好的性能基线数据。如果你在游戏中遇到崩溃,或者外部杀进程,将无法获得性能信息。

(5) Run the 'prof' command:
(5) 运行 'prof' 命令:

	prof merc > prof.out

(6) Read prof.out.  Run 'man prof' to understand the format of the output.
(6) 阅读 prof.out。运行 'man prof' 以了解输出格式。

For advanced profiling, you can use 'PROF = -pg' in step (2), and use the
'gprof' command in step 5.  The 'gprof' form of profiling gives you a report
which lists exactly how many times any function calls any other function.  This
information is valuable for debugging as well as performance analysis.
对于高级性能分析,您可以在步骤 (2) 中使用 'PROF = -pg',并在步骤 5 中使用 'gprof' 命令。'gprof' 形式的性能分析会生成一个报告,列出任何函数调用其他函数的次数。这些信息对于调试和性能分析都非常有价值。

Availability of 'prof' and 'gprof' varies from system to system.  Almost every
Unix system has 'prof'.  Only some systems have 'gprof'.
'prof' 和 'gprof' 的可用性因系统而异。几乎所有的 Unix 系统都包含 'prof'。只有某些系统包含 'gprof'。



=== Schedule versus Features versus Quality
=== 计划 versus 功能 versus 质量

Now for a few words on project management.
现在让我们谈谈项目管理。

Sooner or later, almost any project faces a trade-off between schedule,
features, and quality.  Consider a student writing a term paper on the last
night.  He has three unpalatable choices: he can turn it in late (miss the
schedule).  He can turn in a shorter paper that doesn't cover everything
(reduce the features).  Or he can churn out gibberish (lower the quality).
在任何时候,几乎任何项目都会面临计划、功能和质量之间的权衡。考虑一个学生在最后一天写一篇学期论文。他有三个不愉快的选择:他可以推迟提交(错过计划)。他可以提交一篇较短的论文,不涵盖所有内容(减少功能)。或者他可以产出一堆胡言乱语(降低质量)。

Similarly in a software project, one often has a choice between making the
release date, or dropping features, or shipping everything on time and
hoping that it works (it usually doesn't).
同样,在软件项目中,人们往往可以在按时发布、减少功能或希望一切都能正常工作(通常不会)之间做出选择。

The most important thing to realize about this decision is that it IS a
decision.  One can't get out of it by hoping that some miracle will occur.
If you don't react consciously, then external circumstances will drive the
decision.
关于这个决定,最重要的是它确实是一个决定。不能通过希望奇迹发生来逃避它。如果你不主动做出反应,外部环境将会决定这个决定。

Ok, so suppose you are faced with the trade-off and go for a schedule slip.
Don't take a small slip ... take a big impressive slip.  If you say
'I'll just fix this one problem and finish ASAP', then likely you will
wish you had taken just a little more time later.  If you say 'I think I
need another day, so I'll slip by a week', then it's much more likely
that what you'll have at the end of the week will do the job.  It's better
to slip a large block of time once then to slip day-by-day or hour-by-hour
repeatedly.
好的,所以假设你面临时间表推迟的选择,那就推迟一个显著的时间段。如果你说“我只要解决这个问题,尽快完成”,那么很可能你会后悔没有多花一点时间。如果你说“我认为我需要多一天,所以推迟一周”,那么在一周结束时,你很可能已经完成了工作。一次性推迟一个大时间段比一天天或一小时小时地推迟要好。

If you go for dropping features, again, carve off a big hunk.  Don't be
timid and pretend that you're going to do that work 'if you just get a
little spare time.'  That feature of your project is GONE, exploit the
lessened requirements for all the savings you can!
如果你选择减少功能,那就一次性砍掉一大块。不要胆小,假装你会在有空余时间时做那项工作。那个项目功能已经不存在了,利用减少的需求来节省尽可能多的时间!

I can't offer much advise on how to reduce quality, because that's always
my last choice for what to drop on a project.
我无法提供很多关于如何降低质量的建议,因为这是我总是最后选择要舍弃的东西。



=== Sleeping  === 睡觉

Simple and obvious, but true ... engineering takes an alert mind.
简单而显而易见,但却是事实……工程需要一个清醒的头脑。

It's very easy, very seductive, to throw a lot of consecutive hours at a
problem.  One can get into a 'flow' state where one's mind becomes filled
with the problem, and the work just pours out, hour after hour.  Many
writers report that they watch a story take place, and just transcribe
what they see, pounding out page after page of text.  Many software
engineers have experienced a similar feeling, where the code appears
to arise spontaneously as they watch themselves type.
很容易,也很有诱惑力,连续投入很多时间去解决一个问题。人们可能会进入一种“心流”状态,大脑被问题填满,工作一小时接一小时地进行。许多作家报告说,他们就像观看故事的发生,然后将其记录下来,一页接一页地敲出文字。许多软件工程师也有类似的感觉,他们看着自己打字,代码似乎自发地出现。

I believe most real work gets done in this state.
我认为大多数真正的工作就是在这种状态下完成的。

My experience, however, is that the 'flow' period can end subtly and
gradually.  Without ever noticing a change, I notice that new work isn't
flowing out of my hands anymore, that I'm spending lots of time fixing
up mistakes I made just a few moments ago.  Instead of ideas flashing
confidently through my mind, doubts and questions arise.
然而,我的经验是,‘流动’时期可以以一种微妙而渐进的方式结束。我甚至没有注意到任何变化,但我会发现新的工作不再从我的手中流出,我会花费大量时间去修正几分钟前犯下的错误。不再有自信的想法在我脑海中闪现,而是出现了疑虑和问题。

At this point there is a temptation to throw some more hours at the problem.
'I'm here, and I was getting a lot of work done, why don't I just stay all
night until I figure this out?'  This is a trap!  Don't do it!
这时,有一种倾向是投入更多的时间来解决这个问题。‘我已经在这里了,而且我之前做得很多,为什么我不再待一整夜直到解决这个问题呢?’这可是个陷阱!不要这么做!

Instead, I suggest: go home, eat, shower, sleep, put yourself back together
again.  Resume the next day.  While you sleep, your mind will work on the
problem anyways, and you'll probably wake up with new ideas.  You'll get
more done between 10:00 am and 2:00 pm the next day, then if you stayed up
between midnight and 10:00 am.
相反,我建议:回家,吃饭,洗澡,睡觉,让自己重新振作起来。第二天再继续。在你睡觉的时候,你的大脑仍然会继续思考这个问题,你可能会在第二天醒来时有了新的想法。第二天上午 10 点到下午 2 点之间,你会比在午夜到上午 10 点之间工作更有效率。

There is a problem with this strategy: remotivating yourself in the morning.
If the project is one of your choice, that's usually not a problem.  If it's
something you have to do but don't enjoy, you have to balance the remotivation
problem versus the very low productivity of working without sleep.
这种策略有一个问题:早上重新激励自己。如果项目是你选择的,通常这不是问题。如果这是一个你必须完成但又不喜欢的任务,你必须权衡重新激励自己与没有睡眠的非常低的生产力之间的关系。



=== Books for Serious Programmers
=== 严肃程序员的书籍

Out of all the thousands of books out there, three stand out:
在所有成千上万的书籍中,有三本脱颖而出:

Kernighan and Plaugher, _The Elements of Programming Style_.
肯尼高安和普拉格尔,《编程风格要素》。

Kernighan and Ritchie, _The C Programming Language_.
肯尼高安和里奇,《C 程序设计语言》。

Brooks, _The Mythical Man Month_
布鲁克斯,_人月神话_