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