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

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

最近花了一点时间阅读了《C陷阱与缺陷》 ,东西不多,有些标题就够了,

自己总结下需要记录的,以后没事看看。

自己敲的,虽然有误与不足,转载请说明,附原文链接

第 0 章  导读

第 1 章  词法“陷阱”

本章都是一些,拼写错误,主要是仔细,别误写即可。

1.1 =不同于==

比较运算符与赋值运算符的相互误写

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

按位运算符与逻辑运算符的混用

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

1)“大嘴法”:每一个符号应该包含尽可能多的字符;

a---b 意思是:(a--)-b

2)注释

1.4 整型常量

整型变量开头字符是0,实则为八进制数。

1.5 字符与字符串

1)单引号的为字符,实则为整数,为ASCII字符集。

2)双引号的为字符串,代表一个指向无名数组起始字符的指针,该数组被双引号中的字符和二进制为零的’\0’初始化。

Practice:

1.某些C编译器允许嵌套注释,写一个测试程序:

例如:/*/**/”*/”:允许嵌套,等效于一个引号;不允许,等效于字符串“*/”;

所以为了测试该编译器是否允许嵌套注释,而且不报错:

/*/*/0*/**/1

如果允许嵌套:   /*  /*/0*/  **/ 1 值为1:

如果不允许嵌套: /* / */  0 *  / **/  1 值为0*1=0;

2.n-->0含义n-- >0,为什么不是n- ->0?

根据大嘴法,--作为单个符合;

3.a+++++b含义

a++ + ++b;

若是根据大嘴法,a++ ++ +b;语法是不正确的;

等价于((a++)++)+b,a++的结果是不能作为左值的,编译器不会接受。

第 2 章 语法“陷阱”

2.1 理解函数声明

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

1)float *g(),(*h)();()优先级高于*,所以g是一个函数,该函数的返回值类型为指向浮点数的指针。同理,h是一个函数指针,h所指向的函数的返回值为浮点类型。

2)分析(*(void(*)())0)()

(*fp)();fp是一个函数指针,*fp该指针指向的函数;

(*0)(),指向返回值为void类型的函数指针。(*fp)()的值为void,fp的声明如下:

void (*fp)(); 用(void(*)())0替换fp;

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

或者 typedef void (*fun)();

(*(fun)0)();

假定sig是一个整数,(*sfg)(sig)值为void类型,可以声明sfg;

void (*sfg)(int);后面int为函数的参数;

void ( *signal(int , void(*)(int) ) (int);

2.2 运算符的优先级问题

If(flag &FLAG!=0)  !=优先级高于&;

R=hi<<4 + low      +高于<<;

1、优先级最高包括:数组下标、函数调用操作符个结构成员选择操作符,自左至右结合;

2、单目运算符其次,自右至左,*p++,为*p(++);

3、双目运算符:算术最高,移位次之,关系再次之,接着是逻辑运算符,赋值运算符,最后条件运算符;

4、三目条件运算符最低;

5、赋值运算符,从右至左;a=b=0; 相当于b=0,a=b;

主要记住两点:

1、任何一个逻辑运算符低于任何一个关系运算符;

2、移位运算符优先级比算术运算符低、但是比关系运算符高;

任何逻辑运算符优先级不同;按位运算符高于顺序运算符;&高于|;^介于二者之间;

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

注意If语句、while(); 后面的封号

函数main缺省为int类型;

2.4 switch语句

注意break不要遗漏;

2.5 函数调用

f();函数调用语句;

f;什么也不做,计算函数f的地址;

2.6 “悬挂” else引发的问题

else始终与同一对括号内最近的未匹配的if结合;

记得加大括号区分就行;

第 3 章  语义“陷阱”

3.1 指针与数组

数组注意的两点:

1、C语言只有一维数组,而且数组的大小必须在编译器作为一个常数确定。

2、对于一个数组只能做2件事:确定该数组的大小,以及获得指向该数组下标为0的元素的指针。

所以、任何一个数组的下标运算符等同于一个对于的指针运算。

int calendar[12][31]; calendar是一个数组,数组有12个数组类型的元素,每个元素都是拥有31个整型元素的数组;

*(a+i)即数组a中下标为i的元素的引用,记为a[i],由于a+i与i+a一致,所以a[i]与i[a]相同含义。

i=calendar[4][7]

i=*(calendar[4]+7)

i=*(*(calendar+4)+7)

3.2 非数组的指针

char *r;

strcpy(r,s);

strcat(r,t);

之所以不行在于不能确定r指向何处。所以要有malloc分配内存。

char *r,*malloc();

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

if(!r){

complain();

exit(1);}

strcpy(r,s);

strcat(r,t);

free(r);

容易犯的三个错误。

3.3 作为参数的数组声明

我们没有办法将一个数组作为函数参数直接传递,可以放入一个结构体,数组作为函数的参数自动转换为相应的指针声明。

3.4 避免“举隅法”

复制指针并不同时复制指针所指向的数据,只是地址。

修改字符串常量未定义。q[1]=’y’;

3.5 空指针并非空字符串

常数0被转换为指针使用,不能被解除引用。

3.6 边界计算与不对称边界

数组元素0-n-1;

3.7 求值顺序

&&,||的问题,&&左边为假,右边不计算;同理,||;

x,y在函数f(x,y)中未定义;g((x,y))是先x后y顺序,函数g只有一个参数。

3.8 运算符&&、||和!

按位运算符&,|,~;逻辑运算符&&,||,!;

3.9 整数溢出

3.10 为函数main提供返回值

Practice 

实现二分查找;

移位提高程序速度,但是相加可能溢出;下标运算比指针慢;移位运算符优先级低于算法;

完整:

int *bsearch(int *t,int n,int x)
{
	int *lo=t,*hi=t+n;
	while(lo<hi)
	{
		int *mid=lo+((hi-lo)>>1);
		if(x<*mid)
			hi=mid;
		else if(x>*mid)
			lo=mid+1; 
		else
			return mid;
	}
	return NULL;
}

第 4 章  连接

一个C程序可能是由多个分别编译的部分组成,这些不同部分通过一个通常叫做连接器的程序合并成一个整体。

4.1 什么是连接器

C重要思想:分别编译;

处理命名冲突:干脆完全禁止

4.2 声明与定义

int a;如果其位置所有函数体之外,被称为外部对象a的定义,分配存储空间,初始值为0;

extern int a;引用a,其存储空间在其他地方。

4.3 命名冲突与static修饰符

两个具有相同名称的外部对象实际上代表的是同一个对象。

static修饰符是一个能减少此类命名冲突的有用工具,可修饰变量和函数。

static int a;a的作用域限制在一个源文件内,其他源文件,a是不可见。

4.4 形参、实参与返回值

float自动转为doub,short或char转为int类型。

4.5 检查外部类型

4.6 头文件

声明放头文件,#include “file.h”

第 5 章  库函数

5.1 返回整数的getchar函数

getchar返回整数,若是char,无法容下,被截取;

5.2 更新顺序文件

5.3 缓冲输出与内存分配

5.4 使用ernlo检测错误

5.5 库函数signal

Setjmp,longjmp

第 6 章  预处理器

注意介绍宏,使用函数造成开销,宏只是对程序的文本起作用。

6.1 不能忽视宏定义中的空格

6.2 宏并不是函数

注意加括号,防止优先级问题,

宏展开可能产生庞大的表达式,占用空间远远超过了期望的空间

6.3 宏并不是语句

6.4 宏并不是类型定义

#define f int,可移植性好,但是声明多个变量出现问题;

Practice

实现max,只被求值一次;

定义临时变量

static int t1,t2;

#define max(a,b) (t1=(a),t2=(b),t1>t2?t1:t2)

第 7 章  可移植性缺陷

C程序能够在不同的环境中移植

7.1 应对C语言标准变更

函数原型

7.2 标识符名称的限制

C实现必须能够前6个字符不同的外部名称;,定义没有区分大小写

7.3 整数的大小

不同类型整数的相对长度定义:

1、3种类型的整数其长度非递减的:short、int、long,long至少32位

2、一个普通的整数(int)足够容纳任何数字下标

3、字符长度由硬件特性决定

7.4 字符是有符号整数还是无符号整数

7.5 移位运算符

向右移位,0或者1填充

7.6 内存位置0

null指针不指向任何对象

7.7 除法运算时发生的截断

余数与被除数的正负号相同

h=n&HASHSIZE

最好:h=(n+HASHSIZE)%HASHSIZE

7.8 随机数的大小

rand(),返回0到2^15-1的整数,有的为31位;

7.9 大小写转换

7.10 首先释放,然后重新分配

7.11 可移植性问题的一个例子

第 8 章  建议与答案

8.1建议

8.2答案

附录A:PRINTF,VARARGS与STDARG

附录B:Koenig和Moo夫妇访谈

C++程序员建议:

1、避免使用指针

2、提倡使用程序库

3、使用类表示概念

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值