C语言编程要点.pdf
《C语言编程要点.pdf》由会员分享,可在线阅读,更多相关《C语言编程要点.pdf(242页珍藏版)》请在淘文阁 - 分享文档赚钱的网站上搜索。
1、C 语言编程要点-第 1 章 C 语言C 语言编程要点-第 1 章 C 语言 第 1 章 C 语言 本章主要描述 C 语言一些基本要素。当你开始编写 C 程序时,你可能对 C 语言的一些基本问题感到困惑,如 C 语言所使用的约定、关键字和术语等。本章将回答这方面你经常会遇到的一些问题。例如,switch 语句是最常用的一种 C 语言构件,本章将回答与它有关的三个常见问题。本章还涉及其它几个问题,如循环、分支、运算符的优先级和程序块技术。在阅读本章时,请注意有关 switch 语句和运算符优先级的一些问题,这些问题常常会使 C 语言的初学者感到迷惑。11 什么是局部程序块(local block
2、)?局部程序块是指一对大括号()之间的一段 C 语言程序。一个 C 函数包含一对大括号,这对大括号之间的所有内容都包含在一个局部程序块中。if 语句和 swich 语句也可以包含一对大括号,每对大括号之间的代码也属于一个局部程序块。此外,你完全可以创建你自己的局部程序块,而不使用 C 函数或基本的 C 语句。你可以在局部程序块中说明一些变量,这种变量被称为局部变量,它们只能在局部程序块的开始部分说明,并且只在说明它的局部程序块中有效。如果局部变量与局部程序块以外的变量重名,则前者优先于后者。下面是一个使用局部程序块的例子:#include void main(void);void main()
3、/*Begin local block for function main()*/int test_ var=10;printf(Test variable before the if statement:%dn,test_var);if(test_var5)/*Begin local block for if statement*/int test_ var=5;printf(Test variable within the if statement:%dn,test_var);/*Begin independent local block(not tied to any function
4、or keyword)*/int test_var=0;printf(Test variable within the independent local block:%dn,test_var)/*End independent local block*/printf(Test variable after the if statement:%dn,test_var);/*End local block for function main()*/上例产生如下输出结果:Test variable before the if statement:10 Test variable within th
5、e if statement:5 Test variable within the independent local block:0 Test variable after the if statement:10 注意,在这个例子中,每次 test_var 被定义时,它都要优先于前面所定义的 test_var 变量。此外还要注意,当 if 语句的局部程序块结束时,程序重新进入最初定义的 test_var 变量的作用范围,此时 test_var 的值为10。请参见:12 可以把变量保存在局部程序块中吗?12 可以把变量保存在局部程序块中吗?用局部程序块来保存变量是不常见的,你应该尽量避免这样做
6、,但也有极少数的例外。例如,为了调试程序,你可能要说明一个全局变量的局部实例,以便在相应的函数体内部进行测试。为了使程序的某一部分变得更易读,你也可能要使用局部程序块,例如,在接近变量被使用的地方说明一个变量有时就会使程序变得更易读。然而,编写得较好的程序通常不采用这种方式来说明变量,你应该尽量避免使用局部程序块来保存变量。请参见:11 什么是局部程序块?13 什么时候用一条 switch 语句比用多条 if 语句更好?如果你有两个以上基于同一个数字(numeric)型变量的条件表达式,那么最好使用一条 switch 语句。例如,与其使用下述代码:if(x=l)printf(x is equa
7、l to one.n);else if(x=2)printf(x is equal to two.n);else if(x=3)printf(x is equal to three.n);else printf(x is not equal to one,two,or three.n);不如使用下述代码,它更易于阅读和维护:switch(x)case 1:printf(x is equal to one.n);break;case 2:printf(x is equal to two.n);break case 3:printf(x is equal to three.n);break;def
8、ault:printf(x is not equal to one,two,or three.n);break;注意,使用 switch 语句的前提是条件表达式必须基于同一个数字型变量。例如,尽管下述 if 语句包含两个以上的条件,但该例不能使用 switch 语句,因为该例基于字符串比较,而不是数字比较:char*name=Lupto;if(!stricmp(name,Isaac)printf(Your name meansLaughtern);else if(!stricmp(name,Amy)printf(Your name meansBelovedn);else if(!stricmp
9、(name,Lloyd)printf(Your name meansMysteriousn);else printf(I havent a clue as to what your name meansn);请参见:14 switch 语句必须包含 default 分支吗 7 15 switch 语句的最后一个分支可以不要 break 语句吗?14 switch 语句必须包含 default 分支吗?不,但是为了进行错误检查或逻辑检查,还是应该在 switch 语句中加入 default 分支。例如,下述 switch语句完全合法:switch(char_code)case tyt:case
10、y:printf(You answered YES!n)break case N:case n:printf(You answered NO!n);break 但是,如果一个未知字符被传递给这条 switch 语句,会出现什么情况呢?这时,程序将没有任何输出。因此,最好还是加入一个 default 分支,以处理这种情况:.default:printf(Unknown response:%dn,char_code);break.此外,default 分支能给逻辑检查带来很多方便。例如,如果用 switch 语句来处理数目固定的条件,而且认为这些条件之外的值都属于逻辑错误,那么可以加入一个 def
11、ault 分支来辨识逻辑错误。请看下列:void move_cursor(int direction)switch(direction)case UP:cursor_up()break case DOWN:cursor_down()break case LEFT:cursor_left()break case RIGHT:cursor_ right()break default:printf(Logic error on line number%ld!n,_ LINE_)break 请参见:13 什么时候用一条 switch 语句比用多条 if 语句更好?15 Switch 语句的最后一个分支
12、可以不要 break 语句吗?15 switch 语句的最后一个分支可以不要 break 语句吗?尽管 switch 语句的最后一个分支不一定需要 break 语句,但最好还是在 switch 语句的每个分支后面加上break 语句,包括最后一个分支。这样做的主要原因是:你的程序很可能要让另一个人来维护,他可能要增加一些新的分支,但没有注意到最后一个分支没有 break 语句,结果使原来的最后一个分支受到其后新增分支的干扰而失效。在每个分支后面加上 break 语句将防止发生这种错误并增强程序的安全性。此外,目前大多数优化编译程序都会忽略最后一条 break 语句,所以加入这条语句不会影响程序
13、的性能。请参见:1.3 什么时候用一条 switch 语句比用多条 if 语句更好?1.4 switch 语句必须包含 default 分支吗?16 除了在 for 语句中之外,在哪些情况下还要使用逗号运算符?逗号运算符通常用来分隔变量说明、函数参数、表达式以及 for 语句中的元素。下例给出了使用逗号的多种方式:#include#include void main(void);void main()/*Here,the comma operator is used to separate three variable declarations.*/int i,j,k;/*Notice how
14、 you can use the comma operator to perform multiple initializations on the same line.*/i=0,j=1,k=2;printf(i=%d,j=%d,k=%dn,i,j,k);/*Here,the comma operator is used to execute three expressions in one line:assign k to i,increment j,and increment k.The value that i receives is always the rigbtmost expr
15、ession.*/i=(j+,k+);printf(i=%d,j=%d,k=%dn,i,j,k);/*Here,the while statement uses the comma operator to assign the value of i as well as test it.*/while(i=(rand()%100),i!=50)/*随即数*/printf(i is%d,trying again.n,i)printf(nGuess what?i is 50!n)请注意下述语句:i:(j+,k+)这条语句一次完成了三个动作,依次为:(1)把 k 值赋给 i。这是因为左值(lvaul
16、e)总是等于最右边的参数,本例的左值等于 k。注意,本例的左值不等于 k+,因为 k+是一个后缀自增表达式,在把 k 值赋给 j 之后 k 才会自增。如果所用的表达式是+k,则+k 的值会被赋给 i,因为+k 是一个前缀自增表达式,k 的自增发生在赋值操作之前。(2)j 自增。(3)k 自增。此外,还要注意看上去有点奇怪的 while 语句:while(i=(rand()%100),i!=50)printf(i is%d,trying again.n);这里,逗号运算符将两个表达式隔开,while 语句的每次循环都将计算这两个表达式的值。逗号左边是第一个表达式,它把 0 至 99 之间的一个随
17、机数赋给 i;第二个表达式在 while 语句中更常见,它是一个条件表达式,用来判断 i 是否不等于 50。while 语句每一次循环都要赋予 i 一个新的随机数,并且检查其值是否不等于 50。最后,i 将被随机地赋值为 50,而 while 语句也将结束循环。请参见:112 运算符的优先级总能保证是“自左至右”或“自右至左”的顺序吗?113+var 和 var+有什么区别?17 怎样才能知道循环是否提前结束了?循环通常依赖于一个或多个变量,你可以在循环外检查这些变量,以确保循环被正确执行。请看下例:int x char*cpREQUESTED_BLOCKS/*Attempt(in vain,
18、I must add.)to allocate 512 10KB blocks in memory.*/for(x=0;xREQUESTED_ BLOCKS;x+)cpix=(char*)malloc(10000,1)if(cpx=(char*)NULL)break /*If x is less than REQUESTED-BLOCKS,the loop has ended prematurely.*/if(xREQUESTED_BLOCKS)printf(Bummer!My loop ended prematurely!n);注意,如果上述循环执行成功,它一定会循环 512 次。紧接着循环
19、的 if 语句用来测试循环次数,从而判断循环是否提前结束。如果变量 x 的值小于 512,就说明循环出错了。18 goto,longjmp()和 setjmp()之间有什么区别?goto 语句实现程序执行中的近程跳转(local jump),longjmp()和 setjmp()函数实现程序执行中的远程跳转(nonlocaljump,也叫 farjump)。通常你应该避免任何形式的执行中跳转,因为在程序中使用 goto 语句或longjmp()函数不是一种好的编程习惯。goto 语句会跳过程序中的一段代码并转到一个预先指定的位置。为了使用 goto 语句,你要预先指定一个有标号的位置作为跳转位
20、置,这个位置必须与goto语句在同一个函数内。在不同的函数之间是无法实现goto跳转的。下面是一个使用 goto 语句的例子:void bad_programmers_function(void)int x printf(Excuse me while I count to 5000.n);x=l;while(1)printf(%dn,x)if(x=5000)goto all_done else x=x+1;all_done:prinft(Whew!That wasnt so bad,was it?n);如果不使用 goto 语句,是例可以编写得更好。下面就是一个改进了实现的例子:void b
21、etter_function(void)int x printf(Excuse me while I count to 5000.n);for(x=1;x=5000,x+)printf(%dn,x)printf(Whew!That wasnt so bad,was it?n);前面已经提到,longjmp()和 setjmp()函数实现程序执行中的远程跳转。当你在程序中调用 setjmp()时,程序当前状态将被保存到一个jmp_buf类型的结构中。此后,你可以通过调用longjmp()函数恢复到调用setjmp()时的程序状态。与 goto 语句不同,longjmp()和 setjmp()函数
22、实现的跳转不一定在同一个函数内。然而,使用这两个函数有一个很大的缺陷,当程序恢复到它原来所保存的状态时,它将失去对所有在 longjmp()和setjmp()之间动态分配的内存的控制,也就是说这将浪费所有在longjmp()和setjmp()之间用malloc()和calloc()分配所得的内存,从而使程序的效率大大降低。因此,你应该尽量避免使用 longjmp()和 setjmp()函数,它们和 goto 语句一样,都是不良编程习惯的表现。下面是使用 longjmp()函数和 setjmp()函数的一个例子:#include#include jmp_buf saved_state;void
23、main(void);void call_ longjmp(void);void main(void)int ret_code;printf(The current state of the program is being saved.n);ret_code=setjmp(saved_state)if(ret_code=1)printf(The longjmp function has been called.n)printf(The programs previous state has been restored.n);exit(0)printf(I am about to call l
24、ongjmp andn);printf(return to the previous program state.n)call_ longjmp()void call_longjmp(void)longjmp(saved_state,1)19 什么是左值(lvaule)?左值是指可以被赋值的表达式。左值位于赋值语句的左侧,与其相对的右值(rvaule,见 111)则位于赋值语句的右侧。每条赋值语句都必须有一个左值和一个右值。左值必须是内存中一个可存储的变量,而不能是一个常量。下面给出了一些左值的例子:int x;int*p_int;x=1;p_int=5;变量 x 是一个整数,它对应于内存中的
25、一个可存储位置,因此,在语句“x1”中,x 就是一个左值。注意,在第二个赋值语句“*p_int5中,通过“*”修饰符访问 p_int 所指向的内存区域;因此,p_int 是一个左值。相反,下面的几个例子就不是左值:#define CONST_VAL 10 int x/*example 1*/l=x;/*example 2*/CONST_VAL=5;在上述两条语句中,语句的左侧都是一个常量,其值不能改变,因为常量不表示内存中可 存储的位置。因此,这两条赋值语句中没有左值,编译程序会指出它们是错误的。请参见:1.10 数组(array)可以是左值吗?1.11 什么是右值(rvaule)?110 数
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 语言 编程 要点
限制150内