我对C中的free()函数的行为提出了一般性问题。
我是否正确地假设通过调用free()
它只是释放指针指向的内存,但保留指针完整?
换句话说,如果我想重用指针,那么指针不需要重新初始化,只需重新分配内存?
例如,这段代码是否合法?
void foo()
{
data_t *x;
int y = 0;
while(x->data != some_value)
{
x = some_function(y);
y = some_other_function(x);
free(x);
}
}
void foo()
{
data_t *x;
int y = 0;
x = some_function(y);
while(x->data != some_value)
{
y = some_other_function(x);
free(x);
x = some_function(y);
}
}
忽略实际代码(以及data_t
的内部结构)并假设some_function()
正确malloc()
s x
;我只是想知道这是一种管理内存的正确方法,还是我需要在每次免费后重新初始化data_t *x
?
编辑:我编辑了代码,因为我意识到它没有正确说明我的问题。
答案 0 :(得分:2)
释放x
然后转到下一个while
检查会导致未定义的行为。
因为在下一次while
检查中,您创建了while(x->data != some_value)
并且x已经被释放
在进行下一次x
检查之前,您必须将while
分配给下一个值
BTW:即使在开始时(在第一次检查中),您也有未定义的行为,因为x
未在while循环之前启动到内存区域。< / p>
基于问题编辑
新案例中的free(x)
无效。
当你释放x
时,x指针保留指向x的旧内存地址但是内存不再分配它是免费的。
在空闲后将新分配的内存分配给x时,x将指向新的内存区域
答案 1 :(得分:1)
是的,修改后的代码版本是管理内存的正确方法。无需再次重新初始化x。只需要确保释放x后,永远不要使用它。但是,您可以再次为其重新分配一些内存并使用它。
答案 2 :(得分:1)
它只是释放指针指向的内存,但是指针保持不变?
这是对的。如果free()是普通的C函数,则它无法影响传递给它的指针值。由于C通过值传递参数,free()接收传递给它的指针的副本,并且它不能改变原始的x指针。 (它可以改变x点到)
换句话说,如果我想重用指针,那么指针不需要重新初始化,只需重新分配内存?
free(x);
x = some_function(y);
这正是你在这里所做的,你将x分配给其他东西,所以这就是人们通常会做的事情。 指针包含一个地址,并为其分配另一个地址。
这与以下概念相同:
int x
x = function_that_returns_int();
x = function_that_returns_int();
您只需为i
分配新值,在为其分配新值之前,无需对i
执行任何其他操作。
答案 3 :(得分:1)
根据ISO C标准中的抽象语言定义,在对来自分配器的有效非空指针调用free
之后,该指针的值变为 indeterminate 。对该值的任何使用都是未定义的行为。不确定的值不能再次生效。
在C的实际实现中,程序中存在的不确定指针值可以在没有检测的情况下传递和评估,当内存被回收到新分配的对象时,它们与有效指针无法区分,从而导致各种问题。
这并不违反规范,因为任何行为都属于未定义行为的范畴。
如果编写的程序行为可靠,但依赖于通过内存分配复活的悬挂指针,则该程序根据ISO C没有明确定义,并且根据您的不太可能很好地定义编译器或操作系统文档,或者。
已编译的程序今天可以正常工作,但明天,libc.so
中的malloc代码会更新,因此内存的回收方式会有所不同,而且会中断。
测试程序产生预期的行为很重要,但这不是完整的故事。行为还必须依赖于语言规范,系统和工具文档的一些保证,或者失败,某些理性的推理让你对程序的正确运行负责。
后者的一个例子是,有时我们可以在语言级别滥用C编译器以获得一些所需的机器代码。虽然这不是由ISO C定义的,甚至可能不是由编译器开发人员定义的,但是一旦你拥有了机器代码,那么在某种意义上说你如何得到它并不重要;它可以独立检查,看它做你想要的。您可以对此负责,并设置回归保护措施,以监控该代码以进行不必要的更改(当编译器以不同方式运行或升级或其他任何操作时)。
对于malloc
和free
的行为,你不能轻易承担这种责任,这是一个运行时黑盒子,它的行为可以在程序部署后很长时间内改变。< / p>
答案 4 :(得分:0)
while条件检查无效,因为x没有指向任何已分配的内存。因此它会导致一些垃圾值。但是,如果代码进入while循环,则x被分配一些内存然后被释放。所以x现在变成了悬空指针。在下一次检查时它也会包含一些垃圾值。在某些情况下,如果指针试图访问属于其他进程的某些数据,甚至会导致分段错误。