第一章 词法陷阱
=不等于==
贪心法 词法分析
编译器会拿尽可能多的字符组成符号。
a—-b会被编译器解释为a — - b
如果编译器支持=+和+=一致,
那么a=-1会变成a = a - 1;
整形常量
字符串和字符
双引号引出的是一个指针,在堆区的指针
而单引号的本质是整数,存在char的一个整数。
char *str = 'x';
是错误的写法
第二章 语法陷阱
判断
优先级
C语言的符号具有一定优先级,括号的优先级最高,可以通过加括号的方法控制未知 的优先级。
分号
分号是语句结束的符号。
循环语句默认执行下一条语句,因此循环语句本身不能加括号。
switch语句
switch语句需要加break,但是如果刻意设计时,需要注释说明这里没有break
函数名
悬挂else
当if是是外层if时且没有else跟随,需要加上大括号防止悬挂else导致程序逻辑出现问题。
第三章 语义陷阱
内存分配失败
使用malloc分配内存时需要验证是否分配失败。
如果分配失败的情况,需要返回错误信息。如果不增加if语句判断是否成功,可能会报出取消对NULL指针的引用警告。
字符串计数
字符串是组类型,strlen可以分析字符串的长度,但是没有包含那个NULL结尾。所以在给strlen分配长度的时候需要加上一个单位来容纳\0这个结束符号
char *是一个指针类型,
char []是个数组类型
因为传值引用的时候数组只传首地址,所以需要拿指针去接收
但是指针可以直接操作。
举隅法
对于char *p = “Xyz”;来说
实际上是四个空间
Xyz\0这四个,所以p[3]==0
但是这种写法并不提倡
边界
栈区在定义声明时必须考虑边界问题,尤其是数组边界在循环中操作时
例如这个边界问题:
#include<stdio.h>
#include<malloc.h>
#define MAX_HEAP 4
int main(){
int *pHeapBlock = NULL;
int *pCursor = NULL;
int arr[MAX_HEAP] = {0};
int i;
pHeapBlock = (int *)malloc( sizeof(int) * MAX_HEAP);
pCursor = pHeapBlock;
for(i = 0;i < MAX_HEAP;i++){
*pCursor = arr[i];
pCursor++;
}
pCursor = pHeapBlock;
for(i = 0;i < MAX_HEAP;i++){
printf("%d\n",*pCursor);
pCursor++;
}
free(pHeapBlock);
return 0;
}
相对于上面的程序,下面的代码部分的健壮性就弱的多
int *pHeapBlock = NULL;
int *pCursor = NULL;
int arr[10] = {0};
int i;
pHeapBlock = (int *)malloc( sizeof(int) * 4);
pCursor = pHeapBlock;
for(i = 0;i <= 4;i++){ //这个4是边界值,如果改成大于4的其他数,将会引起未知错误。
*pCursor = arr[i];
pCursor++;
}
pCursor = pHeapBlock;
for(i = 0;i < 4;i++){
printf("%d\n",*pCursor);
pCursor++;
}
free(pHeapBlock);
整数溢出
第四章 链接器
链接器是链接文件和代码之间的编译工具。
extren可以重复声明,但是只能定义一次
static是静态变量。
auto是自动变量,但是大部分时候编译器都会忽略auto说明或是自动增加
register是寄存器变量,可以提高一些效率,但是编译器决定哪些register要储存,哪些不储存。
第五章 库函数
库函数提供了很多封装了的功能和函数。
但是函数定义的一些标识需要我们谨慎使用。
例如用来储存EOF的变量是否会溢出。
函数指针的指向和使用是否正确。
第六章 预处理器
预处理器会处理宏和变量。
但是宏不是语句,而是编译器规则,所以使用宏的时候需要注意。