C语言常见问题.docx

上传人:b****9 文档编号:23316691 上传时间:2023-05-16 格式:DOCX 页数:24 大小:27.72KB
下载 相关 举报
C语言常见问题.docx_第1页
第1页 / 共24页
C语言常见问题.docx_第2页
第2页 / 共24页
C语言常见问题.docx_第3页
第3页 / 共24页
C语言常见问题.docx_第4页
第4页 / 共24页
C语言常见问题.docx_第5页
第5页 / 共24页
点击查看更多>>
下载资源
资源描述

C语言常见问题.docx

《C语言常见问题.docx》由会员分享,可在线阅读,更多相关《C语言常见问题.docx(24页珍藏版)》请在冰豆网上搜索。

C语言常见问题.docx

C语言常见问题

1C

本章主要描述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(

"Testvariablewithintheindependentlocal

block:

%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,thecommaoperatorisusedtoexecutethree

expressions

inoneline:

assignktoi,incrementj,andincrementk.

Thevaluethatireceivesisalwaystherigbtmost

expression.*/

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.8goto,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()

这样的函数来实现,例如:

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.9什么是左值(lvaule)?

1.11什么是右值(rvaule)?

1.11什么是右值(rvaule)?

在1.9中,左值被定义为可被赋值的表达式,你也可以认为

左值是出现在赋值语句左边的表达式。

这样,右值就可以被定

义为能赋值的表达式,它出现在赋值语句的右边。

与左值不同,

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > PPT模板 > 简洁抽象

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1