C语言编程中的常见问题Word文档下载推荐.docx
《C语言编程中的常见问题Word文档下载推荐.docx》由会员分享,可在线阅读,更多相关《C语言编程中的常见问题Word文档下载推荐.docx(10页珍藏版)》请在冰豆网上搜索。
test_var)
}
Endindependentlocalblock
printf(Testvariableaftertheifstatement%dn,test_var);
Endlocalblockforfunctionmain()
上例产生如下输出结果:
Testvariablebeforetheifstatement10
Testvariablewithintheifstatement5
Testvariablewithintheindependentlocalblock0
Testvariableaftertheifstatement10
注意,在这个例子中,每次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)
case1printf(xisequaltoone.n);
break;
case2printf(xisequaltotwo.n);
break
case3printf('
xisequaltothree.n);
defaultprintf(xisnotequaltoone,two,orthree.n);
注意,使用switch语句的前提是条件表达式必须基于同一个数字型变量。
例如,尽管下述if语句包含两个以上的条件,但该例不能使用switch语句,因为该例基于字符串比较,而不是数字比较:
charname=Lupto;
if(!
stricmp(name,Isaac))
printf(Yournamemeans'
Laughter'
.n);
elseif(!
stricmp(name,Amy))
Beloved'
stricmp(name,Lloyd))
Mysterious'
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)
N'
n'
printf(YouansweredNO!
n);
但是,如果一个未知字符被传递给这条switch语句,会出现什么情况呢这时,程序将没有任何输出。
因此,最好还是加入一个default分支,以处理这种情况:
......
defaultprintf(Unknownresponse%dn,char_code);
此外,default分支能给逻辑检查带来很多方便。
例如,如果用switch语句来处理数目固定的条件,而且认为这些条件之外的值都属于逻辑错误,那么可以加入一个default分支来辨识逻辑错误。
请看下列:
voidmove_cursor(intdirection)
switch(direction)
caseUPcursor_up()
caseDOWNcursor_down()
caseLEFTcursor_left()
caseRIGHTcursor_right()
defaultprintf(Logicerroronlinenumber%ld!
!
n,
__LINE__)
1.5Switch语句的最后一个分支可以不要break语句吗
尽管switch语句的最后一个分支不一定需要break语句,但最好还是在switch语句的每个分支后面加上break语句,包括最后一个分支。
这样做的主要原因是:
你的程序很可能要让另一个人来维护,他可能要增加一些新的分支,但没有注意到最后一个分支没有break语句,结果使原来的最后一个分支受到其后新增分支的干扰而失效。
在每个分支后面加上break语句将防止发生这种错误并增强程序的安全性。
此外,目前大多数优化编译程序都会忽略最后一条break语句,所以加入这条语句不会影响程序的性能。
1.3什么时候用一条switch语句比用多条if语句更好
1.6除了在for语句中之外,在哪些情况下还要使用逗号运算符
逗号运算符通常用来分隔变量说明、函数参数、表达式以及for语句中的元素。
下例给出了使用逗号的多种方式:
#includestdlib.h
voidmain()
Here,thecommaoperatorisusedtoseparate
threevariabledeclarations.
inti,j,k;
Noticehowyoucanusethecommaoperatortoperform
multipleinitializationsonthesameline.
i=0,j=1,k=2;
printf(i=%d,j=%d,k=%dn,i,j,k);
Here,thecommaoperatorisusedtoexecutethreeexpressions
inonelineassignktoi,incrementj,andincrementk.
Thevaluethatireceivesisalwaystherigbtmostexpression.
i=(j++,k++);
printf(i=%d,j=%d,k=%dn,i,j,k);
Here,thewhilestatementusesthecommaoperatorto
assignthevalueofiaswellastestit.
while(i=(rand()%100),i!
=50)
printf(iis%d,tryingagain...n,i)
printf(nGuesswhatiis50!
n)
请注意下述语句:
i:
(j++,k++)
这条语句一次完成了三个动作,依次为:
(1)把k值赋给i。
这是因为左值(lvaule)总是等于最右边的参数,本例的左值等于k。
注意,本例的左值不等于k++,因为k++是一个后缀自增表达式,在把k值赋给j之后k才会自增。
如果所用的表达式是++k,则++k的值会被赋给i,因为++k是一个前缀自增表达式,k的自增发生在赋值操作之前。
(2)j自增。
(3)k自增。
此外,还要注意看上去有点奇怪的while语句:
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
charcp[REQUESTED_BLOCKS]
Attempt(invain,Imustadd...)to
allocate51210KBblocksinmemory.
for(x=0;
xREQUESTED_BLOCKS;
x++)
cpi[x]=(char)malloc(10000,1)
if(cp[x]==(char)NULL)
IfxislessthanREQUESTED-BLOCKS,
theloophasendedprematurely.
if(xREQUESTED_BLOCKS)
printf(Bummer!
Myloopendedprematurely!
n);
注意,如果上述循环执行成功,它一定会循环512次。
紧接着循环的if语句用来测试循环次数,从而判断循环是否提前结束。
如果变量x的值小于512,就说明循环出错了。
1.8goto,longjmp()和setjmp()之间有什么区别
goto语句实现程序执行中的近程跳转(localjump),longjmp()和setjmp()函数实现程序执行中的远程跳转(nonlocaljump,也叫farjump)。
通常你应该避免任何形式的执行中跳转,因为在程序中使用goto语句或longjmp()函数不是一种好的编程习惯。
goto语句会跳过程序中的一段代码并转到一个预先指定的位置。
为了使用goto语句,你要预先指定一个有标号的位置作为跳转位置,这个位置必须与goto语句在同一个函数内。
在不同的函数之间是无法实现goto跳转的。
下面是一个使用goto语句的例子:
voidbad_programmers_function(void)
printf(ExcusemewhileIcountto5000...n);
x----l~
while
(1)
printf(%dn,x)
if(x==5000)
gotoall_done
x=x+1;
all_done
prinft(Whew!
Thatwasn'
tsobad,wasitn);
如果不使用goto语句,是例可以编写得更好。
下面就是一个改进了实现的例子:
voidbetter_function(void)
printf(ExcusemewhileIcountto5000...n);
for(x=1;
x=5000,x++)
printf(Whew!
tsobad,wasitn);
前面已经提到,longjmp()和setjmp()函数实现程序执行中的远程跳转。
当你在程序中调用setjmp()时,程序当前状态将被保存到一个jmp_buf类型的结构中。
此后,你可以通过调用longjmp()函数恢复到调用setjmp()时的程序状态。
与goto语句不同,longjmp()和setjmp()函数实现的跳转不一定在同一个函数内。
然而,使用这两个函数有一个很大的缺陷,当程序恢复到它原来所保存的状态时,它将失去对所有在longjmp()和setjmp()之间动态分配的内存的控制,也就是说这将浪费所有在longjmp()和setjmp()之间用malloc()和calloc()分配所得的内存,从而使程序的效率大大降低。
因此,你应该尽量避免使用longjmp()和setjmp()函数,它们和goto语句一样,都是不良编程习惯的表现。
下面是使用longjmp()函数和setjmp()函数的一个例子:
#includesetjmp.h
jmp_bufsaved_state;
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(Iamabouttocalllongjmpandn);
printf('
returntothepreviousprogramstate...n)
call_longjmp()
voidcall_longjmp(void)
longjmp(saved_state,1)
1.9什么是左值(lvaule)
左值是指可以被赋值的表达式。
左值位于赋值语句的左侧,与其相对的右值(rvaule,见1.11)则位于赋值语句的右侧。
每条赋值语句都必须有一个左值和一个右值。
左值必须是内存中一个可存储的变量,而不能是一个常量。
下面给出了一些左值的例子:
intx;
intp_int;
x=1;
p_int=5;
变量x是一个整数,它对应于内存中的一个可存储位置,因此,在语句“x=1”中,x就是一个左值。
注意,在第二个赋值语句“p_int=5中,通过“”修饰符访问p_int所指向的内存区域;
因此,p_int是一个左值。
相反,下面的几个例子就不是左值:
#defineCONST_VAL10
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;
i5,i++)
x[i]=y[i];
此外,你可能想一次拷贝整个数组,这可以通过象memcpy()这样的函数来实现,例如:
memcpy(x,y,sizeof(y));
与数组不同,结构(structure)可以作为左值。
你可以把一个结构变量赋给另一个同类型的结构变量,例如:
typedefstructt_name
charlast_name[25];
charfirst_name[15];
charmiddle-init[2];
}NAME
...
NAMEmy_name,your_name;
your_name=my_name;
在上例中,结构变量my_name的全部内容被拷贝到结构变量your_name中,其作用和下述语句是相同的:
memcpy(your_name,my_name,sizeof(your_name);
1.11什么是右值(rvaule)
在1.9中,左值被定义为可被赋值的表达式,你也可以认为左值是出现在赋值语句左边的表达式。
这样,右值就可以被定义为能赋值的表达式,它出现在赋值语句的右边。
与左值不同,右值可以是常量或表达式:
例如:
intX,y;
x=1;
1iSanrvalue,xisanlvalue
y=(x+1);
(x+1)isanrvalue;
yisanlvalue
在1.9中已经介绍过,一条赋值语句必须有一个左值和一个右值,因此,下述语句无法通过编译,因为它缺少一个右值:
x=void_function_call();
the{unctionvoid—function—call()
returnsnothing
如果上例中的函数返回一个整数,那么它可以被看作一个右值,因为它的返回值可以存储
到左值x中。
1.9什么是左值(lvaule)
1.10数组可以是左值吗
对这个问题的简单回答是:
这两种顺序都无法保证。
C语言并不总是自左至右或自右至左求值,一般说来,它首先求函数值,其次求复杂表达式的值,最后求简单表达式的值。
此外,为了进一步优化代码,目前流行的大多数C编译程序常常会改变表达式的求值顺序。
因此,你应该用括号明确地指定运算符的优先级。
例如,请看下述表达式:
a=b+cdfunction—call()5
上述表达式的求值顺序非常模糊,你很可能得不到所要的结果,因此,你最好明确地指定运算符的优先级:
a=b+(((cd)function—call())5)
这样,就能确保表达式被正确求值,而且编译程序不会为了优化代码而重新安排运算符的优先级了。
“++”运算符被称为自增运算符。
如果“++”运算符出现在变量的前面(++var),那么在表达式使用变量之前,变量的值将增加1。
如果“++”运算符出现在变量之后(var++),那么先对表达式求值,然后变量的值才增加1。
对自减运算符(--)来说,情况完全相同。
如果运算符出现在变量的前面,则相应的运算被称为前缀运算;
反之,则称为后缀运算。
例如,请看一个使用后缀自增运算符的例子:
intx,y;
y=(x++5);
上例使用了后缀自增运算符,在求得表达式的值之后,x的值才增加1,因此,y的值为1乘以5,等于5。
在求得表达式的值之后,x自增为2。
现在看一个使用前缀自增运算符的例子:
y=(++x5);
这个例子和前一个相同,只不过使用了前缀自增运算符,而不是后缀自增运算符,因此,x的值先增加1,变为2,然后才求得表达式的值。
这样,y的值为2乘以5,等于10。
1.14取模运算符(modulusoperator