2013-03-15 76 views
4

在我的测试中,我发现可以在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 

为什么免费(PHEAD)我可以访问(读取和写入)的指针?为什么没有分段错误?

+11

取消引用free'd指针是_undefined behavior_。它很可能不会崩溃,因为它仍然指向一些真实的记忆,但内容可能不是您所期望的。总之,不要这样做! – 2013-03-15 12:12:22

回答

17

因为使用无效指针会调用未定义的行为。这意味着行为是......嗯...... 未定义。这不是责任崩溃。

+0

On调试在VS运行时构建通常会用无效的内容覆盖内存,所以更容易发现错误。尽管如此,它并不在发布版本上。 – sashoalm 2018-02-16 09:12:36

9

当你打电话时被释放free(phead),内存phead点,但phead停留值不变,使得phead一个悬摆指针。访问已释放的内存会产生未定义的行为

这是分配NULL指针一个很好的做法,一旦你释放它指向内存:

free(phead); 
phead = NULL; 
+2

分配NULL ...不会总是有所帮助,所以它更隐蔽,这可能会影响另一个错误。 (head = malloc(); list = head; free(head); head = NULL; => list仍然指向free'ed memory) – 2013-03-15 12:31:26

+2

@PeterMiehle:我不是说指定'NULL'将解决世界上所有的问题和所有的程序员都会过上幸福的生活。我只是说如何避免创建额外的悬挂指针是一种很好的做法。 – LihO 2013-03-15 12:37:00

+0

@PeterMiehle什么?如果你将'NULL'赋给'head',它不会指向'free()'d内存...... – 2013-03-15 12:45:19

4

由于内存仍然映射到你的进程,但不分配。 C程序中有两级内存管理:首先,内核为您提供可写入的页面。如果进程需要更多内存,它必须从内核请求更多内存(sbrk)。尽管如此,malloc会将它切片成块,根据需要请求更多的页面,但会使用已分配给程序的可能内存。直到所有使用它的分配都被释放后,free才能返回一个页面。因此,内核给你的内存访问冲突(SIGSEGV)是相当粗糙的,并且不能从内核角度挑选大多数内存错误,而只能是致命的内存错误。您可以自由地清理malloc的跟踪数据,读取大部分分配的结尾以及多次释放之后,依此类推。

尽管不曾提交内存错误。使用非常严格的虚拟机valgrind运行您的应用程序来检查所有错误并立即压扁它们。

2

为什么鸡可以继续奔跑,尽管它的头被切断了,还是会跳来跳去?请注意,这并不是总是发生;有些鸡可能立即停止移动,而others might continue to run around for months。当它发生时,它发生是因为它发生。这是否意味着我们应该把我们的宠物关掉?

就像切割我们的宠物一样,使用已释放的内存是一个坏主意。这并不意味着它会产生负面结果;您的程序可能会继续按预期在您的机器上运行,而if (fubar)(其中farar是您的无效指针)可能足以导致您的程序在其他实现中发生故障。

这个想法自由的实现来定义(或不​​)行为正式名为未定义的行为;行为是未定义的,因为C标准(一组文档规定了C实现应该如何表现),未定义行为。因此,就像切断我们的宠物头,我们应该避免全部未定义的行为。

+1

“为什么鸡可以继续奔跑,尽管它的头被切断了,还是会跳来跳去?”这是我从整个页面学到的唯一的东西!! – 2013-03-15 14:16:18

4

因为释放内存(指针)会将该内存放回空闲内存池。内存不会消失,内存不会被清除,直到内存再次分配并写入时为止。

因此,在释放内存之后访问内存是非常有可能的。就像你想象的那样,不是一个好主意,因为它可以随时分配和修改。

+0

“记忆不仅仅是消失,并且它的内容没有清理“不是由C指定的。它可能被清除 - 它可能不会。 – chux 2017-09-14 02:37:37