--
分类: C/C++
2014-01-14 15:25:28
第二章 预处理
1、宏是危险的,C99加入内联函数,内联函数和宏互换使用应该优先选择内联函数。静态函数与内联函数有相同的优点,并且得到了C90的支持。但是,宏可以实现类型通用函数,若不借助C++模板机制,C语言无法实现这种函数。
Typedef void (*exec_func)(void);
Inline void exec_bump(exec_func f)
{
F(); .....
}
2、预防多重定义,都#include <*.h>文件,
#ifndef HEADER_H
#define HEADER_H
。。。。。
#endif
3、两个连续的问号表示一个三字符序列
4、头文件唯一,C99之区分8个字符,但是OS支持长文件名,所以可以区分它们。
5、使用安全API,避免没有边界检查,缓冲区溢出的API
6、Do while中包裹几条语句:
#define SWAP(x,y) tmp=x;x=y;y=tmp;
若if(z==0)SWAP(x,y)则不会按照我们安排来处理,所以if总是要带括号或者
#define SWAP(x,y) do{tmp=x;x=y;y=tmp;}while(0)
7、Assert()宏是不安全宏,只是用于确定不正确的假设,而不适合错误检查,一般不用与服务器程序或嵌入式系统,不安全宏中避免对参数赋值、增值、减值等操作。
第三章 声明和初始化
1、用const限定不可修改对象
2、使用typedef声明提高代码可读性
3、使用有意义的符号常量表示程序逻辑中的字面值 ,const unsigned int buffer_size = 256;
或是使用enum{BUFFER_SIZE = 256};
4、有大小上关系的常量要表示出大小关系,enum{IN_STR_LEN=18, OUT_STR_LEN=IN_STR_LEN+2 };不要将有关系的两个分开,修改一个没注意到另一个也被修改。
5、返回errno错误代码的函数返回值类型声明为errno_t(errno.h)
6、维护变参函数编写者与调用者之间的契约。按照约定好的结束
Va_list args;
Va_start(args, 一个参数);//对实参列表初始化
Va_arg(args, int)
Va_end(args);
7、信息隐藏和数据封装的软件工程原则,使用不透明的数据类型。
8、不对全局作出假设:
Int init()
{
Static int c=0; return c++;
}
Int a=init(); int b=init(); 在同一个头文件中extern int a,b; // a=0;b=1;
9、把不需要外部链接对象声明为static隐藏起来。
10、保证函数传递的指针是不重叠的。
11、对无法缓存数据使用volatile。使编译器不对其进行优化。
第四章 表达式
1、使用括号保证操作的优先级
2、注意&& || 的短路操作,
3、不要转换掉const限定
4、用sizeof确定变量的长度
Int **matrix = (int**)calloc(100,sizeof(int*));
For (i=0;i<100;i++)
{
Matrix[i]=(int*)calloc(i,sizeof(int));
}
5、字段对其:struct bf{unsinged int m1:8,m2:8, m3:8,m4:8};内存中存储取决于编译器
6、不要将指针转换为对其要求更加严格的指针类型,例如将void* 转为int* 从一个字节对齐转为4个字节对齐,可能会异常终止。
7、
第五章
第六章
第七章 数组
数组长度在编译时可知的,C99加入了对变长数组的支持。
1、获取数组长度时不要对指针应用sizeof操作符。Sizeof(array)/sizeof(array[0])
2、保证下表在合法范围内,不要越界与溢出。进行边界检查
3、如果结果值并不引用合法的数组元素,不要把指针加上或是减去一个整数,
Int array[20]; sizeof(array)/sizeof(array[0])
4、
第八章 字符与字符串
1、不要意外截断NULL结尾的字节字符串,通常使用限定字节长度的API,缓解缓冲区溢出的潜在问题。
2、使用普通char类型表示基本字符集字符。Char与unsigned char、signed char具有不同的范围。最好使用普通的char类型。
3、引用字符串常量使用const指针。
不要以为strtok()不修改字符串。会对字符串修改,因此不是一个安全的API,如果需要保留原先的字符串,复制到缓冲区,
4、检查源指针和目标指针是否为NULL,检查目标缓冲区最大长度是否为0,大于Max或是小于Min,重叠部分不许复制。
5、使用托管字符串开发新的字符串操纵代码,使用安全的字符串第三方库,。
6、不要试图修改字符串常量 Char *p = “hello world!”; // p[0] = ‘S’ --->char a[] = “...”; a[0]=’S’;
7、必须在数组最后一个原色包含一个NULL,提供足够空间存储。Const char s[3]=”abc”;字符串长度为4,结尾的‘\0’,导致潜在风险,并没有正确以null结尾。Const char s[] = “abc”;若是字符数组,而不是字符串,char s[3] = {‘a’, ‘b’, ‘c’};
8、字符处理函数最好类型都是unsigned char类型。Const char* p = src;
Isspace((unsigned char)*p)
第九章 内存管理
1、同一个模块、抽象层中分配释放内存。内存管理不当导致多次释放内存或者写入已经释放的内存。
2、free之后立即在指针存储为NULL。
3、及时清除在可复用资源中的敏感信息。释放动态分配的缓冲区之前,必须清除敏感信息,以防信息泄漏。在free之前memset(p,’\0’,size); free(p); p = NULL; calloc()也需要清除。Realloc()同样是,重新分配内存,原内存有保密信息,销毁就对象,返回指向新对象的指针。
4、不要执行零长度的分配。请求空间长度为0的返回值不确定有编译器定义。可谓nuLL也可为非零值。Realloc()返回NULL未释放原内存导致内存泄漏。
5、避免大型的堆栈分配。
6、保证敏感数据不会写入磁盘。防止密码等秘密信息泄漏。
7、保证calloc()参数相乘后可以用size_t表示,如果需要分配的元素数量与存储大小相乘结果如法用size_t表示,分配的将小于请求数量。检查超出边界的结果是否回绕。
Size_t num_element; if (num_elements > SIZE_MAX/sizeof(long)){......}
Long* buffer = (long*)calloc(num_elements,sizeof(long)); if (NULL == buffer){...............}
8、只把realloc()用于改变动态分配数组的大小.销毁旧对象,返回新对象,新对象与旧对象相同部分,超出部分值不确定。
9、不要假设内存分配函数会对内存初始化。Char* buff = (cahr*)malloc(size); if (NULL == buff){...........} strncpy(buff,str,len); buff[len]=’\0’;
10、定义和使用指针验证函数.
Int valid(void *p)
{
Extern char _etext; return ( (p!=NULL) && ((char*) p > & _etext));
}
11、不要访问已经释放的内存,,动态分配的内存只应释放一次,检查内存分配错误。
12、释放并非动态分配的内存会导致严重错误。Malloc()\calloc()\realloc()释放使用free()。
第十章 输入输出
多数情况下不正确的字符串格式化会导致程序异常终止。
1、调用通过文件名标志文件的函数时必须小心
2、对来自不信任来源的路径名进行标准化。
Struct passwd* pwd = getpwuid( getuid() );
strncmp( filename, pwd->pw_dir, strlen(pwd->pw_dir )) == 0
Strrch(filename,’/’) == filename + pwd->pw_dir;
3、文件操作,如果文件存在却创建。 Fopen(file_name,”wx”)//x不破坏现有文件,相当于open()函数的O_EXCL选项。
4、对函数输入输出状态进行检查,并进行正确的处理错误。
5、用fseek()代替rewind(),都是到初始位置,但rewind()还清空流的错误提示符。无法判断是否成功。
6、在打开的文件上调用remove时应该小心。删除一个打开文件考虑使用unlink()。
7、跨系统传输二进制数据要小心。操作系统在结构填充、浮点模型、字节位数等一些差别导致二进制数据格式不兼容。
8、使用rename函数时应该小心。如果目标文件名存在则其行为是由系统定义。
If (access(dest_file,F_OK)!= 0 ){.............}//先判断目标文件是否已经存在,之后在进行重命名。
9、指定fopen的mode参数时应该小心。
10、使用setvbuf()代替setbuf()有返回值可以验证是否成功,
11、理解文件流的文本模式和二进制模式区别。宿主环境中表示文本的 不同约定。例如:“\n”,windows下“\r\n”,
12、保证文件操作在安全目录下执行。
13、通过创建jail限制对文件的访问,chroot()调用后取消超级用户权限。
14、不要用包含用户输入的格式化字符串调用任何格式化I/O函数。
15、检测和处理导致未定义行为的输入输出错误。对执行是否成功或是哪种失败进行检查。
16、当sizeof(int) == sizeof(char)时使用feof()和ferror()检查文件尾和文件错误。
17、不要假设fgets会读取换行符。删除结尾的换行符。Char buf[size+1], 若没复制换行符,追加换行符和‘\0’
18、不要再没有干预刷新或定位调用的情况下在一个流中交替执行输入输出.。没有fflush()、fseek()、或是其他操作,导致未定义的行为。
19、fgets()失败时重置字符串。Fgets()失败数组内容是未定义的。*buf = ‘\0’;
20、保证文件不再需要时及时关闭,迟打开早关闭。
21、不要再共享目录中创建临时文件。