2015年5月10日 星期日 12:11
第1章 词法“陷阱”
- =不同于==
- &和|(按位运算符)不同于&&和||(逻辑运算符)
- 词法分析中的“贪心法”
- 整型常量,整型常量第一个字符为0被视为八进制,10和010含义截然不同
- 字符和字符串
- 用单引号引起的一个字符代表一个整数,如'a'含义与0141(八进制)或97(十进制)一致
- 用双引号引起的字符串,代表一个指向无名数组起始字符的指针
第2章 语法“陷阱“
- 理解函数声明
声明=类型+声明符,(*(void(*)())0)()的含义是?--->(void(*)())0将0强制转化为”指向返回值为void类型的函数的指针“
- 运算符优先级问题
if(flags & FLAG!=0)等价于 if(flags &(FLAG!=0))
r=hi<<4+low等价于 r=hi<<(4+low) ,优先级影响运算的顺序,有时候并不是程序员设想的那样计算
-
- 优先级最高的并不是真正意义上的运算符,函数调用(),数组下标[],结构成员选择->,点操作符.,都是自左向右结合
- 单目运算符优先级次于上述运算符,*p()编译器会解释为*(p()),与(*p)()不一样。自右向左结合,所以*p++和(*p)++是不一样的
- 双目运算符优先级最低,算数>移位>关系>逻辑>赋值>条件运算符
- 逗号操作符优先级最低
- 注意作为语句结束标志的分号,if或者while字句后面多写/少写分号
- switch语句,break 语句,fall through现象
- 函数调用,f();这是函数调用,f;这表示计算f的地址,并不调用它
- ”悬挂”else引发的问题
第3章 语义“陷阱"
- 指针和数组
- C语言中只有一维数组,但是数组元素可以是数组,”仿真“出多维数组
- 除了数组长度和指向数组下标为0的元素指针,其他操作都是通过指针完成
- 非数组的指针
动态分配内存存储字符串,需要注意的三个问题
-
- 检查是否分配成功
- malloc显示分配内存,必须显示释放内存
- 字符串存储内存需要多一个空间存放结束标志(' ')
- 作为参数的数组声明,数组名作为函数参数会立刻被换为指向该数组第一个元素的指针,C语言中自动将作为参数的数组声明转换为相应的指针声明
- 避免”举隅法“,以部分代替整体,以整体代替部分。char *p;p="xyz"; p仅指向x元素
- 空指针并非空字符串,NULL,任何试图使用NULL指针所指向的内存中的内容都是不合法的
- 边界计算与不对称边界
- 求值顺序,&&,||首先对左侧求值,只有在需要时才对右侧求值
- 运算符&&,||和! 与&,|,~的区别
- 整数溢出,有符号数与无符号数运算,结果为无符号数,不发生溢出。两个有符号数运算,”溢出”可能发生。解决方法(转为无符号数运算,判断a>INT_MAX-b是否成立)
- 为函数main提供返回值,如果为显示声明返回类型,默认为int
第4章 连接
- 什么是连接器
输入:一组目标模块和库文件
输出:载入模块(可执行文件实体)
- 声明和定义 int a;定义,int a=7;定义并初始化,extern int a;声明表示了这是对外部变量a的引用
- 命名冲突与static修饰符,static int a;将a的作用域限制在一个源文件中,对于其它源文件,a是不可见的
- 形参、实参和返回值,在函数声明中可以省略参数类型说明,但是float类型参数会自动转化成double,char和short会转换为int
- 检查外部类型
一个文件包含 char filename[]="etcpasswd";
另一个文件包含声明 extern char *filename;
这种方式并不提倡,毕竟字符串数组和字符指针类型不同,而且它们使用的存储方式也不一样
- 头文件,每个外部对象只在一个地方声明,一般就在头文件中
第5章 库函数
- 返回整数的getchar函数,返回标准输入文件的i下一个字符,类型为int
- 更新顺序文件,允许程序打开一个文件的同时进行读出和写入操作,但一个输入操作不能随后直接跟一个输出操作,反之亦然,如果要同时进行输入和输出操作,插入fseek函数
- 缓冲输出与内存分配,setbuf(stdout,buf),所有写到stdout的输出都应该使用buf作为缓冲,直到buf满或者程序员调用fflush,buf缓冲区中的内容才实际写到stdout中
- 使用errno检测错误
- 库函数signal
第6章 预处理器
在严格意义上的编程过程开始之前,C语言预处理器首先对代码进行必要的转换处理。
- 不能忽略宏定义中的空格 #define f (x) ((x)-x)和 #define f(x) ((x)-1)两种含义
- 宏并不是函数
- 宏定义中的括号,预防引起与优先级有关的问题
- 宏定义中的表达式中的操作数可能被求值多次,多以自增自减操作符需要慎重使用
- 混合了宏和递增运算(评,简直是chaos)
- 宏并不是语句
- 宏并不是类型定义
#define T1 struct foo * typedef struct foo *T2; T1 a,b; /*a是指针,但b是结构*/ T2 a,b; /*a,b均为指向结构的指针*/
第7章 可移植性缺陷
- 应对C语言标准变更,函数原型的概念,新旧标准定义方式不同
- 标示符名称的限制,编译器识别标识符最大长度,不区分外部名称大小
- 整数的大小,short,int,long字符长度由硬件特性决定
- 字符是有符号整数还是无符号整数
- 将一个字符值转换为一个较大的整数时,需要考虑它的有无符号问题
- 如果C是字符变量,(unsigned)c并不一定会得到c等价的无符号整数。因为将字符c转为无符号数时,首先转换为int,正确语句应为(unsigned char)c
- 移位运算符,向右移位时,逻辑右移还是算数右移。移位计数允许的取值范围是什么(大于等于0,严格小于位数n)
- 内存位置0
- 除法运算时发生的截断
- 随机数的大小
- 大小写转换,如果输入的大小写字母都是无效呢
- 首先释放,然后重新分配。