C陷阱与缺陷.doc

上传人:ng****60 文档编号:2245047 上传时间:2019-05-02 格式:DOC 页数:7 大小:38.50KB
下载 相关 举报
C陷阱与缺陷.doc_第1页
第1页 / 共7页
C陷阱与缺陷.doc_第2页
第2页 / 共7页
C陷阱与缺陷.doc_第3页
第3页 / 共7页
C陷阱与缺陷.doc_第4页
第4页 / 共7页
C陷阱与缺陷.doc_第5页
第5页 / 共7页
点击查看更多>>
资源描述

1、1.1 由于程序员在比较字符 和变量 c 时,误将 = 写成 = ,那么 while 后的表达式恒为 1,因为 不等于零,它的 ASCII 码值为32。为了避免这种情况,我们可以这样写 while(=c | t= c | n=c) ,这样即使不小心将 = 写成 = ,编译器也会报错。 在 C 中,单引号括起来表示一个整数,双引号括起来表示指针。例如,字符和字符串。1.3 词法分析中的“贪心法”C 编译器读入一个符号的规则:每一个符号应该包含尽可能多的字符,方向为自左向右。.大嘴法 编译器在解析字符的时候会尽可能的解析成可能组成一个符号的最常的字符串。 例如,a-b (a-) -b而 y = x

2、/*p; 中 /* 被编译器理解为一段注释的开始,如果本意是用 x 除以 p 所指向的值,应该重写如下y = x/ *p; 或更加清楚一点,写作 y = x/(*p); 在用双引号括起来的字符串中,注释符 /* 属于字符串的一部分,而在注释中出现的双引号“属于注释的一部分。整数常量的第一个字符是数字0,那么该常量将被视为八进制数。 单引号所括起的字符代表一个整数,双引号括起的则代表一个指针(字符数组,自动加0) 1.理解函数声明 通过认识这个让人“不寒而栗”的式子: (*(void(*)()0)() 1)把 0 强制转换成了类型 void(*)(),这是一个指向函数的指针,所指向的这个函数返回

3、值为 void ps:a)float *g():这是一个函数,返回指针 float*,因为()的优先级比*高。 b)如果 fp 是一个函数指针,怎么调用该指针所指向的函数呢? (*fp ) () ; c)为了使表述更加清晰: typedef void (*funptr)(); 则那个不寒而栗的式子可以表达为: (*(funptr)0)() 2)由 1)-b 我们得知,这个式子的意思就是调用了上面所说的函数指针所指向的函数。 2.运算符优先级 1)任何一个逻辑运算符的优先级低于任何一个关系运算符 2)移位运算符的优先级低于算术运算符,但是高于关系运算符 算术移位关系 逻辑 3.不要忘了 swit

4、ch 中的 break,否则会把满足的 case 后面的所有 case 都执行。 小心指针和除法的一起使用:y = x/*p;这里的/*理解为注释!解决方法是:(1) y = x / *p(2) y = x /(*p)以第二个更为清晰.6./*看上去是注释的开始如:a=/*b; 老版本的编译器会当作是 :a =/ *b7.char *slash = /; /* 编译错误,/并不是字符指针 */同理,而且有些编译器不检查函数参数类型,所以: printf(n);在程序运行时会产生难以预料的结果,而不给出编译器警告或者错误.8.整型数(一般为 16 或者 32 位)的存储空间可以容纳多个字符(一般

5、为 8 位), 因此有些 C 编译器允许在一个字符常量(以及字符串常量)中包含多个字符.也就是说,用yes代替“yes“ 并不被该编译器检测到.后者的含义是依次包含y,e,s以及空字符0 的 4 个连续内存单元的首地址.前者(即是yes)的含义并没有准确地进行定义:大多数 C 编译器理解为:“ 一个整数值,由y,e,s所代表的整数值按照特定编译器实现中定义的方式组合得到“.(1)在 Borland C+ v5.5 和 LCC v3.6 中采取的做法是,忽略多余的字符,最后的整数值就是第一个字符的整数值;(2)在 Visual C+ 6.0 和 GCC v2.95 中采取的做法是,依次用后一个字

6、符覆盖前一个字符,最后得到的整数值就是最后一个字符的整数值.二、语法陷阱2.1 理解函数声明来看一个表达式, (*(void(*)()0)(); 你能看出来这个表达式的含义吗? 看不出来不要紧,慢慢来.首先,回顾一下 C 变量的声明,它由两部分组成:类型以及一组类似表达式的声明符,最简单的声明符就是单个变量。如,float f,g; 这个声明的含义:当对其求值,表达式 f 和 g 的类型为浮点型。因为声明符与表达式类似,我们可以在声明符中任意使用括号: float (f); 含义:当对其求值,(f)的类型为浮点型,由此可知,f 也是浮点型。我们将这个逻辑推广到函数和指针的声明,如:float

7、ff(); float *pt;这些形式组合使用,如float *g(),(*h)();由于()的结合优先级高于 * ,所以 g 是一个函数,它的返回类型为指向浮点数的指针;h 是一个函数指针,h 所指向函数的返回值为浮点类型。 由声明到类型转换符:只需要把声明中的变量名和声明末尾的分号去掉,再将剩余部分用一个括号整个“封装”。如,声明 float (*h)(); 为一个指向返回值为 float 的函数的指针 而 (float (*)() 表示一个“指向返回值为 float 的函数的指针”的类型转换符。 函数指针的调用: (*fp)();如果 fp 的声明如下:void (*fp)();fp

8、是一个指向返回值为 void 类型的函数的指针,那么(*fp)()的值为 void。 、因此 (void (*)()0 表示将常数 0 转型为“ 指向返回值为 void 的函数的指针”类型。请认真理解这一点。我们可以用 (void (*)()0 代替 fp ,从而得到如下调用(*(void(*)()0)();2.3 注意作为语句结束标志的分号来看一个代码段if (ab);a=0;else b=0;注意 if 表达式后的分号,上面的代码相当于if (ab)a=0;else b=0;由于没有 if 与 else 匹配,编译器将产生警告。再来看一个例子,struct logrecint date;i

9、nt time;main().注意到第一个与 main 定义之间是没有分号的。因此这段代码的效果是声明函数 main的返回值是结构 logrec 类型。三、语义陷阱3.1 指针与数组C 中的数组值得注意的两点:一是 C 中只有一维数组,而且数组的大小必须在编译期就作为一个常数确定下来。然而,数组的元素可以使任何类型的对象,当然也可以是另外一个数组,这样便可以“仿真”多维数组。二是对于一个数组,我们只能够做两件事:确定该数组大小,一级获得指向该数组下标为0的元素的指针(下标运算其实都是通过指针进行的) 。来看一个例子,若定义int s1231;int *p;int i;则 p = s4;是无误的

10、而 p = s; 则是不行的。因为 s 会被转换为指向二维数组的指针,与 p 类型不匹配。然而,像下面这样做是可以的int (*ap)31;int s1231;那么 ap = s; 是可以通过的。 假如定义了 int a22; 则 *(a+i) 即数组 a 中下标为 i 的元素的引用,简记为 ai。实际上, a+i 与 i+a 的含义一样,因此 ai 与 ia 也具有同样的含义。3.2 非数组的指针假如我们有两个这样的字符串 s 和 t,并且希望将他们连成单个字符串 r。如char *r;strcpy(r,s);strcat(r,t);不幸的是,这样做事不行的。因为不能确定 r 指向何处。不仅

11、要让 r 指向一个地址,而且r 所指向的地址还应该有内存空间可供容纳字符串。于是我们可以这样做char *r;r = malloc(strlen(s) + strlen(t) + 1);现在可以 strcpy(r,s); strcat(r,t); 了,不过别忘了最后 free(r); 这里注意到给 r 分配内存的时候 +1 了,这是因为 strlen() 返回的值不包括 0,所以要多申请存放 0 的空间!3.6 边界计算与不对称边界先来看一个例子,我们常常对类似的代码这样处理int i,a10;for (i=0;i=0 且 5) complain(); 这里利用 i=1;while(i(xi+

12、)?(biggest):(xi+);其中这部分 xi+ 有可能被执行两次,与我们的期望不符。解决办法是确保宏 max 中的参数没有副作用,像这样:biggest = x0;for(i = 1;i0 elseassert(yx);展开后得到,if(x0 elseif(!(yx) assert_error(“foo.c“,39);注意到,else 并不是与第一个 if 匹配,这与我们的期望不符。解决办法是,将宏 assert 定义为一个表达式而不是一个语句:#define assert(e) (void)(e)|_assert_error(_FIL_,_LINE_)上述定义利用了对 | 两侧操作数依次顺序求值的性质。6.4 宏不是类型定义考虑下面的代码#define T1 struct foo *typedef struct foo *T2;从两个定义来看,T1和 T2从概念上完全符同,都是指向结构 foo 的指针。但是T1 a,b;T2 a,b;对于第一个声明展开后,struct foo *a,b; ,a 为指针,b 不是指针,与期望不符。7.6 内存位置0null 指针并不指向任何对象。除了用于赋值或比较运算,出于其他任何目的的使用null 指针都是非法的。例如,p 和 q 都是 null 指针,那么 strcmp(p,q)的值就是未定义的。

展开阅读全文
相关资源
相关搜索

当前位置:首页 > 教育教学资料库 > 精品笔记

Copyright © 2018-2021 Wenke99.com All rights reserved

工信部备案号浙ICP备20026746号-2  

公安局备案号:浙公网安备33038302330469号

本站为C2C交文档易平台,即用户上传的文档直接卖给下载用户,本站只是网络服务中间平台,所有原创文档下载所得归上传人所有,若您发现上传作品侵犯了您的权利,请立刻联系网站客服并提供证据,平台将在3个工作日内予以改正。