《C陷阱与缺陷》学习笔记.docx

上传人:b****5 文档编号:2843533 上传时间:2022-11-15 格式:DOCX 页数:13 大小:28.13KB
下载 相关 举报
《C陷阱与缺陷》学习笔记.docx_第1页
第1页 / 共13页
《C陷阱与缺陷》学习笔记.docx_第2页
第2页 / 共13页
《C陷阱与缺陷》学习笔记.docx_第3页
第3页 / 共13页
《C陷阱与缺陷》学习笔记.docx_第4页
第4页 / 共13页
《C陷阱与缺陷》学习笔记.docx_第5页
第5页 / 共13页
点击查看更多>>
下载资源
资源描述

《C陷阱与缺陷》学习笔记.docx

《《C陷阱与缺陷》学习笔记.docx》由会员分享,可在线阅读,更多相关《《C陷阱与缺陷》学习笔记.docx(13页珍藏版)》请在冰豆网上搜索。

《C陷阱与缺陷》学习笔记.docx

《C陷阱与缺陷》学习笔记

《C陷阱与缺陷(CTrapsandPitfalls)》学习笔记

(1)

书作者结合自己实际的编程经验,讲述程序员写C程序中会遇到的问题和麻烦(“陷阱”-程序没有按照程序员所期待的方法执行),教你如何避免遇到这些问题。

 

/*本文仅仅是读书笔记^_^总结理解书中的观点*作者:

nick@hitron*email:

lichuxin9801@*/

 第1章词法“陷阱” 

1.1=不同于==

首先,==为比较运算符而=为赋值运算符,这二个符号再实际使用中,容易写反,而造成的错误不易发现。

   比如:

   while(c=''||c=='\t'||c=='\n')

  c=getc(f);   在c和''比较的时候,写错了。

这样的后果是将''||c=='\t'||c=='\n'这个表达式的值给了c,而使c=1;   同样:

   if((filedesc==open(argv[i],0))<0)

  error();   open的返回值和filedesc比较的结果只能是0或1,所以,error没有机会调用。

但是,此时filedesc的值于open返回值无关,编译器这里不会报错。

容易被忽视,达不到检查效果。

1.2&和|不同于&&和||

  &和|均为按位运算符,而&&和||均为逻辑运算符,不能混淆。

1.3语法分析中的“贪心法”

C语言的某些符号,比如/*、=等,只有一个字符长,称为单字符符号,而/*==这些以及标识符,有多个字符,成为多字符符号。

C编译器读入字符的规则:

每个符号应该包含尽可能多的字符,即:

从左到右,一个个读入,如果能组成一个符号,则继续读入下一个,这样子下去,直到不能组成一个有意义的符号;这个处理策略有时候叫“贪心法”;   除了字符串和字符常量,符号中间不能有空白符(比如空格、制表、换行符);   如:

   a---b与a---b相同,而与a---b不同。

   这些准二义性的问题,要注意避免(如添加()等方法)。

1.4整型常量

如果一个整形常量的第一个字符是数字0,那么该常量将被视作八进制数。

因此,10和010是完全不同的含义。

此外书中还介绍了一些ANSIC不允许的做法,比如将8和9也作为八进制数字处理。

1.5字符和字符串

C语言中单引号和双引号的含义不同,使用错误时,有的编译器并不会检测报错,但是运行产生结果难以预料。

   单引号引起的一个字符实际上代表一个整数,数值对应于该字符在编译器上采用的字符集中的序列。

对于常用的采用ASCII字符集的编译器而言,'a'的含义与97(十进制)或0x61(十六进制)严格一致;   双引号引起字符串,代表一个指向无名数组的起始字符的指针,该数组被双引号之间的字符以及一个额外的二进制值为0的字符'\0'初始化;比如:

printf("Hello\n");   

charhello[]={'H','e','l','l','o','\n',0};   printf(hello);

等效。

《C陷阱与缺陷(CTrapsandPitfalls)》学习笔记

(2) 

/************************************本文仅仅是读书笔记^_^总结理解书中的观点*作者:

nick@hitron*email:

lichuxin9801@************************************/

 第二章:

语法“陷阱”

2.1理解函数声明

问题:

计算机启动时,硬件调用首地址为0位置的子程序,为了模拟开机启动时的情形,设计个C语句,显式的调用该子程序,语句是:

(*(void(*)())0)();

这类表达式有一个构造规则:

按照使用方式声明。

   C变量的声明分两部分:

类型以及一组类似表达式的声明符。

对声明符求值应该返回一个声明中给定类型的结果:

   比如:

   floatfun();

/*表达式fun()求值的结果是一个浮点数,fun()是一个返回值为浮点类型的函数。

*/

   float*pt; 

/**pt()是一个浮点数,pf是一个指向浮点数的指针*/

组合起来也是可以的:

   float*fun(),(*h)();

/**g()和(*h)()是浮点表达式,*g()就是*(g()):

g是一个返回值为指向浮点数的指针的函数,h是一个函数指针,指向的函数返回值为浮点类型*/

   结合上面的例子,(float (*)())表示一个"指向返回值为浮点类型的函数的指针"的类型转换符。

   下面分析表达式(*(void(*)())0)();的含义:

   

(1)fp是一个函数指针,如何调用fp指向的函数呢:

(*fp)();   

(2)将常数0转型为“指向返回值为void的函数的指针”类型,这样子写:

(void(*)())0;替换上面的分析结果,得到(*(void(*)())0)();   使用typedef能更清晰的表达:

   typedefvoid(*funcptr)();   (*(funcptr)0)();   书中还有一个例子signal函数来显示这种用法:

   void(*signal(int,void(*)(int)))(int);   用typedef简化:

   typedefvoid(*HANDLER)(int);   HANDLERsignal(int,HANDLER);

2.2运算符的优先级问题

具体的优先级列表查看书;   优先级最高的不是真正意义上的运算符,比如:

数组下标([]),函数调用操作符(()),各结构体成员选择符(.->),它们都是从左到右结合。

比如:

a.b.c,则是(a.b).c;   其次是单目运算符也叫一元运算符,在有意义的运算符中,他们的优先级最高,他们是从右向左结合的;   比如:

   

(1)*p()解释为*(p())

/*函数调用操作符优先级高于取值符**/

;   

(2)*p++解释为*(p++),即取p指向的对象,然后将p递增1,而不是p指向的对象递增1((*p)++);   接着是双目运算符也叫二元运算符,其中,算术运算符优先级最高,再依次移位,关系,逻辑,赋值,条件运算符(三目)。

重要的是:

(1)逻辑运算符优先级低于任何关系运算符;

(2)移位运算符优先级比算术运算符低,但是比关系运算符高。

   *、/、%优先级相同且高于+、-,位移<<、>>优先级一致,但是低于前2者;   比如:

1

/

2*a解释为(1

/

2)*a,因为*、

/

、%都是从左到右的;

  对于关系运算符,==和!

=优先级低于其他,比如:

a

&,^,|这样的顺序;   运算符举例:

   

(1)tax_rate=income>40000&&residency<5?

3.5:

2.0;分析结果为:

   tax_rate=((income>40000)&&(residency<5)3.5:

2.0);   

(2)while(c=getc(int)!

=EOF)putc(c,out);分析结果为:

c=(getc(int)!

=EOF);   (3)if((t=BTYPE(pt1->aty)==STRTY)||t==UNIONTY)分析结果为:

(t=(BTYPE(pt1->aty)==STRTY)))||t==UNIONTY;

2.3作为语句结束标志的分号

举例说明:

   

(1)if(x[i]>big)big=x[i];   

/*如果加错了;放在if()后面,则必定执行big=x[i],和判断没关系;*/

   

(2)if(n<3)return;

  logrec.data=x[0];

  logrec.time=x[2];

   logrec.code=x[3];

/*如果吧return后面的;忘了,返回的是return=logrec.data=x[0],这样子的bug很难找。

*/

   (3)structlogrec{intdata;inttime;intcode;

  };

  main()

  {……

  }

/*如果遗漏了struct声明后面的;,上面的实际效果为声明main的返回值为structlogrec的类型*/

 

2.4switch语句

switch最容易忘记的是case后面有时候是需要break的,当然有时候不用break可以达到很特殊的效果。

   比如:

写一段代码,作用是一个编译器再查找符号的时候跳过程序中的空白字符,包括空格,制表,换行符号。

到遇到换行符号的时候,计数器递增,代码如下:

   case'\n':

  linecount++;   case'\t':

   case'':

  ……

  2.6函数的调用

函数调用的时候即使不带参数,也必须包括参数的列表,比如:

   f();

/*调用函数*/

   f;

/*计算函数的地址,啥也不干*/

 

2.7“悬挂”else引发的问题

if和else匹配的问题容易出错:

   比如:

   if(x==0)

  if(y==0)error();   else{

  z=x+y;

  f(&z);   }   程序本来意图是第一个if和else搭配的,这样子写是和else最近的一个if和它搭配了。

因此,最好要将if里面的内容用括号封装起来。

《C陷阱与缺陷(CTrapsandPitfalls)》学习笔记(3) 

/************************************本文仅仅是读书笔记^_^总结理解书中的观点*作者:

nick@hitron*email:

lichuxin9801@************************************/

 第三章:

语义“陷阱”

3.1指针和数组

C语言中数组值得注意一下2点:

   

(1)C语言中只有一维数组,而且数组的大小必须再编译期就作为一个常数确定下来,数组元素可以是任何类型对象,也可是另一个数组。

(C99允许遍长数组VLA,还没看过^_^)   

(2)对于一个数组,只能有2种行为:

确定该数组的大小,以及获得指向该数组下标为0的元素的指针。

其他操作都是基于指针的,任何一个数组下标运算都等同于一个对应的指针运算。

   比如:

   

(1)struct{   intp[4];   doublex;}b[17];

/*b是一个拥有17各元素的数组,每个元素都是一个结构……*/

   

(2)intcalendar[12][31];calendar是一个数组,有12个数组类型的元素,每个元素是一个拥有31个整型元素的数组。

因此求sizeof(calendar)的值是31*12*sizeof(int),(注意sizeof与st

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 工程科技 > 能源化工

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1