Skip to content

Latest commit

 

History

History
executable file
·
228 lines (170 loc) · 5.64 KB

C陷阱与缺陷读后感.md

File metadata and controls

executable file
·
228 lines (170 loc) · 5.64 KB
  • C陷阱与缺陷读后感

词法分析的“贪心法”

对于一个语句由多个 字符组成,可能造成二义性时,如:a+++b。C语言的解决方法是:每一个符号应该包含尽可能多的字符。即:从左到右,一个一个字符读入,如果可以下一个字符合法,再读入下一个,即贪心法

运算符 && 和 ||

a && b++

按照优先级,如果a不为真,将不判断后面部分,即b++部分不会被执行

a || b++

同理,如果a为真,将不判断后面部分,b++部分不会被执行 

指针和数组

int a[4], i;
a[i] 和 i[a]有同样的含义,如a[2] == 2[a]

switch语句

每个case结束后都应该接一个break;如果没有接break,那么后面的case语句都会顺序执行下去,不管后面
case的情况是否对应

函数调用

如果f是一个函数名,则语句
f();
是调用函数,可语句
f;
确实一个什么也不做的语句。

else悬挂

else始终于同一对括号内最近未匹配的if结合
缺少'}'可能出现错误:
error: invalid storage class for function 'xxx'

sizeof 和 strlen

    char *p[] = "hello,world"
    siezof(p) = 12
    strlen(p) = 11
    siezof结果是指针指向的空间大小,字符串常量包括结束符在内,空间大小比字符长度大一
    strlen求得结果是字符串的长度,不包括结束符,
    在字符串合并(strcpy)等操作时要主要空间的大小
    

const 关键字

	const int a;
	int const a;	//前两个一样,表示a不能修改
	const int *a;	//a指向的值不能修改,a本身可以修改
	int * const a;	//a本身不能修改,a指向的值可以修改
	int const * a const;	//a本身和a指向的值不能修改

volatile 关键字

一个定义为volatile的变量是说这个变量可能被意想不到的改变,优化器在用到这个变量时,
必须每次小心重新读取这个变量的值,而不是使用寄存器里面的备份
例子:
	并行设备的硬件寄存器
	多线程共享的变量

指针索引字符串

    char c[] = {"abcd\0ef"}, *p= c;
    printf("%c",*p+5);
    printf("%c",*(p+5));
    输出结果:fe
    解析:*p+5输出的是p指向的字符加5的和的ascll码,
        *(p+5)输出的是p指向的地址向后偏移5个字节的字符

关于浮点数计算

    double d=3.2;
    int x,y;
    x=1.2;
    y=(x+3.8)/5.0; 
    printf("%d\n",d*y);
	x是int型,当x被赋值为1.2浮点型时,x实际赋值为1,所以4.8/5.0由于/是整除,结果为小于1的小数,所以y被赋值为0

与 “零值” 比较的if语句

	bool、int 类型
		if(flag)
	float 类型
		if ((x >= - 0.00001) && (x <= 0.00001)
	指针类型
		if(p == NULL) if(p != NULL)

C程序的编译过程

首先多个c源文件经过预处理:省略注释的文本和宏替换,生成被修改的文本文件

china@ubuntu:/mnt/hgfs/Share$ gcc -E test.c -o test.i
test.ctest.i

然后是编译和汇编,生成多个可重定位目标程序(二进制文件)

china@ubuntu:/mnt/hgfs/Share$ gcc -c test.c -o test.o
test.ctest.itest.stest.o
在这两个步骤中编译器会把多个.c源文件分开编译对于使用的外部函数只会检查这个函数前面是否有声明并不关心函数的内部实现和函数或变量名是否有冲突

连接器把多个可重定位目标程序.o文件链接

使用连接器的好处就是可以把多个源文件互不相关的分开编译,而且可以使用静态库的编译方式

extern、static关键字

extern int a

是声明不是定义,声明a在其他源文件定义,要在这引用

static 修饰 全局变量

说明这个变量的作用域仅在这个源文件中,所以可以多个源文件同时存在同名的静态全局变量,互不干扰
生存周期是整个程序运行周期不变

static 修饰 局部变量

作用域还是在函数内,不变
生存周期变成了整个程序运行周期

static 修饰 函数

static修饰的函数只能在源文件内被引用

检查外部类型

C语言规定:如果一个未声明的标识符后跟一个开括号,那么它将被视为一个返回整型的函数

字符数组如果在两个源文件中定义和声明可以char filename[] = "/etc/passwd";
extern char filename[];

预处理器

  1. 不能忽视宏定义的空格
#define f (x) ((x)-1)
f  (x) 中间多了一个空格产生了二义性
1)、f(x) 代表 ((x)-1)
2)、f 代表(x)((x)-1)

2.使用宏注意使用括号

正确用法#define abs(x) (((x)>=0)?(x):-(x))
或者
#define max(a,b) ((a)>(b)?(a):(b))
使用括号可以预防引起优先级有关的问题
假如有这样定义
#define abs(x) x>0?x:-x
abs(a-b)
展开后就是
a-b>0?a-b:-a-b
期望结果却是a-b>0?a-b:(-a-b)
再例如,整个表达式括起来的作用是
abs(a)+1
展开后结果为
a>0?a:-a+1
此时结果为a或者-a+1
而我们期望的结果是a的绝对值加1

最好不要把#define 当作 typedef用

有以下代码
#define T1 struct foo*
typedef struct foo *T2
当同时定义多个变量时#define 会出现问题
T1 a,b;
会被宏替换为struct foo * a, b;
a是结构体指针没错可b被定义为结构体不是指针而本意两个都是结构体指针
T2 a, b;
两个都是结构体指针正确