在我的测试中,我发现可以在free()之后使用指针。我有以下代码:
typedef struct{
int module_id;
int adc_id;
struct config_line * pnext;
} config_line;
config_line * create_list()
{
config_line * phead = (config_line *) malloc(sizeof(config_line));
phead->pnext=NULL;
phead->module_id = 1;
phead->adc_id = 2;
printf("module_id=%d adc_id=%d\n",phead->module_id, phead->adc_id);
free(phead);
printf("module_id=%d adc_id=%d\n",phead->module_id, phead->adc_id);
phead->module_id = 2;
phead->adc_id = 5;
printf("module_id=%d adc_id=%d\n",phead->module_id, phead->adc_id);
}
此代码的输出为:
module_id=1 adc_id=2
module_id=0 adc_id=2
module_id=2 adc_id=5
为什么在free(phead)之后我可以访问(读写)指针?为什么没有分段错误?
答案 0 :(得分:18)
因为使用无效指针会调用未定义的行为。这意味着行为......好...... 未定义。 强制崩溃。
答案 1 :(得分:9)
当您致电free(phead)
时,指向的内存phead
正在被释放,但phead
的值保持不变,使phead
成为悬空指针。访问已释放的内存会产生未定义的行为。
一旦释放指向的内存,将NULL
指定给指针是一个好习惯:
free(phead);
phead = NULL;
答案 2 :(得分:4)
因为内存仍然映射到您的进程,但未分配。在C程序中有两个级别的内存管理:首先,内核为您提供可以写入的页面。如果进程需要更多已映射的内存,则必须从内核(sbrk)请求更多内存。尽管如此,它还是有很大的块,因此malloc会将它分成几块,根据需要请求更多的页面,但是尽可能使用已分配给程序的内存。在释放使用它的所有分配之前,free无法返回页面。
因此,内核为您提供的内存访问违规(SIGSEGV)相当粗糙,并且无法从内核的角度来看大多数内存错误。您可以随意删除malloc的跟踪数据,读取大多数分配结束后以及许多自由之后,等等。
不要提交内存错误。使用valgrind运行你的应用程序,它使用非常严格的VM来检查所有错误并立即压缩它们。
答案 3 :(得分:4)
因为释放内存(指针)只是将内存放回空闲内存池。内存不会消失,直到再次分配内存并写入内存时才会清除内容。
因此,在释放内存后,很有可能访问内存。你可以想象的不是一个好主意,因为它可以随时分配并修改。
答案 4 :(得分:2)
为什么一只鸡会继续奔跑跳跃,尽管它被砍掉了?请注意,这不会始终发生;有些鸡可能会立即停止移动,others might continue to run around for months。当它确实发生时,它会发生,因为它发生了。这是否意味着我们应该切断我们的宠物头?
就像削减我们的宠物头一样,使用已被释放的记忆是一个坏主意。这并不意味着它会产生负面结果;您的程序可能会继续在您的计算机上按预期运行,而if (fubar)
(其中 fubar 是您的无效指针)可能足以导致程序在其他实施。
这种实现自由定义(或不定义)行为的自由被正式称为 undefined behavior ;行为是未定义的,因为C标准是一组文件,规定了C实现应该如何表现,没有定义行为。因此,就像切断宠物头一样,我们应该避免所有未定义的行为。