写C或C++代码时,指针用得好如虎添翼,用不好就是定时炸弹。尤其是半夜改bug,崩溃信息指向某个内存地址,心里一凉——八成又是野指针或者空指针访问了。
打印指针值,先看它指向哪
最简单的办法反而最有效。在关键位置打印指针的值,确认它是否为NULL,或者是否指向了意料之外的地址。
int *ptr = get_data();
printf("ptr 地址: %p\n", (void*)ptr);
if (ptr != NULL) {
printf("ptr 所指值: %d\n", *ptr);
}
别小看这两行输出,很多时候你会发现指针根本没被正确初始化,或者函数返回了栈上局部变量的地址。
用GDB查看内存内容
遇到段错误,直接上GDB。运行程序触发崩溃后,用print和x命令查看内存。
(gdb) print ptr
(gdb) x/4xw ptr # 查看ptr指向的4个32位字
比如你发现指针指向0x0或者0x1,基本可以断定是未初始化或释放后使用。如果看到一堆0xcdcdcdcd,可能是VS的调试填充,说明内存已经被释放。
警惕数组越界引发的指针偏移
有个经典场景:结构体数组后面紧跟着一个指针,结果前面数组越界写入,把后面的指针给覆盖了。
struct Item {
int id[5];
int *flag;
};
struct Item item;
item.id[5] = 99; // 越界!可能破坏flag指针
这种问题不容易复现,但一旦发生,flag指针就会变成随机值。调试时可以用AddressSanitizer编译选项,它能及时报出越界访问。
动态分配后立即检查
malloc可能失败,尤其在嵌入式环境。每次分配完都得检查是否为空。
int *arr = (int*)malloc(sizeof(int) * 100);
if (arr == NULL) {
fprintf(stderr, "内存分配失败!\n");
return -1;
}
别觉得这步多余,很多线上故障都是因为忽略了这个“极小概率”事件。
释放后置空,避免二次释放
free之后不把指针设为NULL,后续再free一次就会出大事。
free(ptr);
ptr = NULL; // 关键一步
团队里有个老手,所有free都写成宏:#define SAFE_FREE(p) do { free(p); p = NULL; } while(0),省事又安全。
用工具辅助,别只靠肉眼
Valgrind、AddressSanitizer这些工具不是摆设。加个编译选项-fsanitize=address,很多隐晦的内存问题当场暴露。
有次同事查了一个星期的随机崩溃,打开ASan一跑,三分钟定位到是指针多释放了一次。工具用对了,效率翻倍。