1.2 C语言的优缺点
与其他任何编程语言一样,C语言也有自己的优缺点。这些优缺点都源于该语言的最初用途(编写操作系统和其他系统软件)和它自身的基础理论体系。
-
C语言是一种底层语言
。为了适应系统编程的需要,C语言提供了对机器级概念(例如,字节和地址)的访问,而这些是其他编程语言试图隐藏的内容。此外,C语言还提供了与计算机内置指令紧密协调的操作,使得程序可以快速执行。应用程序的输入/输出、存储管理以及其他众多服务都依赖于操作系统,因此操作系统一定不能运行得太慢。
-
C语言是一种小型语言
。与其他许多编程语言相比,C语言提供了一套更有限的特性集合。(在
K&R
第2版的参考手册中仅用49页就描述了整个C语言。)为了保持较少量的特性,C语言在很大程度上依赖一个标准函数的“库”(“函数”类似于其他编程语言中描述的“过程”“子例程”或“方法”)。
-
C语言是一种包容性语言
。C语言假设用户知道自己在做什么,因此它提供了比其他许多语言更高的自由度。此外,C语言不像其他语言那样强制进行详细的错误检查。
1.2.1 C语言的优点
C语言的众多优点有助于解释为什么这种语言如此流行。
-
高效
。高效性是C语言与生俱来的优点之一。发明C语言就是为了编写那些以往由汇编语言编写的应用程序,所以对C语言来说,能够在有限的内存空间里快速运行就显得至关重要了。
-
可移植
。虽然程序的可移植性并不是C语言的主要目标,但它还是成了C语言的优点之一。当程序必须在多种机型(从个人计算机到超级计算机)上运行时,常常会用C语言来编写。C程序具有可移植性的一个原因是该语言没有分裂成不兼容的多种分支(这要归功于C语言早期与UNIX系统的结合以及后来的ANSI/ISO标准)。另一个原因是C语言编译器规模小且容易编写,这使得它们得以广泛应用。最后,C语言自身的特性也支持可移植性(尽管它没有阻止程序员编写不可移植的程序)。
-
功能强大
。C语言拥有一个庞大的数据类型和运算符集合,这个集合使得C语言具有强大的表达能力,往往寥寥几行代码就可以实现许多功能。
-
灵活
。虽然C语言最初设计是为了系统编程,但是没有固有的约束将它限制在此范围内。C语言现在可以用于编写从嵌入式系统到商业数据处理的各种应用程序。此外,C语言在其特性使用上的限制非常少。在其他语言中认定为非法的操作在C语言中往往是允许的。例如,C语言允许一个字符与一个整数值相加(或者是与一个浮点数相加)。虽然灵活性可能会让某些错误溜掉,但是它使编程变得更加轻松。
-
标准库
。C语言的一个突出优点就是它具有标准库,该标准库包含了数百个可以用于输入/输出、字符串处理、存储分配以及其他实用操作的函数。
-
与UNIX系统的集成
。C语言在与UNIX系统(包括广为人知的Linux)结合方面特别强大。事实上,一些UNIX工具甚至假定用户是了解C语言的。
1.2.2 C语言的缺点
C语言的缺点和它的许多优点是同源的,均来自C语言与机器的紧密结合。下面是众所周知的几个问题。
-
C程序更容易隐藏错误
。C语言的灵活性使得用它编程出错的概率较高。在用其他语言编程时可以发现的错误,C语言编译器却无法检查出来。从这方面来说,C语言与汇编语言极为相似,后者直到程序运行时才能检查到大多数错误。更糟的是,C语言还包含大量不易觉察的隐患。在后续的章节中我们将看到,一个额外的分号可能导致无限循环,遗漏一个
&
可能引发程序崩溃。
-
C程序可能会难以理解
。虽然大多数衡量标准认为C语言是一种小型语言,但是它有许多其他通用语言没有的特性(并且常常被误解)。这些特性可以用多种方式结合使用,其中的一些结合方式尽管编程者心知肚明,但是其他人恐怕难以理解。另一个问题就是C程序简洁的本质。C语言产生的时候正是人机交互最为单调乏味的时期,因此设计者特意使C语言简洁以便将输入和编辑程序的用时减到最少。C语言的灵活性也可能是一个负面因素,过于聪明的程序员甚至可以编写出除了他们自己几乎没人可以读得懂的程序。
-
C程序可能会难以修改
。如果在设计中没有考虑维护的问题,那么用C语言编写的大规模程序将很难修改。现代的编程语言通常都会提供“类”和“包”之类的语言特性,这样的特性可以把大的程序分解成许多更容易管理的模块。遗憾的是,C语言恰恰缺少这样的特性。
混乱的C语言
即使是那些最热爱C语言的人也不得不承认C代码难以阅读。每年一次的国际C语言混乱代码大赛(International Obfuscated C Code Contest, IOCCC)竟然鼓励参赛者编写最难以理解的C程序。获奖作品着实让人感觉莫名其妙,例如1991年的“最佳小程序”:
v,i,j,k,l,s,a[99];
main()
{
for(scanf("%d",&s);*a-s;v=a[j*=v]-a[i],k=i<s,j+=(v=j<s&&
(!k&&!!printf(2+"\n\n%c"-(!l<<!j)," #Q"[l^v?(l^j)&1:2])&&
++1||a[i]<s&&v&&v-i+j&&v+i-j))&&!(1%=s),v||(i==j?a[i+=k]=0:
++a[i])>=s*k&&++a[--i])
;
}
这个程序是由Doron Osovlanski和Baruch Nissenbaum共同编写的,其功能是打印出八皇后问题(此问题要求在一个棋盘上放置8个皇后,使得皇后之间不会出现相互“攻击”的局面)的全部解决方案。事实上,此程序可用于求解皇后数量在4~99范围内的全部问题。更多的获奖程序可以到IOCCC网站获取。
1.2.3 高效地使用C语言
高效地使用C语言要求在利用C语言优点的同时要避免它的缺点。下面是一些建议。
brrNmCnFueNhJxOa6H9YZyddquytECG1IU3Hlhau2AeoJ8wtl1Dzl5VSeysrMd2x
-
学习如何规避C语言的缺陷
。规避缺陷的提示遍布全书,寻找
符号即可发现。如果想看到更详尽的缺陷列表,可以参考Andrew Koenig的《C陷阱与缺陷》
一书。现代编译器可以检查到常见的缺陷并且发出警告,但是没有一个编译器可以发现全部缺陷。
-
使用软件工具使程序更加可靠
。C程序员是众多软件工具的制造者(和使用者)。
lint
是最著名的C语言工具之一,一般由UNIX系统提供。与大多数C语言编译器相比,
lint
可以对程序进行更加广泛的错误分析。如果可以得到
lint
(或某个类似的程序),那么使用它应该是个好主意。另一个有益的工具是调试工具。由于C语言的本质,许多错误无法被C编译器查出。这些错误会以运行时错误或不正确输出的形式表现出来。因此在实践中,C程序员必须能够很好地使用调试工具。
-
利用现有的代码库
。使用C语言的一个好处是,许多人也在使用C。把别人编写好的代码用于自己的程序是一个非常好的主意。C代码经常被打包成库(函数的集合)。获取适当的库既可以大大减少错误,也可以免去相当多的编程工作。用于常见任务(包括用户界面开发、图形学、通信、数据库管理以及网络等)的库很容易获得。有些库是公用的,有些是开源的,有些则是作为商品销售的。
-
采用一套切合实际的编码规范
。编码规范是一套设计风格准则,即使语言本身没有强制要求,程序员也会遵守。精心选择的规范可以使程序更加统一,并且易于阅读和修改。使用任何一种编程语言时,规范都很重要,C语言尤其如此。正如前面所说的,C语言本身具有高度的灵活性,这使得程序员编写的代码可能会难以理解。本书的编程示例只遵循一套编码规范,但是,还有其他一些同样有效的规范可以使用。(本书将穿插讨论一些可供选择的方法。)选用哪套编码规范并不重要,重要的是必须采纳某些规范并且坚持使用它们。
-
避免“投机取巧”和极度复杂的代码
。C语言鼓励使用编程技巧。用C语言完成某项指定任务时通常会有多种解决途径,程序员经常会尝试选择最简洁的方式。但是,千万不要没有节制,因为最简略的解决方式往往也是最难以理解的。本书将给出一种相当简洁但仍然易于理解的编码风格。
-
紧贴标准
。大多数C编译器提供不属于C89、C99或者C1
X
标准的特性和库函数。为了程序的可移植性,若非确有必要,最好避免使用这些特性和库函数。