悬垂指针
悬垂指针(dangling pointer)是指向已释放内存的指针。使用悬垂指针会导致未定义行为——可能读取垃圾数据、破坏其他数据结构,或导致程序崩溃。悬垂指针是 C 语言中最隐蔽、最难调试的错误之一。
典型场景
释放后未置 NULL:
int *p = malloc(100);
free(p);
/* p 现在是悬垂指针 */
*p = 10; /* 未定义行为! */
函数返回局部指针:
int *bad_func(void)
{
int local = 10;
return &local; /* 返回局部变量地址 */
}
int *p = bad_func();
printf("%d\n", *p); /* 未定义行为 */
重复释放后的指针:
int *p = malloc(100);
free(p);
free(p); /* 双重释放,p 悬垂 */
检测悬垂指针
释放后置 NULL:
#define SAFE_FREE(p) do { free(p); (p) = NULL; } while(0)
int *p = malloc(100);
SAFE_FREE(p);
/* p 为 NULL,后续 *p 会崩溃(容易发现)而非静默错误 */
调试模式填充:
#ifdef DEBUG
#define FREE(p) do { \
memset((p), 0xDE, malloc_usable_size(p)); \
free(p); \
(p) = NULL; \
} while(0)
#else
#define FREE(p) do { free(p); (p) = NULL; } while(0)
#endif
预防策略
所有权明确:
/* 谁分配,谁释放 */
void process(void)
{
int *data = malloc(100);
/* 使用 */
free(data); /* 同作用域释放 */
}
引用计数:
struct Object {
int refcount;
int data;
};
struct Object *create(void)
{
struct Object *obj = malloc(sizeof(struct Object));
obj->refcount = 1;
return obj;
}
void retain(struct Object *obj) { obj->refcount++; }
void release(struct Object *obj)
{
if (--obj->refcount == 0)
free(obj);
}
最佳实践
- 释放后立即置
NULL - 避免返回局部变量地址
- 明确内存所有权
- 复杂场景用引用计数或智能指针
- 使用 AddressSanitizer 检测