📄 c.txt
字号:
以文本方式查看主题
- 大学生科技协会 (http://glietkx.home2.cernet.cn/index.asp)
-- C/C++ (http://glietkx.home2.cernet.cn/list.asp?boardid=2)
---- C语言编程常见问题解答 (http://glietkx.home2.cernet.cn/dispbbs.asp?boardid=2&id=167)
--------------------------------------------------------------------------------
-- 作者:PrOve
-- 发布时间:2005-4-5 22:59:22
-- C语言编程常见问题解答
这个帖子将不断扩充、更新 罗列大部分C语言编程的常见问题
希望各位观众支持帮助 毕竟一人之力太有限
小弟在此先谢过!
--------------------------------------------------------------------------------
-- 作者:PrOve
-- 发布时间:2005-4-5 23:01:44
--
1. tch语句必须包含default分支吗?
不,但是为了进行错误检查或逻辑检查,还是应该tch语句中加入default分支。例如,下tch语句完全合法:
{
case tyt:
case \'y\': printf ( " You answered YES ! \\n" )
break
case \'N\':
case \'n\': printf ("You answered NO!\\n");
break
}
但是,如果一个未知字符被传递给这tch语句,会出现什么情况呢?这时,程序将没有任何输出。因此,最好还是加入一个default分支,以处理这种情况:
......
default: printf ("Unknown response : %d\\n", char_code);
break
......
此外,default分支能给逻辑检查带来很多方便。例如,如果tch语句来处理数目固定的条件,而且认为这些条件之外的值都属于逻辑错误,那么可以加入一个default分支来辨识逻辑错误。请看下列:
void move_cursor (int direction)
{
tch (direction)
{
case UP: cursor_up()
break
case DOWN: cursor_down()
break
case LEFT: cursor_left ()
break
case RIGHT: cursor_ right ( )
break
default: printf ("Logic error on line number %ld!!! \\n",
__ LINE__ )
break
}
}
--------------------------------------------------------------------------------
-- 作者:PrOve
-- 发布时间:2005-4-5 23:02:27
--
2. switch语句的最后一个分支可以不要break语句吗?
尽管switch语句的最后一个分支不一定需要break语句,但最好还是在switch语句的每个分支后面加上break语句,包括最后一个分支。这样做的主要原因是:你的程序很可能要让另一个人来维护,他可能要增加一些新的分支,但没有注意到最后一个分支没有break语句,结果使原来的最后一个分支受到其后新增分支的干扰而失效。在每个分支后面加上break语句将防止发生这种错误并增强程序的安全性。此外,目前大多数优化编译程序都会忽略最后一条break语句,所以加入这条语句不会影响程序的性能。
--------------------------------------------------------------------------------
-- 作者:PrOve
-- 发布时间:2005-4-5 23:02:59
--
3.除了在for语句中之外,在哪些情况下还要使用逗号运算符?
逗号运算符通常用来分隔变量说明、函数参数、表达式以及for语句中的元素。下例给出了使用逗号的多种方式:
#include <stdio.h>
#include <stdlib.h>
void main(void);
void main ()
{
/ * Here, the comma operator is used to separate
three variable declarations. * /
int i, j, k;
/ * Notice how you can use the comma operator to perform
multiple initializations on the same line. * /
i=0, j=1, k=2;
printf("i= %d, j=%d, k= %d\\n", i, j, k);
/ * Here, the comma operator is used to execute three expressions
in one line: assign k to i, increment j, and increment k.
The value that i receives is always the rigbtmost expression. * /
i= ( j++, k++ );
printf("i=%d, j=%d, k=%d\\n", i, j, k);
/ * Here, the while statement uses the comma operator to
assign the value of i as well as test it. * /
while (i=(rand() % 100), i !=50)
printf("i is %d, trying again... \\n", i)
printf ("\\nGuess what? i is 50!\\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("i is %d, trying again... \\n");
这里,逗号运算符将两个表达式隔开,while语句的每次循环都将计算这两个表达式的值。逗号左边是第一个表达式,它把0至99之间的一个随机数赋给i;第二个表达式在while语句中更常见,它是一个条件表达式,用来判断i是否不等于50。while语句每一次循环都要赋予i一个新的随机数,并且检查其值是否不等于50。最后,i将被随机地赋值为50,而while语句也将结束循环。
--------------------------------------------------------------------------------
-- 作者:PrOve
-- 发布时间:2005-4-5 23:03:30
--
4.怎样才能知道循环是否提前结束了?
循环通常依赖于一个或多个变量,你可以在循环外检查这些变量,以确保循环被正确执行。请看下例:
int x
char * cp[REQUESTED_BLOCKS]
/ * Attempt (in vain, I must add... )to
allocate 512 10KB blocks in memory. * /
for (x = 0; x<REQUESTED_ BLOCKS ; x++ )
{
cpi[x]= (char * ) malloc (10000,1)
if (cp[x]= = (char * ) NULL)
break
}
/ * If x is less than REQUESTED-BLOCKS,
the loop has ended prematurely. * /
if (x<REQUESTED_BLOCKS)
printf ("Bummer ! My loop ended prematurely ! \\n" );
注意,如果上述循环执行成功,它一定会循环512次。紧接着循环的if语句用来测试循环次数,从而判断循环是否提前结束。如果变量x的值小于512,就说明循环出错了。
--------------------------------------------------------------------------------
-- 作者:PrOve
-- 发布时间:2005-4-5 23:04:36
--
5.运算符的优先级总能保证是“自左至右”或“自右至左”的顺序吗?
对这个问题的简单回答是:这两种顺序都无法保证。C语言并不总是自左至右或自右至左求值,一般说来,它首先求函数值,其次求复杂表达式的值,最后求简单表达式的值。此外,为了进一步优化代码,目前流行的大多数C编译程序常常会改变表达式的求值顺序。因此,你应该用括号明确地指定运算符的优先级。例如,请看下述表达式:
a=b+c/d/function—call() * 5
上述表达式的求值顺序非常模糊,你很可能得不到所要的结果,因此,你最好明确地指定运算符的优先级:
a=b+(((c/d)/function—call())* 5)
这样,就能确保表达式被正确求值,而且编译程序不会为了优化代码而重新安排运算符的优先级了。
--------------------------------------------------------------------------------
-- 作者:PrOve
-- 发布时间:2005-4-5 23:08:11
--
6.变量存储在内存(memory)中的什么地方?
变量可以存储在内存中的不同地方,这依赖于它们的生存期。在函数外部定义的变量(全局变量或静态外部变量)和在函数内部定义的static变量,其生存期就是程序运行的全过程,这些变量被存储在数据段(datasegment)中。数据段是在内存中为这些变量留出的一段大小固定的空间,它分为两部分,一部分用来存放初始化变量,另一部分用来存放未初始化变量。
在函数内部定义的auto变量(没有用关键字static定义的变量)的生存期从程序开始执行其所在的程序块代码时开始,到程序离开该程序块时为止。作为函数参数的变量只在调用该函数期间存在。这些变量被存储在栈(stack)中。栈是内存中的一段空间,开始很小,以后逐渐自动增大,直到达到某个预定义的界限。在象DOS这样的没有虚拟内存(virtual memory)的系统中,这个界限由系统决定,并且通常非常大,因此程序员不必担心用尽栈空间。关于虚拟内存 的讨论,请参见2.3。
第三种(也是最后一种)内存空间实际上并不存储变量,但是可以用来存储变量所指向的数据。如果把调用malloc()函数的结果赋给一个指针变量,那么这个指针变量将包含一块动态分配的内存的地址,这块内存位于一段名为“堆(heap)”的内存空间中。堆开始时也很小,但当程序员调用malloc()或calloc()等内存分配函数时它就会增大。堆可以和数据段或栈共用一个内存段(memorysegment),也可以有它自己的内存段,这完全取决于编译选项和操作系统。
与栈相似,堆也有一个增长界限,并且决定这个界限的规则与栈相同。
--------------------------------------------------------------------------------
-- 作者:PrOve
-- 发布时间:2005-4-5 23:08:40
--
7.变量必须初始化吗?
不。使用变量之前应该给变量一个值,一个好的编译程序将帮助你发现那些还没有被给定一个值就被使用的变量。不过,变量不一定需要初始化。在函数外部定义的变量或者在函数内部用static关键字定义的变量(被定义在数据段中的那些变量,见2.1)在没有明确地被程序初始化之前都已被系统初始化为0了。在函数内部或程序块内部定义的不带static关键字的变量都是自动变量,如果你没有明确地初始化这些变量,它们就会具有未定义值。如果你没有初始化一个自动变量,在使用它之前你就必须保证先给它赋值。
调用malloc()函数从堆中分配到的空间也包含未定义的数据,因此在使用它之前必须先进行初始化,但调用calloc()函数分配到的空间在分配时就已经被初始化为0了。
--------------------------------------------------------------------------------
-- 作者:PrOve
-- 发布时间:2005-4-5 23:09:28
--
8.什么时候应该使用register修饰符?它真的有用吗?
register修饰符暗示编译程序相应的变量将被频繁使用,如果可能的话,应将其保存在CPU的寄存器中,以加快其存取速度。但是,使用register修饰符有几点限制。
首先,register变量必须是能被CPU寄存器所接受的类型。这通常意味着register变量必须是一个单个的值,并且其长度应小于或等于整型的长度。但是,有些机器的寄存器也能存放浮点数。
其次,因为register变量可能不存放在内存中,所以不能用取址运算符“&”来获取register变量的地址。如果你试图这样做,编译程序就会报告这是一个错误。
register修饰符的用处有多大还受其它一些规则的影响。因为寄存器的数量是有限的,而且某些寄存器只能接受特定类型的数据(如指针和浮点数),因此,真正能起作用的register修饰符的数目和类型都依赖于运行程序的机器,而任何多余的register修饰符都将被编译程序所忽略。
在某些情况下,把变量保存在寄存器中反而会降低运行速度,因为被占用的寄存器不能再用于其它目的,或—者变量被使用的次数不够多,不足以抵消装入和存储变量所带来的额外开销。
那么,什么时候应该使用register修饰符呢?回答是,对现有的大多数编译程序来说,永远不要使用register修饰符。早期的C编译程序不会把变量保存在寄存器中,除非你命令它这样做,这时register修饰符是C语言的一种很有价值的补充。然而,随着编译程序设计技术的进步,在决定哪些变量应该被存到寄存器中时,现在的C编译程序能比程序员作出更好的决定。
实际上,许多C编译程序会忽略register修饰符,因为尽管它完全合法,但它仅仅是暗示而不是命令。
在极罕见的情况下,程序运行速度很慢,而你也知道这是因为有一个变量被存储在内存中,也许你最后会试图在该变量前面加上register修饰符,但是,如果这并没有加快程序的运行速度,你也不要感到奇怪。
--------------------------------------------------------------------------------
-- 作者:PrOve
-- 发布时间:2005-4-5 23:10:05
--
9.什么时候应该使用const修饰符?
使用const修饰符有几个原因,第一个原因是这样能使编译程序找出程序中不小心改变变量值的错误。请看下例:
while ( * str=0) / * programmer meant to write * str! =0 * /
{
/ * some code here * /
strq++;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -