通过对《常见软件编程低级错误》资料的学习,了解到了编码中容易出现错误的地方,并且对代码编写的要求有了更深的认识。文中对“好”代码的要求是我们每个程序员应该达到的目标:“能工作的代码并不等于‘好’代码,‘好’代码的指标很多,包括可读性、可维护性、可移植性和可靠性等。出现网上问题的代码,大多是不良编程习惯引起的,不遵守编程规范的代码,往往也是最不可靠的代码。”
1 1 内存泄露
1、函数的异常处理的出口,没有释放申请的资源。在函数return时,特别是包含有return的宏定义,一定要检查前面有没有申请资源,如果有申请则要考虑资源释放的问题。
2、要删除一个结构体指针时,要检查一下结构体中是否还有指针申请了资源,如果有,则要由底至上一个个删除。
3、数组中的元素如果有申请资源,在释放数组时,要确保这些资源被全部释放。
4、已经分配过内存的指针,在没有释放前,不能再次对其分配内存,尤其是代码拷贝的时候要着重检查。
5、判断申请资源是否成功的条件语句,不要将多个条件或在一起,那样无法判断是由哪些申请资源成功,哪些申请失败。尽量使用简单的条件语句。
6、队列删除其中某个节点,要记得一起删除该节点所申请的内存。
2 2 内存越界
1、定义一个数组时,一定要考虑到数组的最大情况,避免造成越界读写。
2、进行memset,memcpy操作时,长度要考虑到边界,不能越界操作。
3、定义一个字符串时,要接上结束符’\0’,不然在计算该字符串长度时,会出现错误,进而导致该字符串的复制等操作越界。
4、指针的加减运算是根据指针类型进行指针的移动。
5、字符串,结构体等长度,要采用strlen,sizeof等函数来获取,不要人工计算,避免人工计算的错误。
6、外部传入函数的入参要进行判断,入参异常,则返回,不执行后面的语句。
7、如果是一个指针变量,使用sizeof计算的是该指针的大小,而不是指针所指内容的大小。
8、尽量减少编译开关,避免编译开关选择错误。
3 3 野指针
1、指针删除时,要赋值为NULL,其他地方调用该指针后出错。
2、局部变量的使用,当函数执行结束后,局部变量的生命期结束,内存备释放,此时其他函数不能继续调用该变量。
3、函数返回值为一个局部变量的指针,函数结束后该变量已经被释放,该地址已经为其他地方使用,不能继续对该指针进行操作。
4、内存的重复释放,每次释放内存前,加入判断,如果不为空才释放。并且内存的申请和释放最好在同一个作用域里。
4 4 杂项
1、变量溢出,对变量进行加减运算时,必须要考虑变量的类型,是否会产生溢出。
2、atoi,atol,atof函数内部都不检测溢出,最好不使用此类危险函数。
3、参数的类型转换时,要考虑是否会引起数据的截断或改变。
4、定义类型冲突:变量在定义和extern时,类型要相同。
5、Memcpy时,先将目的地址的内存清0,防止有之前的数据。
6、字符串拷贝使用字符串专用的拷贝函数,以便维护人员理解
7、寄存器赋值时,未修改的BIT位要考虑是否需要清0.
8、栈大小的定义足够实际的使用。
9、不使用超大临时变量,这样容易造成栈溢出。
10、可以考虑用指针替换超大的临时变量。
11、减少函数调用层次,慎用递归函数。
12、多任务操作全局变量或链表等数据结构时,要加上保护。
13、子进程如果不需要父进程的资源,要立刻将其关闭
5 5 变量
1、 指针变量没有使用时,初始化要为NULL,不要初始化为其他数字。
2、 确保所有的条件分支不遗漏变量的初始化。
3、 结构体的初始化,要包含所有的成员。
4、 数组的初始化要包含所有的元素,使用正确的函数计算数组长度。
5、 局部变量命名不能和成员变量,全局变量重名。
6、 全局变量的初始化,要考虑初始化的顺序。
7、 使用全局变量和初始化全局变量两者之间的时序关系要考虑清楚。
6 6 表达式
1、 表达式中有多种运算符,要用括号控制计算的次序。
2、 表达式使用默认优先级时,要考虑各种运算符优先级高低
3、 函数的入参不要使用表达式。
4、 ++、--等运算最好是单独使用一个表达式。
5、 Volatile变量运算结果不是预期值,是因为该变量可能被其他程序或硬件改变。
6、 表达式中的嵌套语句,可能导致某些变量被多次赋值
7 7 空指针
1、 内存申请后,要判断是否申请成功,要考虑异常分支的处理。
2、 外部入参传入的参数或是全局变量指针,要判断是否合法。
3、 外部接口传入的指针使用前必须判断是否合法。
4、 判断多个指针是否为空时,要分开判断,不要使用一条判断语句。
5、 ||条件判断中,左边的表达式成立,则不会继续判断右边的了,如果右边有计算则不会执行