码迷,mamicode.com
首页 > 其他好文 > 详细

《c陷阱与缺陷》读书笔记

时间:2015-04-27 16:50:15      阅读:150      评论:0      收藏:0      [点我收藏+]

标签:读书笔记   c语言   移植   指针   对象   

1. = 不同于 ==

2. & 和 | 不同于 && 和 ||

3. 编译器将程序分解成符号的方法是,从左到右一个一个读,直到读入的字符组成的字符串已不在可能组成一个有意义的符号,贪心的处理程序。如a --- b编译器就会认为是  a-- - b,而不是认为a - --b。

4. 整数常量,如果一个整型常量第一个字符是0,那么该变量会被认为是8进制数。

5. 单引号引起的一个字符实际上代表一个整数,‘a’与0141和97是严格一致的。双引号引起的字符创代表的是一个指向无名数组其实字符的指针,该数组被双引号之间的字符以及一个额外的‘\0’初始化。一个代表整数一个代表指针,所以混用编译器会报错。

6. 任何C变量的声明都由两部分组成,类型以及一组类似表达式的声明符。

7. 运算符优先级问题,解决办法就是加括号。但最好是记住c语言中运算符的优先级。

(1) 任何一个逻辑运算符优先级均低于一个关系运算符。

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

8. 注意作为语句结束符的分号 ;

9. Switch语句,case后一定要写break; 否则它会一直执行下去。

10. 调用函数的时候,即使没有参数后面也要有括号。如f();

11. Else语句始终与同一括号内最近未匹配的if相结合。所以如果if和else使用的时候要注意添加大括号。

12. 指针与数组,C语言中只有一位数组,而且数组的大小必须在编译器就作为一个常数确定下来。我们只能做两件事,确定该数组的大小和获得指向该数组下标为0的元素的指针。任何一个数组下标的运算都等同于一个对应的指针运算。

Calendar[month][day] = 0;

*(*(Calendar + month) + day) = 0;

//上述二者其实是等价的

int month;

for (month = 0 ; month < 12 ; month ++)

{

int day;

for (day = 0 ; day < 31 ; day ++)

{

calendar[month][day] = 0;

}

}

int (*monthp)[31];

for (monthp = calendar ; monthp < &calendar[12] ; monthp ++)

{

int *dayp;

for (dayp = *monthp ; dayp < &(*monthp)[31] ; dayp ++)

{

*dayp = 0;

}

}

//上述二者其实是等价的

13. 非数组指针

//将s和t两个字符串合并为一个字符串r

char *r,*malloc();

r = malloc(strlen(s) + strlen(t) + 1);

if(!r){complain();exit(1);}

strcpy(r , s);

strcat(r , t);

……//一段时间之后进行释放

free(r);

14. 作为参数的数组声明,实际上数组名会被转换成指向该数组的第一个元素的指针进行传递。

15. 当指针相互赋值的时候,实际上传递的是这个数组的首地址,也就是说两个指针指向内存中同一地址。改变其中一个指针时另外一个也会改变,因为他们是指向相同地址。

16. 空指针并非空字符串

17. 仔细计算边界,C语言中数组的下标是从0开始计算。用第一个入界点和第一个出界点来表示一个数值范围。

18. 求值顺序,C语言只有四个运算符(&&、||、?: 、,)存在规定的求值顺序,运算符&&和||首先对左侧操作数求值,只有需要时才对右侧操作数求值。运算符a?b:c中操作数a首先被求值,根据a的值再求操作数b或者c的值。而逗号运算符首先对左侧操作数求值,然后该值被丢弃再对右侧操作数求值。

19. 运算符&&、||、和!

对操作数的处理方式是将其视作要么为真要么为假,通常视0为假,而非0为真。

运算符&、| 和 ~

对操作数的处理方式是将其视为一个二进制的位序列,分别对其每个位进行操作。

20. 整数溢出,在无符号数中是没有溢出的概念的,只有两个操作数都为有符号整数时,溢出才有可能发生。可以将其都转换为无符号数来运算。

21. 为函数main提供返回值,返回值为0则代表程序运行成功,返回值为非0则表示程序执行失败。

22. 连接

一个C程序可能有多个分别编译的部分组成,这些不同的部分通过连机器的程序组成一个整体。

Lint这个程序就是检查连接过程中错误的程序应该重视,并且善加利用。

23. C语言中一个重要思想就是分别编译,即若干的源程序可以在不同的时候单独进行编译,然后在恰当的时候整合在一起。编译器会生成若干的目标模块,连接器通常将目标模块看成一组外部对象组成的。每个外部对象代表着及其内存中的某个部分,并通过一个外部名称来识别,连接器的输入是一组目标模块和库文件,连接器的输出是一个载入模块,因为连接器对C语言知之甚少,所以有很多错误不能被检测出来,lint程序一定要使用。

24. 声明和定义

int  a;//定义a的变量

extern int a;//实际上是一个声明,对外部变量a的引用

每个外部变量最只定义一次,因为定义多次引用会出错。

25. 命名冲突与static修饰符

static int a;将a的作用域限制在一个源文件中,对于其他源文件来说a是不可见的。因此如果若干个函数需要共享一组外部对象,可以将这些函数放在一个源文件中,把它们需要用到的对象也都在一个源文件中以static修饰符声明。同样static不仅适用于变量,也适用于函数。有了static之后就不用担心多个源文件中有相同函数名了。

26. 形参、实参与返回值

任何函数都有一个形参列表,列表中的每一个参数都是一个变量,该变量调用过程中被初始化。对于某些函数来说形参列表为空,函数调用时将实参列表传递给被调用的函数。

如果一个函数在被定义和声明之前被调用,那么它的返回类型默认为整型。

27. 检查外部类型

假设我们有一个C程序,它由两个源文件组成,一个文件中包含外部变量n的声明 extern int n;另一个文件中包含 long n;这是一个无效的程序,但是有的C编译器可能检测不出这种问题。

28. 头文件

每个外部对象只在一个地方声明,这个声明的地方一般就在一个头文件中,需要用到该外部对象的所有模块都应该包含这个头文件,一定要注意定义该外部对象的模块也应该包含这个头文件。

29. 返回整数的getchar函数

一般情况下返回的是标准输入文件中的下一个字符,无输入时返回EOF。

30. 读写操作不能同时

如果想同时读写,那么必须在其中插入fseek函数的调用。

fseek(文件指针,从起始位置跳过多少字节,起始位置)

31. 缓存输出与内存分配

程序的输出有两种方式,一种是即时处理,另一种是先暂存起来,然后再大块写入。

setbuf(stdout,buf);语句将通知输入/输出库,所有写入到stdout的输出都应该使用buf作为输出缓冲区,直到缓冲区满或者调用fflush的时候才将文件实际写入stdout中。缓冲区的大小有头文件stdio.h中的BUFSIZ定义。

将buf数组定义为静态,或者放在main函数之外,再或者使用动态分配setbuf(stdout,malloc()); main函数结束之后,C运行时库会进行清理工作,如果设置为普通数组,main结束之后就被释放了,当运行时库再次释放的时候就会报错。

32. 库函数signal

作为捕捉异步事件的一种方式,要是用该库函数需要在源文件中加上#include<signal.h>要处理一个特定的信号,可以这样调用

signal(signal type,handler function);

signal type代表要捕捉的信号类型

handler function当事件发生时,将要加以调用的事件处理函数

信号非常棘手,尽量让处理函数简单,我们可以很容易修改。

33. 宏定义中的空格

#define f (x) ((x)-1)代表的意思就是,f等价于(x) ((x)-1),所以空格神马的一定要注意,这一点不适用于宏调用,只对宏定义适用。

正确的定义方式#define f(x) ((x)-1)

34. 宏不是函数

宏定义中的括号它们的作用是预防引起与优先级有关的问题。

35. 宏不是语句

考虑一下assert宏,它的参数是一个表达式,如果该表达式为0,则使程序终止执行,并给出一条出错信息。定义之后可以将其还原带程序中,看其是否有问题。

36. 宏并不是类型定义

#define FOOTYPE struct foo

FOOTYPE a;

FOOTYPE b,c;

这样的用法有一个优点就是可移植性,但最好还是使用类型定义

typedef struct foo FOOTYPE;

37. 字符是有符号数还是无符号数

使用语句(unsigned char)c,一个unsigned char类型的字符可以直接转换为无符号数。

38. 移位运算符

如果被移位的对象长度是n位,那么移位计数必须大于或等于0,而严格小于n。因此不肯那个做到在单次操作中将某个数值的所有位都移出,加上这个限制条件,我们就能在硬件上更高效的实现移位运算。

39. 内存位置0

null指针并不指向任何对象,因为有的机器不允许读内存为0的地方,而有的机器可以读内存为0的地方,所以就导致程序移植到另一台机器就会出现问题。

40. 首先释放,然后重新分配

大多数C语言实现都为使用者提供了3个内存分配函数:malloc、realloc、free。调用malloc(n)将返回一个指针,指向一个新分配的可以容纳n个字符串的内存,将malloc返回的指针作为参数传入free的时候,就释放了这块内存。调用realloc函数的时候,需要把指向已分配内存指针以及这块内存新的大小作为参数传入,就可以调整这块新区域的内存大小了。

 

《c陷阱与缺陷》读书笔记

标签:读书笔记   c语言   移植   指针   对象   

原文地址:http://blog.csdn.net/djd1234567/article/details/45312281

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!