词法陷阱
1.贪心法
C编译器对C语言符号的识别,基于每一个符号应该包含尽可能多的字符原则。
如果输入流截止至某个字符之前都已经分解成为一个个符号,那么下一个符号将包括从该字符之后可能组成一个符号的最长字符串。
因此
x = y/*p,中的/*被解释为注释符号,而非除以p所指内容。
此类问题即是所谓准二义性问题(near-ambiguity).因此a+++++b,实际被编译器解释为:a ++ ++ + b
2.如果一个整形常量的第一个字符是0,则被处理为八进制,因此,实际编程中要注意,例如可能为了数据对齐,在常数前加0等。
3.单引号引起的一个字符实际是一个整数,但双引号引起的则是一个指向无名数组起始字符的指针。
语法陷阱
1.关于申明
我们根据表达式的申明,可以得到该类型的类型转换符,即只需要把申明中的变量名和申明末尾的分号去掉,再将剩余的部分用一个括号整个括起来即可。例如:
float (*fun)()
表示一个指向返回值为浮点类型的函数的指针,所以
(float (*)())表示一个指向返回值为浮点类型的函数的指针的类型转换符,
对于该指针所指函数的调用,则用:
(*fun)();
ANSIC标准允许程序员将上式简写为fun()
对于一个常数进行这样的类型转换,如下:
(float (*)())0
如果该0表示地址,则我们可以这样表示在0地址出的子程序:
(void (*)())0
换句话说,就是0指向一个返回值为float的函数,也就是说,0是一个指向返回值为浮点类型的函数的指针。那么,这样调用0所指函数:
(*(void(*)())0)()
如果用typedef 来表示,则如下:
typedef void (*fun)();//即fun是一个指向无返回的函数的指针的类型转换符。那么对0转换如下:
(fun)0
0地址子程序调用如下:
(*(fun)0)();
以上也出现在signal函数。
所以,实际上,typedef void (*fun)(),定义的是一种数据类型,是一个指向无返回值无形参的函数的指针,实际在使用中,需要定义该数据类型的实体,并指向该种类型的函数,才可用用该指针访问所指函数。
2.关于优先级
    实际上,关于优先级的问题,要看平时常用那些,这里只提示自己不常用的那些:
2.1单目运算符,如!,~,++,--,-,(T),*,&,sizeof,这些是自右向左的结合性。即他们相同优先级,从右开始结合处理。*p++,表示*(p++),即取*p,再p++。
2.2移位运算符比关系运算符优先级高。可以直接:
if (t << 1 > 2)
{
}
2.3关系运算6个优先级并不相同,< <= >= > 的优先级比== != 要高。
eg:
tax_rate = income > 40000 && residency < 5 ? 3.5 : 2.0;
先income > 40000 后residenecy < 5 然后 ? 最后 =、
3 关于switch
某些只要操作某一种情况的条件,即可与其他情况类似的实现的case,我们可以用switch省去break的方式实现,例如,减法就是加法的一种,即减数取负。
语义陷阱
int a[10];
for (int i = 0; i <= 10; ++i)
{
a[i] = 0;
}
这里发生死循环的关键在于,如果编译这段程序的编译器按照内存地址递减的方式给变量分配内存,那么a最后一个元素之后的一个字实际上分配给了变量i,也就是a[10]= 0编程i = 0;导致出现死循环。
#define NULL 0
在C中除了0,其他整数转换成指针时取决于具体C编译器的实现,编译器保证由0转换而来的指针不等于任何有效的指针。当0转换成指针时,该指针绝对不能解除引用。
关于求值顺序
if (count != 0 && sum / count < k)
C语言中只有四个运算符(&&, || ?:, 和,,)。
其中&& 和||先计算左侧,再根据需要计算右侧。
a?b:c, 先计算a,再根据a,计算b或者c。
逗号运算符,先对左侧求值,然后该值被丢弃,再对右侧求值。
而其他所有运算符的运算顺序,都是未定义的。
i= 0;
while(i < tabsize && tab[i] != x)
{
i++;
} 
i = tabsize时,在表中没有找到要找的x,其他情况,就是找到了。
关于溢出
有符号类型的数据溢出检测,如下:
if ((unsigned)a + (unsigned)b > INT_MAX)
{
}
ANSIC标准的limits.h中定义了INT_MAX
或者
if (a > INT_MAX - b)
{
}
链接
有一点要说明的是,链接器是独立于C语言实现的,因此,链接器对于编译器无法检测到的错误,同样束手无策。此时可以用lint。
另外,变量的多处引用,命名冲突等,可以将需要引用的变量,extern在单个.h文件中,在需要用到的.c中包含该.h文件,而在这些文件的某一个.c文件中,定义该变量即可。
库函数
1.为了保持与过去不能同时读写的程序向下兼容性,一个输入操作不能随后直接紧跟一个输出操作,反之亦然。如果同时要进行输入输出操作,必须增加fseek函数,调整文件状态。
关于库函数,以后有机会读C标准库。
预处理器
宏定义的对空格要求很严格,然后,在宏调用中,不如此严格。。。
#define f(x) ((x) - 1)
f(3) 等于2.f (3)也等于2.。。。。。
原来断言宏,还可以这样写:
#define assert(e) \
   ((void)((e)||_assert_error(__FILE_,__FILE__)))
利用||操作符的计算顺序,即保证效率,又稳定。 
补充:关于#define TEST (100 * 20),要分清楚的是,#define自始至终都是在预处理阶段,做宏展开,而真正的100 * 20是在编译阶段,编译器检测出常量运算而计算出其值的。

关于可移植性的缺陷
1、ANSIC 标准保证,C实现必须能够区别出前6个字符不同的外部名称。而且,这个定义中不区分大小写。因此,要注意的是,你编写的标示符名称,state,State可能在某些编译环境下,是一样的。
2.对于数据长度,short,int,long,有以下规定,short < int < long,一个int整数能容纳任何数组下标,字符长度由硬件决定。
有些硬件的字符长度是8位,有些是9位,另外,还有一些是16位,用来处理如日语之类的语言的大字符集。
ANSIC 要求long型整数的长度至少应该是32位。因此,要考虑程序移植性问题,那么最好用long型数据类型。
3.char转换成较大整数时,需要考虑应该讲该字符作为有符号数还是无符号数,如果你关注字符的最高位为1的char型数据是有符号数,还是无符号数,则可以将该字符定义为unsigned char,这样,无论什么编译器,都将其处理为无符号数,而如果申明为一般的字符变量,则视编译器而定。
因此,(unsigned)c,无法得到与c等价的整数,只能用(unsigned char)c.
4.移位运算符,被移除后的位用什么填充。
   如果是无符号数,填0,有符号数,则可能填0,也可能填用符号位的副本填充。而移位的范围是0~n-1.
5.内存位置0
   有的编译器,对内存位置0,设置强制硬件保护,有的则只读,但也有可以读写的情况,因此,NULL指针的使用要注意。
6.早期的realloc函数实现要求待重新分配的内存区域必须首先释放掉。
7.除法截断问题:
定我们让a除以b,商为q,余数为r:
那么,
q = a / b;
r = a % b;
假定b > 0,那么a,b,q,r之间维持怎样的关系呢?
1.q * b + r = a
2.如果我们改变a的正负号,我们希望只改变q的符号,不改变q的绝对值。
3.当b > 0,我们希望保证r >= 0 且 r < b.
实际上,许多语言不能同时保证以上3条,C和大多数语言一样,也是只保证以上前2条。
因此,就会出现,(-3) / 2 = -1, (-3) % 2 = -1.因此, r = a % b,最好定义a为无符号数。

一些建议:
1.直接了当的表明自己的意图,例如用()等方式。
2.考查最简单的特列。
3.使用不对称边界。
4.注意潜伏的bug。
5.防御性编程。

C陷阱与缺陷阅读笔记(上)相关推荐

  1. C陷阱与缺陷阅读笔记(下)

    关于printf函数 printf函数将数据写到标准输出,fprintf函数将数据写到任何文件,sprintf函数格式化字符串.这三个函数的返回值都是已经处理的字符数. sprintf函数作为输出数据 ...

  2. 《C语言陷阱和缺陷》笔记

    原著:Andrew Koenig - AT&T Bell Laboratories Murray Hill, New Jersey 07094 翻译:lover_P 修订:CQBOY 来自:h ...

  3. C陷阱与缺陷学习笔记

    导读 程序是由符号(token)序列所组成的,将程序分解成符号的过程,成为"词法分析". 符号构成更大的单元--语句和声明,语法细节最终决定了语义. 词法陷阱 符号(token)指 ...

  4. C陷阱与缺陷--读书笔记3 语义“陷阱”

    第三章 一.知识点 1.C语言中的数组值得注意的地方有以下两点:(P41) (1).C语言中只有一维数组,而且数组的大小必须在编译期就作为一个常数确定下来.然而,C语言中数组的元素可以是任何类型的对象 ...

  5. 《c陷阱与缺陷》笔记--注意边界值

    如果要自己实现一个获取绝对值的函数,应该都没有问题,我这边也自己写了一个: void myabs(int i){if(i>=0){printf("%d\n",i);}else ...

  6. 《C陷阱与缺陷》学习笔记(2):作者有话说

    道阻且长,行则将至.埋头苦干,不鸣则已,一鸣惊人!加油,骚年! 1 参考资料 1.书本参考资料 <C陷阱与缺陷>前言: 2.网络参考资料 [维基百科]Andrew Koenig:https ...

  7. 【2018深信服 醒狮计划】《C陷阱与缺陷》学习笔记

    2018深信服"醒狮计划"笔记 先自我介绍一下,湖大研一计算机的菜鸡,本科网络工程的,大学里不务正业一直在做应用,大一自学过一段时间的MFC,Windows网络编程,感觉比控制台好 ...

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

    第一章 词法陷阱 笔记本:<C陷阱与缺陷> 创建时间:2018/4/23 22:06:21                                                  ...

  9. 阅读《C陷阱与缺陷》的知识增量

    看完<C陷阱与缺陷>,忍不住要重新翻一下,记录一下与自己的惯性思维不符合的地方.记录的是知识的增量,是这几天的流量,而不是存量. 这本书是在ASCI C/C89订制之前写的,有些地方有疏漏 ...

最新文章

  1. C#版本的CPU性能测试
  2. 【读薄Effective Java】创建和销毁对象
  3. 跨编译单元之初始化次序
  4. 谈阿里核心业务监控平台SunFire的技术架构
  5. appium 搭建及实例
  6. 在 VC6 中使用 GdiPlus-安装
  7. Python正则表达式简单说明(菜鸟教程里面的说明)
  8. 命令行编译java文件
  9. 阿里腾讯华为在行动!程序员远程办公究竟用哪个视频会议好?
  10. 基于数据挖掘技术的客户保有应用研究
  11. H3C模拟器2012鼎杰终极版的基本使用教程
  12. C语言做的猜数字小游戏
  13. 千锋Django学习笔记
  14. Es6里面的Set和Map集合
  15. tortoise set autocrlf convert
  16. 硬件电路常用设计摘要
  17. ffmpeg之av_read_frame
  18. 从零开始学android:Android中的基本控件(上)
  19. @Primary和@Qualifier注解
  20. php电影播放系统在线视频点播系统 php毕业设计题目课题选题 php毕业设计项目作品源码(1)功能模块概要

热门文章

  1. Android系统自带AEC/AGC/NC的demo
  2. repo/git提交代码
  3. 添加native和java系统服务
  4. iOS 后台运行实现总结
  5. VALSE学习(七):跨媒体分析-Cross-Media Analysis and Intelligence
  6. c# checkbox 外观_2020款日产蓝鸟上市!外观比大众朗逸漂亮,油耗6L 国六,9.59万_搜狐汽车...
  7. python k线顶分型_顶分型底分型代码
  8. c++删除数组中重复元素_C / C ++中的数组
  9. 将JSON格式的字符串转换成List集合引入gson 的jar包
  10. npm换成国内源 npm换源 npm换淘宝源镜像