这是从单链表中释放内存的C代码。它是用Visual C++ 2008编译的,代码可以正常工作。
/* Program done, so free allocated memory */
current = head;
struct film * temp;
temp = current;
while (current != NULL)
{
temp = current->next;
free(current);
current = temp;
}但我也遇到过(甚至在书中)写成这样的相同代码:
/* Program done, so free allocated memory */
current = head;
while (current != NULL)
{
free(current);
current = current->next;
}如果我用VC++ 2008编译代码,程序就会崩溃,因为我首先释放current,然后将current ->赋值给current。但很明显,如果我用其他编译器(例如,书作者使用的编译器)编译这段代码,程序就可以工作。所以问题是,为什么这个代码是用特定的编译器编译的?是不是因为编译器在二进制文件中放入了记住current->next地址指令,尽管我释放了current而我的VC++没有,我只是想了解编译器是如何工作的。
发布于 2010-04-14 18:30:37
第二个程序调用未定义的行为。这不是编译器的不同,而是C标准库和free()函数实现的不同。编译器会将指针current存储为局部变量,但不会存储它所引用的内存的副本。
当您调用free()时,您将放弃传递给free()函数的指针所指向的内存的所有权。有可能在您放弃所有权之后,指向的内存的内容仍然是合理的,并且仍然是您的进程地址空间中的有效内存位置。因此,访问它们可能看起来像是在工作(请注意,您可以通过这种方式默默地损坏内存)。非空的指向已经释放的内存的指针被称为dangling pointer,这是非常危险的。仅仅因为它看起来是有效的并不意味着它是正确的。
我还应该指出的是,实现free()的方式可以捕获这些错误,比如每次分配都使用单独的页面,并在调用free()时取消页面映射(这样内存地址就不再是该进程的有效地址)。这样的实现效率很低,但有时某些编译器在调试模式下会使用它来捕获悬空指针错误。
发布于 2010-04-14 18:31:42
在执行free(current)之后,current指向的内存(存储current->next的地方)已经返回到C库,因此您不应该再访问它。
C库可以随时更改内存的内容-这将导致current->next被损坏-但它也可能不会更改部分或全部内存,特别是在这么快的时候。这就是为什么它可以在某些环境中工作,而不是在其他环境中工作。
这有点像开车闯红灯。有时你会逃脱惩罚,但有时你会被一辆卡车轧过。
发布于 2010-04-14 18:33:39
了解编译器如何工作的最好方法是不要询问它们是如何处理无效代码的。你需要阅读一本关于编译的书(实际上不止一本)。一个不错的入门位置是查看Learning to write a compiler上的资源。
https://stackoverflow.com/questions/2636563
复制相似问题