# 内存泄漏
1、对动态内存的使用保持敬畏,一旦malloc过,free要谨记在心。
尤其是对于多重提前return的函数,如:
1 void func(void) 2 { 3 char *ptr = malloc(100); 4 ... 5 if (condition1) { 6 ... // !!! 记得free 7 return; 8 } 9 10 ... 11 if (condition1) { 12 ... // !!! 记得free 13 return; 14 } 15 16 // !!! 记得free 17 }
2、减少动态内存的嵌套使用,如:
1 struct item_ctx { 2 char *ctx; // dynamic alloc 3 } 4 5 struct item { 6 int item_desc; 7 struct item_ctx *ctx; // dynamic alloc 8 } 9 10 struct queue { 11 int size; 12 struct item *data; // dynamic alloc 13 }
这里层层嵌套,使用完很容易漏掉某个地方的内存释放。
3、釜底抽薪之策:尽量不用动态内存。
比如,如果一项业务最大使用内存是知道的,就可以使用栈内存。如此,既避免了内存泄漏,又有效率方面的提升。
4、智能指针
# 死锁
死锁的避免措施,在思想上和预防内存泄漏是一样的。重复一遍就是:
1、lock/unlock成对出现
2、避免锁的嵌套
3、设计阶段,尽量避免锁的使用
4、智能锁
# for/while语句导致死循环
这是一个比较容易忽视、一旦出现(尤其大型软件)很难排查的问题,一个简单示例:
1 uint32_t g_counter; 2 3 while (g_counter > 0) { 4 g_counter --; 5 ... 6 }
如果 g_counter 减到0的瞬间,又被外部某段代码减了一次,bug就这么发生了。
# system()系统调用
在使用system/popen执行系统命令的时候,注意参数可能带空格的情况,如:
1 system("wpa_passphrase ssid passwd_1234");
上述命令根据用户的WiFi热点"ssid"对明文密码"passwd_1234"进行加密,如果"ssid"带空格的话就悲剧了。
解决措施:在构建命令的时候,记得用引号("")圈起来。
# errno的误用
一种常见的 errno 错误使用示例
if (somecall() == -1) { printf("somecall() failed "); if (errno == ...) { ... } }
errno 是一个全局变量,当 if (errno == ...) 的时候,errno 的值可能已经被其他代码修改掉了。
正确的做法是,在 somecall() 调用异常时,尽快保存 errno 的值:
if (somecall() == -1) { int errbk = errno; printf("somecall() failed "); if (errbk == ...) { ... } }
# 宏定义使用
1、宏定义时记得用括号()引起来。
2、编码过程中如果修改了宏,一定要 rebuild (make clean;make)你的工程。
# 使用变量前一定要初始化
示例:
char buffer[32]; // 错误做法!!! strncpy(buffer, src, sizeof(buffer) - 1); // 正确做法 memset(buffer, 0, sizeof(buffer)); strncpy(buffer, src, sizeof(buffer) - 1);
# 字节对齐
涉及结构体成员大小的时候,注意字节对齐问题。如:
struct msgq_data { pid_t pid; size_t len; }
在 64 位系统上,pid_t 类型占 4 字节大小,size_t 类型占 8 字节大小,编译器按照 8 字节对齐,所以 pid 成员也是 8 字节大小。
待续。。。