数据结构导论自考实验第1章C语言.docx
- 文档编号:20133196
- 上传时间:2023-04-25
- 格式:DOCX
- 页数:16
- 大小:22.64KB
数据结构导论自考实验第1章C语言.docx
《数据结构导论自考实验第1章C语言.docx》由会员分享,可在线阅读,更多相关《数据结构导论自考实验第1章C语言.docx(16页珍藏版)》请在冰豆网上搜索。
数据结构导论自考实验第1章C语言
第1章 C语言
本章主要描述C语言一些基本要素。
当你开始编写C程序时,你可能对C语言的一些基本问题感到困惑,如C语言所使用的约定、关键字和术语等。
本章将回答这方面你经常会遇到的一些问题。
例如,switch语句是最常用的一种C语言构件,本章将回答与它有关的三个常见问题。
本章还涉及其它几个问题,如循环、分支、运算符的优先级和程序块技术。
在阅读本章时,请注意有关switch语句和运算符优先级的一些问题,这些问题常常会使C语言的初学者感到迷惑。
1.1 什么是局部程序块(localblock)?
局部程序块是指一对大括号({})之间的一段C语言程序。
一个C函数包含一对大括号,这对大括号之间的所有内容都包含在一个局部程序块中。
if语句和swich语句也可以包含一对大括号,每对大括号之间的代码也属于一个局部程序块。
此外,你完全可以创建你自己的局部程序块,而不使用C函数或基本的C语句。
你可以在局部程序块中说明一些变量,这种变量被称为局部变量,它们只能在局部程序块的开始部分说明,并且只在说明它的局部程序块中有效。
如果局部变量与局部程序块以外的变量重名,则前者优先于后者。
下面是一个使用局部程序块的例子:
#include
voidmain(void);
voidmain()
{
/*Beginlocalblockforfunctionmain()*/
inttest_var=10;
printf("Testvariablebeforetheifstatement:
%d\n",test_var);
if(test_var>5)
{
/*Beginlocalblockfor"if"statement*/
inttest_var=5;
printf("Testvariablewithintheifstatement:
%d\n",
test_var);
{
/*Beginindependentlocalblock(nottiedto
anyfunctionorkeyword)*/
inttest_var=0;
printf(
"Testvariablewithintheindependentlocalblock:
%d\n",
test_var)
}
/*Endindependentlocalblock*/
printf("Testvariableaftertheifstatement:
%d\n",test_var);
}
/*Endlocalblockforfunctionmain()*/
上例产生如下输出结果:
Testvariablebeforetheifstatement:
10
Testvariablewithintheifstatement:
5
Testvariablewithintheindependentlocalblock:
0
Testvariableaftertheifstatement:
10
注意,在这个例子中,每次test_var被定义时,它都要优先于前面所定义的test_var变量。
此外还要注意,当if语句的局部程序块结束时,程序重新进入最初定义的test_var变量的作用范围,此时test_var的值为10。
请参见:
1.2可以把变量保存在局部程序块中吗?
1.2 可以把变量保存在局部程序块中吗?
用局部程序块来保存变量是不常见的,你应该尽量避免这样做,但也有极少数的例外。
例如,为了调试程序,你可能要说明一个全局变量的局部实例,以便在相应的函数体内部进行测试。
为了使程序的某一部分变得更易读,你也可能要使用局部程序块,例如,在接近变量被使用的地方说明一个变量有时就会使程序变得更易读。
然而,编写得较好的程序通常不采用这种方式来说明变量,你应该尽量避免使用局部程序块来保存变量。
请参见:
1.1什么是局部程序块?
1.3 什么时候用一条switch语句比用多条if语句更好?
如果你有两个以上基于同一个数字(numeric)型变量的条件表达式,那么最好使用一条switch语句。
例如,与其使用下述代码:
if(x==l)
printf("xisequaltoone.\n");
elseif(x==2)
printf("xisequaltotwo.\n");
elseif(x==3)
printf("xisequaltothree.\n");
else
printf("xisnotequaltoone,two,orthree.\n");
不如使用下述代码,它更易于阅读和维护:
switch(x)
{
case1:
printf("xisequaltoone.\n");
break;
case2:
printf("xisequaltotwo.\n");
break
case3:
printf('xisequaltothree.\n");
break;
default:
printf("xisnotequaltoone,two,orthree.\n");
break;
}
注意,使用switch语句的前提是条件表达式必须基于同一个数字型变量。
例如,尽管下述if语句包含两个以上的条件,但该例不能使用switch语句,因为该例基于字符串比较,而不是数字比较:
char*name="Lupto";
if(!
stricmp(name,"Isaac"))
printf("Yournamemeans'Laughter'.\n");
elseif(!
stricmp(name,"Amy"))
printf("Yournamemeans'Beloved'.\n");
elseif(!
stricmp(name,"Lloyd"))
printf("Yournamemeans'Mysterious'.\n");
else
printf("Ihaven'taclueastowhatyournamemeans.\n");
请参见:
1.4switch语句必须包含default分支吗7
1.5switch语句的最后一个分支可以不要break语句吗?
1.4switch语句必须包含default分支吗?
不,但是为了进行错误检查或逻辑检查,还是应该在switch语句中加入default分支。
例如,下述switch语句完全合法:
switch(char_code)
{
casetyt:
case'y':
printf("YouansweredYES!
\n")
break
case'N':
case'n':
printf("YouansweredNO!
\n");
break
}
但是,如果一个未知字符被传递给这条switch语句,会出现什么情况呢?
这时,程序将没有任何输出。
因此,最好还是加入一个default分支,以处理这种情况:
......
default:
printf("Unknownresponse:
%d\n",char_code);
break
......
此外,default分支能给逻辑检查带来很多方便。
例如,如果用switch语句来处理数目固定的条件,而且认为这些条件之外的值都属于逻辑错误,那么可以加入一个default分支来辨识逻辑错误。
请看下列:
voidmove_cursor(intdirection)
{
switch(direction)
{
caseUP:
cursor_up()
break
caseDOWN:
cursor_down()
break
caseLEFT:
cursor_left()
break
caseRIGHT:
cursor_right()
break
default:
printf("Logicerroronlinenumber%ld!
!
!
\n",
__LINE__)
break
}
}
请参见:
1.3什么时候用一条switch语句比用多条if语句更好?
1.5Switch语句的最后一个分支可以不要break语句吗?
1.5switch语句的最后一个分支可以不要break语句吗?
尽管switch语句的最后一个分支不一定需要break语句,但最好还是在switch语句的每个分支后面加上break语句,包括最后一个分支。
这样做的主要原因是:
你的程序很可能要让另一个人来维护,他可能要增加一些新的分支,但没有注意到最后一个分支没有break语句,结果使原来的最后一个分支受到其后新增分支的干扰而失效。
在每个分支后面加上break语句将防止发生这种错误并增强程序的安全性。
此外,目前大多数优化编译程序都会忽略最后一条break语句,所以加入这条语句不会影响程序的性能。
请参见:
1.3什么时候用一条switch语句比用多条if语句更好?
1.4switch语句必须包含default分支吗?
1.6除了在for语句中之外,在哪些情况下还要使用逗号运算符?
逗号运算符通常用来分隔变量说明、函数参数、表达式以及for语句中的元素。
下例给出了使用逗号的多种方式:
#include
#include
voidmain(void);
voidmain()
{
/*Here,thecommaoperatorisusedtoseparate
threevariabledeclarations. */
inti,j,k;
/*Noticehowyoucanusethecommaoperatortoperform
multipleinitializationsonthesameline. */
i=0,j=1,k=2;
printf("i=%d,j=%d,k=%d\n",i,j,k);
/*Here,thecommaoperatorisusedtoexecutethreeexpressions
inoneline:
assignktoi,incrementj,andincrementk.
Thevaluethatireceivesisalwaystherigbtmostexpression. */
i=(j++,k++);
printf("i=%d,j=%d,k=%d\n",i,j,k);
/*Here,thewhilestatementusesthecommaoperatorto
assignthevalueofiaswellastestit. */
while(i=(rand()%100),i!
=50)
printf("iis%d,tryingagain...\n",i)
printf("\nGuesswhat?
iis50!
\n")
}
请注意下述语句:
i:
(j++,k++)
这条语句一次完成了三个动作,依次为:
(1)把k值赋给i。
这是因为左值(lvaule)总是等于最右边的参数,本例的左值等于k。
注意,本例的左值不等于k++,因为k++是一个后缀自增表达式,在把k值赋给j之后k才会自增。
如果所用的表达式是++k,则++k的值会被赋给i,因为++k是一个前缀自增表达式,k的自增发生在赋值操作之前。
(2)j自增。
(3)k自增。
此外,还要注意看上去有点奇怪的while语句:
while(i=(rand()%100),i!
=50)
printf("iis%d,tryingagain...\n");
这里,逗号运算符将两个表达式隔开,while语句的每次循环都将计算这两个表达式的值。
逗号左边是第一个表达式,它把0至99之间的一个随机数赋给i;第二个表达式在while语句中更常见,它是一个条件表达式,用来判断i是否不等于50。
while语句每一次循环都要赋予i一个新的随机数,并且检查其值是否不等于50。
最后,i将被随机地赋值为50,而while语句也将结束循环。
请参见:
1.12运算符的优先级总能保证是“自左至右”或“自右至左”的顺序吗?
1.13++var和var++有什么区别?
1.7 怎样才能知道循环是否提前结束了?
循环通常依赖于一个或多个变量,你可以在循环外检查这些变量,以确保循环被正确执行。
请看下例:
intx
char*cp[REQUESTED_BLOCKS]
/*Attempt(invain,Imustadd...)to
allocate51210KBblocksinmemory. */
for(x=0; x { cpi[x]=(char*)malloc(10000,1) if(cp[x]==(char*)NULL) break } /*IfxislessthanREQUESTED-BLOCKS, theloophasendedprematurely. */ if(x printf("Bummer! Myloopendedprematurely! \n"); 注意,如果上述循环执行成功,它一定会循环512次。 紧接着循环的if语句用来测试循环次数,从而判断循环是否提前结束。 如果变量x的值小于512,就说明循环出错了。 1.8 goto,longjmp()和setjmp()之间有什么区别? goto语句实现程序执行中的近程跳转(localjump),longjmp()和setjmp()函数实现程序执行中的远程跳转(nonlocaljump,也叫farjump)。 通常你应该避免任何形式的执行中跳转,因为在程序中使用goto语句或longjmp()函数不是一种好的编程习惯。 goto语句会跳过程序中的一段代码并转到一个预先指定的位置。 为了使用goto语句,你要预先指定一个有标号的位置作为跳转位置,这个位置必须与goto语句在同一个函数内。 在不同的函数之间是无法实现goto跳转的。 下面是一个使用goto语句的例子: voidbad_programmers_function(void) { intx printf("ExcusemewhileIcountto5000...\n"); x----l~ while (1) { printf("%d\n",x) if(x==5000) gotoall_done else x=x+1; } all_done: prinft("Whew! Thatwasn'tsobad,wasit? \n"); } 如果不使用goto语句,是例可以编写得更好。 下面就是一个改进了实现的例子: voidbetter_function(void) { intx printf("ExcusemewhileIcountto5000...\n"); for(x=1;x<=5000,x++) printf("%d\n",x) printf("Whew! Thatwasn'tsobad,wasit? \n"); } 前面已经提到,longjmp()和setjmp()函数实现程序执行中的远程跳转。 当你在程序中调用setjmp()时,程序当前状态将被保存到一个jmp_buf类型的结构中。 此后,你可以通过调用longjmp()函数恢复到调用setjmp()时的程序状态。 与goto语句不同,longjmp()和setjmp()函数实现的跳转不一定在同一个函数内。 然而,使用这两个函数有一个很大的缺陷,当程序恢复到它原来所保存的状态时,它将失去对所有在longjmp()和setjmp()之间动态分配的内存的控制,也就是说这将浪费所有在longjmp()和setjmp()之间用malloc()和calloc()分配所得的内存,从而使程序的效率大大降低。 因此,你应该尽量避免使用longjmp()和setjmp()函数,它们和goto语句一样,都是不良编程习惯的表现。 下面是使用longjmp()函数和setjmp()函数的一个例子: #include #include jmp_bufsaved_state; voidmain(void); voidcall_longjmp(void); voidmain(void) { intret_code; printf("Thecurrentstateoftheprogramisbeingsaved...\n"); ret_code=setjmp(saved_state) if(ret_code==1) { printf("Thelongjmpfunctionhasbeencalled.\n") printf("Theprogram'spreviousstatehasbeenrestored.\n"); exit(0) } printf("Iamabouttocalllongjmpand\n"); printf('returntothepreviousprogramstate...\n") call_longjmp() } voidcall_longjmp(void) { longjmp(saved_state,1) } 1.9 什么是左值(lvaule)? 左值是指可以被赋值的表达式。 左值位于赋值语句的左侧,与其相对的右值(rvaule,见1.11)则位于赋值语句的右侧。 每条赋值语句都必须有一个左值和一个右值。 左值必须是内存中一个可存储的变量,而不能是一个常量。 下面给出了一些左值的例子: intx; int*p_int; x=1; p_int=5; 变量x是一个整数,它对应于内存中的一个可存储位置,因此,在语句“x=1”中,x就是一个左值。 注意,在第二个赋值语句“*p_int=5"中,通过“*”修饰符访问p_int所指向的内存区域;因此,p_int是一个左值。 相反,下面的几个例子就不是左值: #defineCONST_VAL10 intx /*example1*/ l=x; /*example2*/ CONST_VAL=5; 在上述两条语句中,语句的左侧都是一个常量,其值不能改变,因为常量不表示内存中可 存储的位置。 因此,这两条赋值语句中没有左值,编译程序会指出它们是错误的。 请参见: 1.10数组(array)可以是左值吗? . 1.11什么是右值(rvaule)? 1.10数组(array)可以是左值吗? 在1.9中,左值被定义为可被赋值的表达式。 那么,数组是可被赋值的表达式吗? 不是,因为数组是由若干独立的数组元素组成的,这些元素不能作为一个整体被赋值。 下述语句是非法的: intx[5],y[5]; x=y; 不过,你可以通过for循环来遍历数组中的每个元素,并分别对它们赋值,例如: inti; intx[5]; inty[5]; ...... for(i=0;i<5,i++) x[i]=y[i]; ...... 此外,你可能想一次拷贝整个数组,这可以通过象memcpy()这样的函数来实现,
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 数据结构 导论 自考 实验 语言