《C-语言常见问题.docx》由会员分享,可在线阅读,更多相关《C-语言常见问题.docx(148页珍藏版)》请在taowenge.com淘文阁网|工程机械CAD图纸|机械工程制图|CAD装配图下载|SolidWorks_CaTia_CAD_UG_PROE_设计图分享下载上搜索。
1、第1章C语言本章主要描述c语言些基本要素。当你开始编写c程序 时,你可能对c语言的些基本问题感到困惑,如c语言所使 用的约定、关键字和术语等。本章将回答这方面你经常会遇到 的些问题。例如,switch语句是最常用的种C语言构件,本章将回答 与它有关的三个常见问题。木章还涉及其它几个问题,如循环、 分支、运算符的优先级和程序块技术。在阅读本章时,请注意 有关switch语句和运算符优先级的些问题,这些问题常常会 使C语言的初学者感到迷惑。1. 1什么是局部程序块(local block)?局部程序块是指对大括号()之间的段C语言程序。 个C函数包含对大括号,这对大括号之间的所有内容都包含 在个局
2、部程序块中。if语句和swich语句也可以包含对大括 号,每对大括号之间的代码也属于个局部程序块。此外,你 完全可以创建你自己的局部程序块,而不使用C函数或基本的 C语句。你可以在局部程序块中说明一些变量,这种变量被称 为局部变量,它们只能在局部程序块的开始部分说明,并且只 在说明它的局部程序块中有效。如果局部变量与局部程序块以 外的变量重名,则前者优先于后者。下面是一个使用局部程序 块的例子:#include void main(void);void main()/ * Begin local block for function main() * /int test_ var = 10;p
3、rintf(Test variable before the if statement: %dn”, test_var);if (test_var5)(/ * Begin local block for if statement * / int test_ var = 5;printf(Test variable within the if statement: %dn, test_var);/ * Begin independent local block (not tied to any function or keyword) * /int test_var = 0;printf(Tes
4、t variable within the independent local block: %dn,test_var)/ * End independent local block * /printf fTest variable after the if statement: %dn test_var);1/*End local block for function main () * /上例产生如下输出结果:Test variable before the if statement: 10Test variable within the if statement: 5Test varia
5、ble within the independent local block:0Test variable after the if statement: 10注意,在这个例子中,每次test_var被定义时,它都要优先 于前面所定义的tesjvar变量。此外还要注意,当if语句的局部 程序块结束时,程序重新进入最初定义的test_var变量的作用范 围,此时test_var的值为!0.请参见:1. 2可以把变量保存在局部程序块中吗?I. 2可以把变量保存在局部程序块中吗?用局部程序块来保存变量是不常见的,你应该尽量避免这样 做,但也有极少数的例外。例如,为了调试程序,你可能要说 明一个全局变
6、量的局部实例,以便在相应的函数体内部进行测 试。为了使程序的某部分变得更易读,你也可能要使用局部 程序块,例如,在接近变量被使用的地方说明个变量有时就 会使程序变得更易读。然而,编写得较好的程序通常不采用这 种方式来说明变量,你应该尽量避免使用局部程序块来保存变 量。请参见:1. I什么是局部程序块?1. 3什么时候用一条switch语句比用多条if语句更好?如果你有两个以上基于同一个数字(numeric)型变量的条件表 达式,那么最好使用一条switch语句。例如,与其使用述代 码:if(x =1)printf (x is equal to one. n);else if (x =2)pri
7、ntf (x is equal to two. nH);else if (x = =3)printf (x is equal to three. n);elseprintf (x is not equal to one, two, or three. n);不如使用下述代码,它更易于阅读和维护:switch (x)breakcase 1: printf (x is equal to one. nH);break;case 2: printf (x is equal to two. nM);breakcase 3: printf (x is equal to three. nH);break;d
8、efault: printf (x is not equal to one, two, or three. nH);break;)注意,使用switch语句的前提是条件表达式必须基于同一个 数字型变量。例如,尽管下述if语句包含两个以上的条件,但 该例不能使用switch语句,因为该例基于字符串比较,而不是 数字比较:char *name=Lupto;if(!stricmp(name ”Isaac)printf(Your name meansLaughter. n);else if(!stricmp(name, Amy)printf( Your name meansBeloved. n);el
9、se if(!stricmp(name, Lloyd)printf(Your name meansMysterious. n);elseprintf(I havent a clue as to what your name means. n);请参见:1. 4 switch语句必须包含default分支吗71. 5 switch语句的最后一个分支可以不要break语句吗?1.4 switch语句必须包含default分支吗?不,但是为了进行错误检査或逻辑检査,还是应该在switch 语句中加入default分支。例如,下述switch语句完全合法: switch (char_code)(cas
10、e tyt:case y: printf ( You answered YES ! n) breakcase N:case n: printf (You answered NO!n);break)但是,如果一个未知字符被传递给这条switch语句,会出现什 么情况呢?这时,程序将没有任何输出。因此,最好还是加入 个default分支,以处理这种情况:default: printf (Unknown response : %dn, char_code); 此外,default分支能给逻辑检查带来很多方便。例如,如果用 switch语句来处理数冃固定的条件,而且认为这些条件之外的 值都属于逻辑错误
11、,那么可以加入个default分支来辨识逻辑 错误。请看下列:void move_cursor (int direction)switch (direction)(case UP: cursor_up()breakcase DOWN: cursor_down()breakcase LEFT: cursor_left ()breakcase RIGHT: cursor_ right () breakdefault:printf (Logic error on line number %ld! n,_ LINE_)break)请参见:1. 3什么时候用一条switch语句比用多条if语句更好?1.
12、 5 Switch语句的最后个分支可以不要break语句吗?1. 5 switch语句的最后一个分支可以不要break语句吗?尽管switch语句的最后一个分支不一定需要break语句,但 最好还是在switch语句的每个分支后面加上break语句,包括 最后个分支。这样做的主要原因是:你的程序很可能要让另个人来维护,他可能要增加一些新的分支,但没有注意到最 后个分支没有break语句,结果使原来的最后个分支受到其 后新増分支的干扰而失效。在毎个分支后面加上break语句将防 止发生这种错误并增强程序的安全性。此外,目前大多数优化 编译程序都会忽略最后一条break语句,所以加入这条语句不会
13、影响程序的性能。请参见:1. 3什么时候用一条switch语句比用多条if语句更好?1. 4 switch语句必须包含default分支吗?1.6除了在for语句中之外,在哪些情况还要使用逗号运算符? 逗号运算符通常用来分隔变量说明、函数参数、表达式以及 for语句中的元素。下例给出了使用逗号的多种方式:#includc #include void main(void);void main ()(/ * Here, the comma operator is used to separate three variable declarations. * /int i, j, k;/ * Not
14、ice how you can use the comma operator to perform multiple initializations on the same line. * /i=0,j=l,k=2;printf(i= %d, j=d, k= %dn”, i, j, k);/ * Here, the comma operator is used to execute three expressionsin one line: assign k to i, increment j, and increment k.The value that i receives is alwa
15、ys the rightmostexpression. * /i=(j+, k+);printf(i=%d,j=%d, k=%dn, 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 (HnGuess what? i is 50!nH)请注意述语句:i: (j+ k+)这条语句次完
16、成了三个动作,依次为:(1)把k值赋给io这是因为左值(Ivaule)总是等于最右边的参 数,本例的左值等于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. nH);这里,逗号运算符将两个表达式隔开,while语句的每次循环 都将计算这两个表达式的值。逗号左
17、边是第 个表达式,它把 至99之间的一个随机数赋给i!第二个表达式在while语句中更 常见,它是个条件表达式,用来判断i是否不等于50。while 语句每一次循环都要赋予i 个新的随机数,并且检査其值是否 不等于50。最后,i将被随机地赋值为50,而while语句也将结 束循环。请参见:1. 12运算符的优先级总能保证是“自左至右或“自右至左 的顺序吗?1. 13+var和var+有什么区别?1. 7怎样才能知道循环是否提前结束了?循环通常依赖于个或多个变量,你可以在循环外检查这些 变量,以确保循环被正确执行。请看下例:int xchar * cpREQUESTED_BLOCKS/ * At
18、tempt (in vain, I must add. )toallocate 512 10KB blocks in memory. * /for (x = 0; xREQUESTED_ BLOCKS ; x+ )(cpix= (char* ) ma Hoc (10000,1)if(cpx= = (char *) NULL) break)/* Ifx is less than REQUESTED-BLOCKS,the loop has ended prematurely. * /if (xREQUESTED_BLOCKS)printf (Bummer ! My loop ended prema
19、turely ! nH);注意,如果上述循环执行成功,它一定会循环512次。紧接着 循环的if语句用来测试循环次数,从而判断循环是否提前结束。 如果变量x的值小于512,就说明循环出错了。1. 8 goto, longjmp()和 setjmpO之间有什么区别?goto 语句实现程序执行中的近程跳转(localjump),longjmp() 和setjmpO函数实现程序执行中的远程跳转(nonlocaljump,也叫 faijump)。通常你应该避免任何形式的执行中跳转,因为在程序 中使用goto语句或longjmp。函数不是种好的编程习惯。goto语句会跳过程序中的一段代码并转到个预先指定的
20、位 置。为了使用goto语句,你要预先指定个有标号的位置作为 跳转位置,这个位置必须与goto语句在同一个函数内。在不同 的函数之间是无法实现goto跳转的。下面是个使用goto语句 的例子:void bad_programmers_function(void)(int xprintf(Excuse me while I count to 5000. n);while (1)printf( %dn, x)goto all_doneif(x =5000)elsex=x+l;)all_done:prinft(Whew! That wasnt so bad. was it?n);如果不使用got。语
21、句,是例可以编写得更好、下面就是个改 进了实现的例子:void better_function (void)int xprintf(Excuse me while I count to 5000. n);for (x=l; x=5000, x+)printf(M %dn”, x)printf(Whew! That wasnt so bad, was itAn);)前面已经提到,longjmpOft setjmpO函数实现程序执行中的远 程跳转。当你在程序中调用s的mp()时,程序当前状态将被保存 到个jmp_buf类型的结构中。此后,你可以通过调用longjmpO 函数恢复到调用setjmp(
22、)时的程序状态。与goto语句不同, longjmpO和s的mp()函数实现的跳转不一定在同一个函数内。然 而,使用这两个函数有一个很大的缺陷,当程序恢复到它原来 所保存的状态时,它将失去对所有在longjmpO和setjmp()之间 动态分配的内存的控制,也就是说这将浪费所有在longjmpO和 setjmpO之间用malloc()和calloc。分配所得的内存,从而使程序 的效率大大降低。因此,你应该尽量避免使用longjmpO和setjmpO 函数,它们和goto语句样,都是不良编程习惯的表现。面是使用longjmpO函数和setjmpO函数的一个例子:#include #include
23、 jmp_buf savcd_state;void main(void);void call_ longjmp (void);void main(void)(int ret_code;printf(The current state of the program is being saved. n);ret_code = setjmp (saved_state)if (ret_code =1)printffThe longjmp function has been called. n)printffThe programs previous state has been restored. n
24、);exit(O)prints! am about to call longjmp andnM);printf(retum to the previous program state. n) call_ longjmp ()void call_longjmp (void)longjmp (saved_state, 1 )(1. 9什么是左值(Ivaule)?左值是指可以被赋值的表达式。左值位于赋值语句的左侧, 与其相对的右值(rvaule,见1. 11)则位于赋值语句的右侧。每 条赋值语句都必须有一个左值和一个右值。左值必须是内存中 一个可存储的变最,而不能是个常量。下面给出了一些左值 的例子
25、:int x;int *p_int;x=l;p_int=5;变量x是个整数,它对应于内存中的一个可存储位置,因此, 在语句“x=中,x就是个左值。注意,在第二个赋值语句 “*p_int=5”中,通过“*”修饰符访问p_int所指向的内存区域: 因此,p_int是个左值。相反,下面的儿个例子就不是左值: #define CONST. VAL 10int x/* example 1 * / l=x;/ * example 2 * /CONST.VAL = 5;在上述两条语句中,语句的左侧都是个常量,其值不能改变, 因为常量不表示内存中可存储的位置。因此,这两条赋值语句中没有左值,编译程序会 指出它
26、们是错误的。请参见:1. 10数组(array)可以是左值吗?.1. 1I什么是右值(rvaule)?I. 10数组(array)可以是左值吗?在1. 9中,左值被定义为可被赋值的表达式。那么,数组是 可被赋值的表达式吗?不是,因为数组是由若干独立的数组元素 组成的,这些元素不能作为个整体被赋值。下述语句是、法的:int x5, y5;x=y:不过,你可以通过for循环来遍历数组中的每个元素,并分 别对它们赋值,例如:int i;int x5;inty5;for(i=0; i y;x = 1; /* 1 iS an rvalue, x is an lvalue */在1.9中已经介绍过,条赋值
27、语句必须有 个左值和,个 右值,因此,下述语句无法通过编译,因为它缺少个右值:int x;x=void_function_call(); /* the unction void-function-call() returns nothing */如果上例中的函数返回一个整数,那么它可以被看作一个右 值,因为它的返冋值可以存储到左值X中。请参见:1. 9什么是左值。vaule)?1. 10数组可以是左值吗?I. 12运算符的优先级总能保证是“自左至右”或“自右至左”的顺 序吗?对这个问题的简电回答是:这两种顺序都无法保证。C语言 并不总是自左至右或自右至左求值,一般说来,它首先求函数 值,其次求
28、复杂表达式的值,最后求简单表达式的值。此外, 为了进步优化代码,目前流行的大多数C编译程序常常会改 变表达式的求值顺序因此,你应该用括号明确地指定运算符 的优先级。例如,请看下述表达式:a=b+c/d/functioncall() * 5I:述表达式的求值顺序非常模糊,你很可能得不到所要的结 果,因此,你最好明确地指定运算符的优先级:a=b+(c/d)/functioncall()* 5)这样,就能确保表达式被正确求值,而且编译程序不会为了 优化代码而重新安排运算符的优先级了。1. 13 +var和var+有什么区别?“+”运算符被称为白增运算符。如果“+”运算符出现在变量 的前面(+var)
29、,那么在表达式使用变量之前,变量的值将增加 1。如果“+”运算符出现在变量之后(var+),那么先对表达式求 值,然后变量的值增加1。对自减运算符(-)来说,情况完全 相同。如果运算符出现在变量的前面,则相应的运算被称为前 缀运算:反之,则称为后缀运算。例如,请看个使用后缀自增运算符的例子:int x, y;x=l;y=(x+* 5);上例使用了后缀自增运算符,在求得表达式的值之后,x的 值增加1,因此,y的值为1乘以5,等于5。在求得表达式 的值之后,x自増为2。现在看个使用前缀自增运算符的例子:int X, y;K=l;y=(x+l); /* (x+l)is an rvalue; y is
30、 an lvalue */y=(+x*5);这个例子和前一个相同,只不过使用了前缀自增运算符,而 不是后缀自增运算符,因此,X的值先增加1,变为2,然后才 求得表达式的值。这样,y的值为2乘以5,等于10.1. 14取模运算符(modulus operato”的作用是什么?取模运算符“”的作用是求两个数相除的余数.例如,请看 下面这段代码:x=15/7;如果x是个整数,x的值将为2.然而,如果用取模运算符 代替除法运算符/,得到的结果就不同了:X=15%7;这个表达式的结果为15除以7的余数,等于1.这就是说, 15除以7得2余1.取模运算符通常用来判断个数是否被另一个数整除.例如, 如果你要
31、打印字母表中序号为3的倍数的字母,你可以使用下 面这段代码:int x;for(x = l; x=26; x+)if(x%3)=0)printf(%cH; x+64);上例将输出字符串cfilorux,即字母表中序号为3的倍数的所 有字母。第2章变量和数据存储C语言的强大功能之一是可以灵活地定义数据的存储方式. C语言从两个方面控制变量的性质:作用域(scope)和生存期 (lifetime).作用域是指可以存取变量的代码范围,生存期是指可 以存取变量的时间范围.作用域有三种:1. extern(外部的)这是在函数外部定义的变量的缺省存储方 式.extern变量的作用域是整个程序.2. sta
32、tic(静态的)在函数外部说明为static的变量的作用域 为从定义点到该文件尾部:在函数内部说明为static的变量的作 用域为从定义点到该局部程序块尾部.3. auto(自动的)这是在函数内部说明的变量的缺省存储方 式.auto变量的作用域为从定义点到该局部程序块尾部.变量的生存期也有三种,但它们不象作用域那样有预定义的 关键字名称.第一种是extern和static变量的生存期,它从main() 函数被调用之前开始,到程序退出时为止.第二种是函数参数 和auto变策的生存期,它从函数调用时开始,到函数返回时为 止.第三种是动态分配的数据的生存期,它从程序调用malloc() 或callo
33、c()为数据分配存储空间时开始,到程序调用68()或程序 退出时为止.2. 1变量存储在内存(memory)中的什么地方?变量可以存储在内存中的不同地方,这依赖了它们的生存期。 在函数外部定义的变量(全局变量或静态外部变量)和在函数内 部定义的static变量,其生存期就是程序运行的全过程,这些变 量被存储在数据段(datasegment)中。数据段是在内存中为这些变 量留出的一段大小固定的空间,它分为两部分,一部分用来存 放初始化变量,另部分用来存放未初始化变量.在函数内部定义的auto变量(没有用关键字static定义的变量) 的生存期从程序开始执行其所在的程序块代码时开始,到程序 离开该
34、程序块时为止。作为函数参数的变量只在调用该函数期 间存在.这些变量被存储在栈(stack)中.栈是内存中的一段空间, 开始很小,以后逐渐自动增大,直到达到某个预定义的界限。 在象DOS这样的没有虚拟内存(virtual memory)的系统中,这个 界限由系统决定,并且通常非常大,因此程序员不必担心用尽 栈空间。关于虚拟内存的讨论,请参见2. 3.第三种(也是最后种)内存空间实际上并不存储变量,但是 可以用来存储变量所指向的数据。如果把调用malloc。函数的结 果赋给个指针变量,那么这个指针变量将包含块动态分配 的内存的地址,这块内存位于段名为“堆(heap)”的内存空间 中。堆开始时也很小
35、,但当程序员调用malk)c()或calloc()等内 存分配函数时它就会增大.堆可以和数据段或栈共用个内存 段(memorysegment),也J以有它自己的内存段,这完全取决于 编译选项和操作系统。与栈相似,堆也有一个增长界限,并且决定这个界限的规则与 栈相同.请参见:1. 1什么是局部程序块(lOcalblock)?2. 2变量必须初始化吗?2. 3 什么是页抖动(pagethrashing)?7. 20什么是栈(stack)?7. 21什么是堆(heap)7 .2. 2变量必须初始化吗?不.使用变量之前应该给变量一个值,个好的编译程序将 帮助你发现那些还没有被给定一个值就被使用的变量。
36、不过, 变量不定需要初始化.在函数外部定义的变量或者在函数内 部用static关键字定义的变量(被定义在数据段中的那些变量, 见2. 1)在没有明确地被程序初始化之前都已被系统初始化为 了.在函数内部或程序块内部定义的不带static关键的变量都 是自动变量,如果你没有明确地初始化这些变量,它们就会具 有未定义值。如果你没有初始化一个自动变量,在使用它之前 你就必须保证先给它赋值.调用malloc。函数从堆中分配到的空间也包含未定义的数据, 因此在使用它之前必须先进行初始化,但调用calloc()函数分配 到的空间在分配时就已经被初始化为。 了。请参见:1. 1什么是局部程序块(lOcalbl
37、ock)?7. 20什么是栈(stack)?7. 21什么是堆(heap)?2. 3 什么是页抖动(pagethrashing)?有些操作系统(如UNIX和増强模式的Windows)使用虚拟 内存,这是种使机器的作业地址空间大于实际内存的技术, 它是通过用磁盘空间模拟RAM(randomaccess memory)来实现 的。在80386和更高级的Intel CPU芯片中,在现有的大多数其它 微处理器(如Motorola 68030, spare和Power PC)中,都有一个 被称为内存管理单元(Memory Management Unit,缩写为MMU) 的器件。MMU把内存看作是由系歹页
38、(page)组成的来处理。页内存是指个具有定大小的连续的内存块,通常为4096 或8192字节。操作系统为每个正在运行的程序建立并维护张 被称为进程内存映射(Process Memory Map,缩与为PMM)的表, 表中记录了程序可以存取的所有内存页以及它们的实际位置。每当程序存取块内存时,它会把相应的地址(虚拟地址, virtualaddress)传送给MMU, MMU会在PMM中查找这块内存 的实际位置(物理地址,physical address),物理地址可以是由操 作系统指定的在内存中或磁盘I:的任何位置。如果程序要存取 的位置在磁盘1就必须把包含该地址的页从磁盘上读到内存 中,并且
39、必须更新PMM以反映这个变化(这被称为pagefault, 即页错)。希望你继续读去,因为面就要介绍其中的难点了。存取 磁盘比存取RAM要慢得多,所以操作系统会试图在RAM中保 持尽量多的虚拟内存。如果你在运行个非常大的程序(或者同 时运行几个小程序),那么可能没有足够的RAM来承担程序要 使用的全部内存,因此必须把些页从RAM中移到磁盘1:(这 被为pagingout,即页出)。操作系统会试图去判断哪些页可能暂时不会被使用(通常基 于过去使用内存的情况),如果它判断错了,或者程序正在很多 地方存取很多内存,那么为了读入已调出的页,就会产生大量 页错动作。因为RAM已被全部使用,所以为了调入要
40、存取的 页,必须调出另页,而这将导致更多的页错动作,因为此时 不同的页已被移到磁盘上。在短时间内出现大量页错动作的 情形被称为页抖动,它将大大降低系统的执行效率。频繁存取内存中大量散布的位置的程序更容易在系统中造成 页抖动。如果同时运行许多小程序,而实际上已经不再使用这 些程序,也很容易造成页抖动。为了减少页抖动,你应该减少 同时运行的程序的数冃。对了大的程序,你应该改变它的工作 方式,以尽量使操作系统能准确地判断出哪些页不再需要。为 此,你可以使用髙速缓冲存储技术,或者改变用于大型数据结 构的查找算法,或者使用效率更髙的malloc。函数。当然,你也 可以考虑增加系统的RAM,以减少页出动作
41、。请参见:7. 17怎样说明个大于640KB的数组?7. 21什么是堆(heap)?18. 14怎样才能使DOS程序获得超过64KB的可用内存?21. 31 Windows是怎样组织内存的?2. 4什么是const指针?如果希望个变量在被初始化后其值不会被修改,程序员就 会通过cons,修饰符和编译程序达成默契。编译程序会努力去保 证这种默契它将禁止程序中出现对说明为const的变量:进 行修改的代码。const指针的准确提法应该是指向const数据的指针,即它所 指向的数据不能被修改。只要在指针说明的开头加入const修饰 符,就可说明一个cosnt指针。尽管const指针所指向的数据不 能
42、被修改,但cosnt指针本身是可以修改的。下而给出了 const 指针的些合法和非法的用法例子:const char *str=hello;char c=*str; /*legal*/str+;/* legal*/*str=*a*;/* illegal */strl = b; /illegal*/前两条语句是合法的,因为它们没有修改str所指向的数据: 后两条语句是非法的,因为它们要修改str所指向的数据。在说明函数参数时,常常要使用const指针。例如,一个计 算字符串长度的函数不必改变字符串内容,它可以写成这样:my_strlen(const char *str)int count=0;w
43、hile ( * str+)(count +;)return count;)注意,如果有必要,个非const指针可以被隐式地转换为 consl指针,但个const指针不能被转换成非const指针。这就 是说,在调用my_strlen()时,它的参数既可以是个const指针, 也可以是个非const指针。请参见:2. 7 个变量可以同时被说明为const和volatile吗?2. 8什么时候应该使用const修饰符?2. 14什么时候不应该使用类型强制转换(typecas?2. 18用const说明常量有什么好处?2. 5什么时候应该使用register修饰符沱真的有用吗?代gister修饰符暗
44、示编译程序相应的变量将被频繁使用,如果 可能的话,应将其保存在CPU的寄存器中,以加快其存取速度。 但是,使用register修饰符有几点限制。首先,register变量必须是能被CPU寄存器所接受的类型。 这通常意味着register变量必须是个单个的值,并且其长度应 小于或等于整型的长度。但是,有些机器的寄存器也能存放浮 点数。其次,因为register变量可能不存放在内存中,所以不能用取 址运算符“&”来获取register变量的地址。如果你试图这样做, 编译程序就会报告这是个错误。register修饰符的用处有多大还受其它些规则的影响。因为 寄存器的数量是有限的,而且某些寄存器只能接受特定类型的 数据(如指针和浮点数),因此,.真正能起作用的register修饰符 的数目和类型都依赖于运行程序的机器,而任何多余的register 修饰符都将被编译程序所忽略。在某些情况,把变量保存在寄存器中反而会降低运行速度, 因为被占用的寄存器不能再用于其它目的,或一者变量被使用 的次数