1. C 语言深度解剖
-------------------解开程序员面试笔试的秘密
以含金量勇敢挑战国内外同类书籍陈正冲编著石虎审阅版权申明本书尚未出版,先放到网
上给大家免费下载和阅览。本书正式出版前读者可以仔细研读和自由传阅本书电子版,但不
允许私自大量印刷和销售。出版社如想出版此书可通过邮件或博客留言联系作者商谈出版事
宜。
写在前言前面的话
最近面试了一些人,包括应届本科、硕士和工作多年的程序员,在问到 C 语言相关的问题的
时候,总是没几个人能完全答上我的问题。甚至一些工作多年,简历上写着“最得意的语言
是 C 语言”,“对 C 有很深的研究”,“精通 C 语言”的人也答不完全我的问题,甚至有
个别人我问的问题一个都答不上。于是我就想起了我去年闲的使用写的这本小册子。
这本小册子已经在我电脑里睡了一年大觉了。并非没有出版社愿意出版,而是几个大的出版
社都认为书写得不错,但太薄,利润太低,所以要求我加厚到 300 页以上。我拒绝加厚,并
为此和几个出版社僵持了一年多。我认为经典的东西一定要精炼,不要废话。这次由于面试
别人,所以终于记起了我还写过这么一本小册子。想了想,还是决定挂到网上免费让大家看
得了。并为此专门为本书开了个博客,以方便和读者交流。博客地址:
http://blog.csdn.net/dissection_c
作者简介:
陈正冲:湖南沅江人,毕业于长春光学精密机械学院(长春理工大学)数学系。目前从事嵌
入式软件开发和管理方面的工作。石虎:湖南沅江人,毕业于吉林大学计算机系。目前为大
连交通大学计算机系讲师。
前言
我遇到过很多程序员和计算机系毕业的学生,也给很多程序员和计算机系毕业的学生讲
解过 《高级 C 语言程序设计 。》 每期班开课前,我总会问学生:你感 觉 C 语言学得怎么样?难
吗?指针明白吗?数组呢?内存管理呢?往往学生回答说:感觉还可以,C 语言不难,指针
很明白,数组很简单,内存管理也不难。一般我会再问一个问题:通过这个班的学习,你想
达到什么程度?很多学生回答:精通 C 语言。 我告诉他们:我很无奈,也很无语。 因为我完全
在和一群业余者或者是 C 语言爱好者在对话。你们大学的计算机教育根本就是在
浪费你们的时间,念了几年大学,连 C 语言的门都没摸着。现在大多数学校计算机系都开了
C、C++、Java、C#等等语言,好像什么都学了,但是什么都不会,更可悲的是有些大学居然取
消了 C 语言课程,认为其过时了。我个人的观点是“十鸟在林,不如一鸟在手”,真正把
C 语言整明白了再学别的语言也很简单,如果 C 语言都没整明白,别的语言学得再好也是花
架子,因为你并不了解底层是怎么回事。当然我也从来不认为一个没学过汇编的人能真正掌
握 C 语言的真谛。我个人一直认为,普通人用 C 语言在 3 年之下,一般来说,还没掌握 C 语
言;5 年之下,一般来说还没熟悉 C 语言;10 年之下,谈不上精通。所以,我告诉我的学生:
听完我的课,远达不到精通的目标,熟悉也达不到,掌握也达不到。那能达到什么目
标?-----领你们进入 C 语言的大门。 入门之后的造化如何在于你们自己。 不过我可以告诉你
1
31. 试的时候要单步执行的最多的代码。如果放在后面的话,找起来可能会比较困难,而放在前
面的话,可以很快的找到。
1.7.4,使用 case 语句的其他注意事项
【规则 1-24】简化每种情况对应的操作。 使得与每种情况相关的代码尽可能的精炼。 case 语句
后面的代码越精炼, case 语句的结果就会越清晰。 你想想,如果 case 语句后面的代码整个
屏幕都放不下,这样的代码谁也难看得很清晰吧。 如果某个 case 语句确实需要这么多的代码
来执行某个操作,那可以把这些操作写成一个或几个子程序,然后在 case 语句后面调用这
些子程序就 ok 了。一般来说 case 语句后面的代码尽量不要超过 20 行。
【规则 1-25】不要为了使用 case 语句而刻意制造一个变量。
case 语句应该用于处理简单的,容易分类的数据。如果你的数据并不简单,那可能使用
ifelseif 的组合更好一些。
为了使用 case 而刻意构造出来的变量很容易把人搞糊涂,应该
避免这种变量。比如:
char action = a[0];
switch
(action)
{
case‘c’: fun1();
break;
case‘d’:
…
break;
default:
break;
}
这里控制 case 语句的变量是 action。而 action 的值是取字符数组 a 的一个字符。但是这
种方式可能带来一些隐含的错误。一般而言,当你为了使用 case 语句而刻意去造出一个变
量时,真正的数据可能不会按照你所希望的方式映射到 case 语句里。在这个例子中,如果
用户输入字符数组 a 里面存的是“ const”这个字符串,那么 case 语句会匹配到第一个
case 上,并调用 fun1()函数。然而如果这个数组里存的是别的以字符 c 开头的任何字符
串(比如:“col”,“can”),case 分支同样会匹配到第一个 case 上。但是这也许并不
是你想要的结果,这个隐含的错误往往使人抓狂。 如果这样的话还不如使用 if-elseif 组合
比如:
31
32. if(0 == strcmp(“const”,a))
{
fun1();
}
elseif
{
…
}
【规则 1-26】把 default 子句只用于检查真正的默认情况。
有时候,你只剩下了最后一种情况需要处理,于是就决定把这种情况用 default 子句来处
理。 这样也许会让你偷懒少敲几个字符,但是这却很不明智。 这样将失去 case 语句的标号所
提供的自说明功能,而且也丧失了使用 default 子句处理错误情况的能力。 所以,奉劝你不
要偷懒,老老实实的把每一种情况都用 case 语句来完成,而把真正的默认情况的处理交给
default 子句。
1.8,do、while、for 关键字
C 语言中循环语句有三种:while 循环、do-while 循环、 循环。 while 循环:先判断 while
for
后面括号里的值,如果为真则执行其后面的代码;否则不执行。while(1)表示死循环。死
循环有没有用呢?看下面例子:
比如你开发一个系统要日夜不停的运行,但是只有操作员输入某个特定的字符‘ #’才可以
停下来。
while(1)
{
if(‘#’== GetInputChar())
{
break;
}
}
1.8.1,break 与 continue 的区别
break 关键字很重要,表示终止本层循环。 现在这个例子只有一层循环,当代码执行到 break
时,循环便终止。如果把 break 换成 continue 会是什么样子呢?continue 表示终止本次
(本轮)循环。当代码执行到 continue 时,本轮循环终止,进入下一轮循环。
while(1)也有写成 while(true)或者 while(1==1)或者 while((bool) 1)等形式的,效
果一样。
do-while 循环:先执行 do 后面的代码,然后再判断 while 后面括号里的值,如果为真,
循环开始;否则,循环不开始。其用法与 while 循环没有区别,但相对较少用。
for 循环:for 循环可以很容易的控制循环次数,多用于事先知道循环次数的情况下。 留一个
问题:在 switch case 语句中能否使用 continue 关键字?为什么?
32
33. 1.8.2,循环语句的注意点
【建议 1-27】 在多重循环中,如果有可能,应当将最长的循环放在最内层,最短的循环放在
最外层,以减少 CPU 跨切循环层的次数。
例如:
长循环在最内层,效率高长循环在最外层,效率低
for(col=0;col<5;col++)
{
for(row=0;row<100; row++)
{
sum = sum + a[row][col];
}
}
for (row=0; row<100;row++)
{
for ( col=0; col<5; col++)
{
sum = sum + a[row][col];
}
}
【建议 1-28】建议 for 语句的循环控制变量的取值采用“半开半闭区间”写法。
半开半闭区间写法和闭区间写法虽然功能是相同,但相比之下,半开半闭区间写法写法更
加直观。
半开半闭区间写法闭区间写法
for(n=0; n < 10; n++)
for(n= 0; n<=9; n++)
{
{
. .
}
}
【规则 1-29】不能在 for 循环体内修改循环变量,防止循环失控。
for (n = 0;n < 10;n++)
{ …
n=8;//不可,很可能违背了你的原意
…
}
33