学习C语言100问.docx
- 文档编号:14092303
- 上传时间:2023-06-20
- 格式:DOCX
- 页数:35
- 大小:41.85KB
学习C语言100问.docx
《学习C语言100问.docx》由会员分享,可在线阅读,更多相关《学习C语言100问.docx(35页珍藏版)》请在冰点文库上搜索。
学习C语言100问
学习C语言100问
问题1:
什么是分程序(复合语句)?
分程序是指一对大括号{}之间的一段C语言程序。
每一个C函数的函数体都是包括在一对大括号中,switch语句所有取值情况的列举也是包括在一对大括号中,以此可以看出分程序在C语言中使用非常广泛,用户也可以根据需要自己组织分程序(更多的是程序功能的需要),它在C程序中的功能相当于一局部程序块,其间可以定义变量,这种变量称为局部变量,只能定义在分程序的开始部分,变量的有效范围是分程序内部。
如果局部变量与分程序以外的变量重名,在本分程序内部,该局部变量对外面的同名变量进行屏蔽,另外提示一点的是,一般不用分程序来保存变量,例如:
#include
inttest=5;
voidmain()
{
inttest=10;
voidfun1();/*-----5----*/
fun1();
printf(“2--%d\n”,test);/*-----10-----*/
{inttest=15;
printf(“3--%d\n”,test);/*-----15-----*/
}
}
voidfun1()
{
printf(“1--%d\n”,test);
}
问题2:
什么情况下用switch语句比if语句的多重嵌套更适合?
如果有两个以上基于同一个数字型变量(整型变量,字符型变量,枚举类型变量等)的条件表达式,尤其是对于作为判断的数字型变量的取值很有限,且对每一个不同的取值,其所做的处理也不一样的情况,最好使用一条switch语句,这样更易于阅读各维护。
这里有两点需要注意就是,第一就是用于作为判断条件的变量一定要是数字型的,另一点就是所有的判断条件都是基于同一个数字变量,而不是多个变量。
例如:
有如左下的if嵌套更适合用右下的switch语句表达。
chargrade;
if(grade==’A’)printf(“85~100\n”);
elseif(grade==’B’)printf(“70~84\n”);
elseif(grade==’C’)pritnf(“60~69\n”);
elseif(grade==’D’)printf(“<60\n”);
elseprintf(“error\n”);
switch(grade)
{case‘A’:
printf(“85~100\n”);break;
case‘B’:
printf(“70~84\n”);break;
case‘C’:
printf(“60~69\n”);break;
case‘D’:
printf(“<60\n”);break;
default:
printf(“error”);break;}
问题3:
switch语句必须包含default分支吗?
回答是否定的。
但是为了逻辑上的严密性,一般应写default分支,这样在出现所有的case子句以外的取值时才不至于难以确定其错误所在,例如象上面的例子,如省略了后面的default子句,而用户如输入的是除‘A’,‘B’,‘C’,‘D’以外的其他字符时,程序不做出任何反应,而这本身是一种非法输入。
问题4:
switch语句的最后一个分支可以不要break语句吗?
每一个case分支后面是否必须要加break语句?
两者的答案都是“不”。
大家知道,在switch语句中,如果作为条件判断的数字表达式的值与某一个case后面的取值相等,则以该case分支语句作为入口顺序执行后续语句,如遇上break语句,则结束switch语句的处理,转而处理switch语句的后续语句,根据这个道理显然最后一个分支中的break语句可以省略,因为无论是否有break语句,此时都会结束switch语句的处理。
问题5:
怎样判断循环是否提前结束?
在多循环条件下,又如何知道是因为哪个条件不满足而使循环提前结束的?
解决这种问题通常是在循环语句的后面再用一个或多个判断语句检查循环变量的取值,从而确知循环是正常结束还是提前结束,如循环条件是由多个循环变量构成,则可以对各个变量分别进行判断。
例如:
#include
voidisprime(intnum)
{ints;
for(s=2;s<=sqrt(num);s++)
if(num%s==0)flag=0;
if(s>sqrt(num))return
(1);
elsereturn(0);
}
以上的例子用于判断给定的数是否为素数,函数中的循环语句正常结束是当s的取值大于给定数的平方根,但只要该数能够被某一个大于1而小于其平方根的数整除,即说明该数不是素数,循环提前结束,于是在循环语句后面加一个判断,以得出正确结果。
问题6:
什么叫左子值?
数组名为什么不能作为左子值?
左子值是指可以被赋值的表达式,即可以出现在赋值符号的左侧的表达式。
同理出现在赋值符号右侧的称为右子值,每一个赋值语句都必须有一个左子值和一个右子值。
左子值必须是内存的一个存储单元(在程序中通常表现为一个变量),而不能是一个常量。
我们知道数组名在C语言中,代表的是数组所分配的存储单元的起始地址,而C语言中数组不可以在程序运行的过程进行移动,也就是说,数组一旦定义,其所占据的存储单元是固定的,也即他的起始地址也是固定不变,所以C语言在的数组名被当作是一个常量,也因此不能作为左子值。
问题7:
++var和var++有什么区别?
“++”运算符在C语句中的使用可以说多得不能胜数,但是要真正掌握它的运算却也并非易事,尤其是在多个运算符混合使用的情况下更是如此。
“++”有前辍运算(++var)和后辍运算(var++)两种,大家都知道前者在表达式中是先把var加1,而后引用var的值,后者是先引用var的值,而后var加1,但在如下的运用中,就很难用这种规则来解释了:
如:
intx=10;y=x+++x++;经以上运算y的值是多少呢?
答案是20,这里首先要确定各运算符的运算关系,按C语言确定运算符的规则(从左至右取尽量多的字符构成一个运算符),以上表达式相当y=(x++)+(x++),其中有两个后辍运算的自加运算,值得注意的是,引用时先把x的值在整个表达式中使用,而后进行两次加1操作,所以y后来的取值为20,而x的值为12,不理解的话,很容易得出y的值最后是21的错误结果。
再如:
intx=5;printf(“%d,%d\n”,x++,x++);输出的究竟是“5,6”,或“6,5”,还是“5,5”,再或者是其他取值呢?
按照一般的思路要得出正确结果还是颇费周折的,正确结果是“6,5”,这里牵涉到C语言实参的求值顺序(自右至左计算各实参表达式)。
问题8:
变量存储在内存中的什么地方?
变量名是一个符号地址,代表内存中的某个内存单元,那么它究竟存储在什么位置呢?
对于一般程序设计人员,并不需要具体知道变量的存储位置,但应该根据变量的存储属性判断变量的存储区域。
第一种是在函数外部定义的变量(全局变量或静态外部变量)和在函数内部定义的static变量,其生存期是程序运行的全过程,这些变量被存储在数据段中。
数据段是在内存中为这些变量留出的一段大小固定的空间。
第二种是在函数内部定义的auto变量(不加关键字static的变量)的生存期从程序开始执行其所在的程序块代码时开始,到程序离开该程序块时为止,即定义该变量的函数的部。
作为函数参数的变量只在调用该函数期间存在。
这些变量被存储在栈中。
栈是内存中的一段空间,开始很小,以后逐渐自动增大,这个界限由系统决定,并且通常非常大,因此程序员不必担心用尽栈空间。
第三种是内存空间实际上并不存储变量,但是可以用来存储变量所指向的数据。
如果把调用malloc()函数的结果赋给一个指针变量,那么这个指针变量将包含一块动态分配的内存的地址,这块内存位于一段名“堆(heap)”的内存空间中。
堆开始时也很小,但当程序员调用malloc()或calloc()等内存分配函数时它就会增大。
堆可以和数据段或栈共用一个内存段,也可以有它自己的内存段,这取决于编译选项和操作系统。
问题9:
使用register变量的意义何在?
有什么用途?
register作名词的本义是“注册,寄存器”的意思,在程序中如果用register修饰定义变量,表示将该变量保存在CPU的寄存器中,显然可以加快访问速度,但也不可以随意使用,而有以下几点限制:
第一,register变量必须是能被CPU寄存器所接受的数据类型,如不能把一个组合类型的变量指定为register变量。
第二,因为register变量保存在CPU中,显然不应该用取地址运算符“&”来获取变量的地址。
除去以上的限制外,也并不是说我们尽可能地多地定义register变量就能加快程序的运行速度,毕竟CPU中寄存器是有限的,如果你把变量指定为register变量,意味着可用于别的用途的寄存器就减少了,如程序运算产生的中间结果,它们的应用又很频繁,在寄存器不足的情况下,只好借助于内存,这样反倒会降低程序的运算速度。
在现今的C版本中,大多已没有定义register变量的必要,因为编译程序忽略register修饰符,而根据寄存器的使用情况和变量的情况决定是否把变量解释为register变量。
问题10:
浮点数比较是否可靠?
众所周知,计算机只能表示离散的数据,而浮点数是连续,任意两个浮点数之间都有无限多个浮点数的存在,所以浮点数是计算机编程中的一个盲点。
由于浮点数很难对付,因此比较一个浮点数和某个值是否相等或不等通常是不好的编程习惯。
但是,判断一个浮点数是否大于或小于某个数就安全多了。
例如:
如果你想以较小的步长依次使用一个范围内的数字,你可能编写这样一个程序:
#include
main()
{floatf;
for(f=0.0;f!
=70.0&&f<71.0;f+=0.007)
;
printf(“fisnow%g\n”,f);
}
然而,舍入误差可能导致f的值永远不等于70.0,这样,循环会跳过70.0。
加入不等式“f<71.0”变是为了防止在这种情况下程序继续运行很长时间。
一种安全的做法是用不等式“f<71.0”作为条件来终止循环,例如:
floatf;
for(f=0.0;f<71.0;f+=0.007)
;
或事先算出循环次数,然后通过整数进行循环计数:
floatf;
intcount=70.0/0.007;
for(f=0.0;count-->0;f+=0.007)
;
问题11:
说明一个变量和定义一个变量有什么区别?
说明一个变量意味着向一个编译程序描述变量的类型,但并不为变量分配空间。
定义一个变量意味着在说明变量的同时还为变量分配存储空间。
在定义一个变量的同时可以对变量进行初始化。
一个变量可以被说明多次,但只能被定义一次。
因此,不应该在头文件中定义变量,因为一个头文件可能会被一个程序的多个源文件包含。
函数说明和函数定义也有类似的性质,当然不是说头文件中也不应该定义函数。
问题12:
为什么在定义变量的时候应该说明变量的数据类型?
数据类型是变量的一个重要的属性。
不同的数据类型通常代表所定义的变量所占据的内存长度不同,编程时应该根据你所要的变量的取值范围选择一个合适的数据类型,例如:
同样是作为循环计数变量,如果只循环100次,则可以选择循环变量的类型为字符型(char),而在循环次数为50000次的时候,则应该选择循环变量的类型为长整型。
此外,不同的数据类型往往也代表其所属类型可以参加的运算也不同。
比如:
整型变量可以有取模(%)、按位与(&)等运算,而浮点数则不具有这些运算。
因此,编程时也应该根据变量所要参加的运算而选择相应的数据类型。
问题13:
怎样使用宏定义(#define)?
使用宏定义有什么好处?
宏定义(#define)是一种预处理指令,在程序中用字符串(宏体)来被定义的宏名。
有以下几点好处:
第一,在输入源代码时,可省去许多键入操作,因为宏名往往比宏体要短。
第二,因为宏只需定义一次,但可以多次使用,而且宏名往往代表一定的含义,所以使用宏能增强程序的易读性和可靠性。
第三,使用宏不需要额外的开销,因为宏所代表的代码只在宏名出现的地方展开,因此不象函数调用会引起程序跳转,宏展开不会引起程序跳转。
第四,宏的参数对类型不敏感,因此你不必考虑将何种类型的数据传递给宏。
宏虽然有诸多好处,但在使用也应该小心行事,尤其要注意以下几个方面:
第一,在宏名和括起来的参数的括号之间绝对不能有任何空格类字符。
第二,为避免在宏展开时产生歧义,宏体也应该用括号括起来。
第三,对传递给宏的实参要千万小心,尤其是中把自增(减)变量作为参数传递给宏时。
问题14:
可以用#include指令包含扩展名不是“.h”的文件吗?
回答是肯定的。
可以用#include命令包含任何一个文件。
如果一个程序是由多个源程序文件所构成,用在一个源程序文件中使用#include把其它程序文件都包含进来的方法是解决多文件程序的一种有效方法。
当然,为了程序的可读性考虑,最好不要把不以“.h”为扩展名的文件用#include命令包含进来,这样不容易区分哪些文件是用于编译预处理的。
问题15:
在程序中加入注释有什么好处?
注释作为一种被编译程序所忽略的说明性文字,往往被初学编程人员所忽视,认为它对于程序的功能没有任何作用,不加并没有损害,所以往往很少使用注释,这种看法是完全错误的。
适当使用注释至少有以下两个方面的好处:
第一,在软件技术相对滞后的今天,为了开发软件的需要,大量的程序除了要能在计算机上运行外,出于种种原因,还需要程序源代码能够让编程人员读懂,而这本身是一件非常费力的工作,此时,注释的作用就不可低估了,有无良好的注释成了程序能不能读懂的关键点之一,这也现代源程序文档化要求的一个重要方面。
第二,注释还可以为程序调试带来很大的方便。
无论是多么有经验的程序人员,他编制的程序都不可能一次即保证完全正确,也因此需要对程序进行调试。
调试工作的第一步是查错,这也是一件很费力的工作,往往是按模块分部进行。
如怀疑某模块有错时,可以先将原来的模块注释掉,加入一个新编模块,如错误消失,则说明原来的模块确实有问题,此时才将原来的模块删除。
问题16:
怎样检查一个符号是否已经被定义?
利用预处理指令#ifdef可以检查一个符号是否已被定义,例如,在指针一节中,你不能确切知道NULL这个符号是否已经有定义,你可以用如下的一段程序实现这一点:
#ifdefNULL
#undefNULL
#endif
#defineNULL(void*)0
这里,首先检查NULL符号是否已被定义,如果已有定义,则取消原来的定义,然后重新定义NULL符号。
问题17:
什么是变量的间接引用?
用变量名对变量的值进行引用的方式称为变量的直接引用。
对指向变量或内存中的任何对象的指针来说,指针就是对变量值的间接引用。
如果p是一个指针,p的值就是其所指向的对象的地址;*p的值就是其所指向的内存单元的值。
值得注意的是,用指针间接引用变量的值时,一定要明确知道指针的当前指向,断不可以随意通过指针间接引用而修改一个内存单元的值。
如:
int*p,a;
*p=5;
的做法就是很危险的,指针p当前的值不知道的情况下,它既可能指向用户程序区,也可能指向别的用户的程序区,还有可能指向操作系统区,盲目修改可能会带来严重后果。
问题18:
什么是空指针?
在指针和链表中用的非常广泛的空指针,表示的是不指向任何对象的一种指针,其值定义为NULL,它是在头文件“stdio.h”中定义的一个宏,其值与任何有效指针的值都不同,NULL是一个纯粹的零,它可能被强制转换为void*或char*类型。
即NULL可能是0或(void*)0等。
值得注意的是,绝对不能间接引用一个空指针,否则,你的程序可能会得到一个毫无意义的结果,或得到一个全部是零的值,或者会突然停止运行。
问题19:
什么是void指针?
void指针一般称为通用指针,千万要与空指针严格区分,void指针指向某个对象,但该对象不属于任何类型,但之所以称它为通用指针,是因为任何时候你都可能用其它类型的指针来代替void指针或者用void指针来代替其它类型的指针,如:
函数malloc()的返回值是void类型,你可以在程序中将它的返回值赋给任何类型的指针。
问题20:
两个指针可以相减吗?
从一般意义上讲,回答是可以。
但作为一种编程工具,在程序中做任何事都得考虑其物理意义,两个指向不同类型对象的指针其相减就没有任何意义,故不应该做减法,同样不是同一组相关单元的两个指针其相减也没有任何意义,这就象问人家两个不同街道的两个门牌号之间相差多少个门牌号一样,是没有任何价值的,因此真正有意义的两个指针相减,只是在当两个指针指向相关的同类型的对象时,通常是数组,才对它们进行相减,其值为两个指针所指向的对象之间相差的同类型的对象的个数,即数组元素的个数。
问题21:
数组的尺寸可以在程序运行时才确定吗?
在C语言中,回答是肯定的,也就是说C语言不可以定义动态数组,数组的大小必须是在程序编译的时候就已经确定。
可能这样会给程序的灵活性带来一定的影响,但动态数组的定义会得程序的运行速度的降低。
如果你确实事先难以确定数组的大小,其实C语言还是可以帮你实现的,那就是我们可以定义一个指针,然后调用malloc()或calloc()函数从堆中为这个数组分配内存空间,如:
main()
{int*pa,num;
scanf(“%d”,&num);
pa=(int*)malloc(num*sizeof(int));
…………
}
问题22:
malloc()函数与calloc()函数有什么区别?
这两个函数都可以用来分配动态内存空间,但两者除了调用时的参数的数目不同以外,还有一点重要的区别,那就是
malloc()函数不能初始化所分配的内存空间,而calloc()函数会将所分配的内存空间的每一位都初始化为零。
这样一来,用malloc()函数所分配的空间,如果原来没有使用过,则其中的每一位都是0,但如果这部分内容曾被分配、释放和重新分配,则其中可能遗留原有的数据。
用calloc()函数所分配的空间,无论以前是否使用过,都将初始化为零。
所以在使用中,程序员应该根据你的实际需要选择相应的函数,除此外,从使用的角度看,这两个函数没有太大的区别。
问题23:
什么叫做内部函数?
怎样说明?
只在定义某函数的程序文件中才能调用的函数称为内部函数。
与变量不同,函数的默认属性是外部的,即可以被其他程序文件中函数所调用,因此为了说明一个函数是内部函数,在定义该函数或说明该函数时应该加修饰符static,
如:
/*文件1..c*/
#include“stdio.h”
staticvoidsort(int*a,intn);
voidselect(int*a,intn,intnum);
……………
voidsort(int*a,intn)
{
intj,k;
………
}
voidselect(int*a,intn,intnum)
{
intk,mid;
sort(a,n);
………
}
在“文件1.c”中,函数sort就是内部函数,它只能在该程序文件中调用,而不能在其他程序文件中调用,而函数select就是外部函数,它可以在其他程序文件中调用。
问题23:
为什么要说明函数原型?
函数原型提供函数调用时所需的参数个数及各参数的类型,编译程序就据此检查函数调用时给定的实参是否符合要求。
当然,如果函数定义总在函数调用处之前,可以不必说明函数原型,但是,因为C程序可以编制得非常大,函数调用之处与函数的定义之处可能相隔非常之远,这样编程时每次都去参考函数定义处的参数的数目和类型,其不方便之处显而易见;如果使用函数说明,则因为可以与调用处相隔很近(因为函数可以说明多次)。
在多程序文件程序中尤其如此,往往是在程序的开头对程序中所使用到的函数都进行说明,以便查找。
值得注意的是,如果函数定义在后,调用在前,应务必在调用前对函数原型进行说明。
问题24:
一个函数可以有多少个参数吗?
对于一个函数的参数的数目C语言并没有明确的说明,但参数不应该太多,这也是编程人员应该遵循的一种程序风格。
因为函数参数在调用过程中存放在堆栈中,参数数目太多的情况下,意味着更多的数据需要保留,从而降低了函数的运行速度,从另一个方面说,参数越多编程的难度显然也会加大,有利于发现程序在错误,因此,通常应该尽可能地减少参数的数目,如果实在要使用很多的参数时,可以考虑用一个结构体类型变量来容纳这些参数,用这个结构体类型的变量来作为函数的参数。
问题25:
如果一个函数没有返回值,是否需要加入return语句?
如果一个函数没有返回值(函数原型的“返回值”类型为void),此时没有必要加入return语句,但函数执行如可能引起严重错误(可以预料的),则应该预设return语句返回,另外注意一点的是,不应该在函数中设多处return语句,这程序编程习惯不可取,而应该让函数的退出操作尽量集中和简洁(必要时不妨使用goto语句)。
需要注意的是,函数没有返回值不是在函数定义时缺省返回值类型的情况,其实应该对所有函数都明确给定其返回值类型,包括返回值为空(即没有返回值)。
问题26:
用数组名或指针作为函数的参数其实质是什么?
众所周知,数组名做为一个符号地址其所代表的是数组所分配的内存单元的起始地址,而指针本身就是地址,用这两者作为函数的形参时,它们是等价的,都是相当于用指针变量作为函数的形参(也正因于此,用数组名作函数形参时,可以进行自增、自减等运算);用这两者作为函数的实参时,按C语言中“用值传递”参数传递原则,即是把实参数组名(指针)的值传递给相应的形参(因传递的值是一个地址,故有些资料介绍这是一种“地址传递”),也就是说,经过参数传递后,形参数组名(指针)与实参数组名(指针)指向同一内存单元,故此,如果在函数中通过形参数组名(指针)的引用可以修改实参数组名(指针)所代表的数组元素的值。
问题27:
exit()与return有什么不同?
用exit()可以退出程序的处理而把控制权交回操作系统,而用return语句可以结束当前函数的处理,控制权交回调用该函数的函数(即所谓主调函数)。
前者通常是用在程序中可能发生的不可忽略的错误时使用,如在文件打开出错时,我们通常用exit()来退出程序的处理,而后者是作为函数运行的正常情况或非严重错误时使用,但如在main()中使用return语句,其效果与exit()没有多大差别。
问题28:
为什么要谨慎对待紧跟在数组后面的元素?
C语言不检查数组下标的越界,这是众所周知的事,在给程序员带来了一定的灵活性的同时,却也给程序编制留下了一个不小的陷阱,要切记C语言的数组元素下标是从0开始的,也就是说下标值为数组长度的元素已经不是数组的元素,例如:
inta[10];a[10]=5;如果不是特意安排,象这种赋值在程序中就不应该出现。
在定义一个数组后,系统为其分配相应的存储区间,而数组后面的存储单元既然不是数组存储区内,系统可能会将它分给其他的程序或系统程序使用,显然,如果你在程序随意将这些单元进行修改,可能会影响其他程序甚至系统程序,从而产生不可预料的后果,所以对紧跟数组的存储单元应该谨慎而又谨慎。
问题29:
通过指针和数组下标方式都可以访问数组元素,哪一种方式更好?
首先让我们看一下两种引用方式的引用过程。
例如:
in
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 学习 语言 100
![提示](https://static.bingdoc.com/images/bang_tan.gif)