关于编译器及其工作原理的问题

don*_*too 12 c c++ compiler-construction

这是释放单链表的内存的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;
}
Run Code Online (Sandbox Code Playgroud)

但是我也遇到过(甚至在书中)相同的代码:

/* Program done, so free allocated memory */
current = head;
while (current != NULL)
{
    free(current);
    current = current->next;
}
Run Code Online (Sandbox Code Playgroud)

如果我使用VC++ 2008编译该代码,程序崩溃是因为我首先释放当前的电流,然后分配current-> current.但显然如果我用其他编译器(例如,书籍作者使用的编译器)编译此代码,程序将起作用.所以问题是,为什么用特定编译器编译的代码工作?是因为编译器将指令放在记住current-> next的二进制文件中,尽管我释放了当前的而我的VC++却没有.我只想了解编译器的工作原理.

Mic*_*yan 18

第二个程序正在调用未定义的行为.它与编译器没有区别,而是C标准库和函数free()的实现差异.编译器将指针存储current为局部变量,但不会存储它引用的内存副本.

当你调用free()时,你放弃了传递给free()函数的指针所指向的内存的所有权.在放弃所有权后,指向的内存内容仍然是合理的,并且仍然是进程地址空间中的有效内存位置.因此,访问它们可能会起作用(请注意,您可以通过这种方式静默地破坏内存).指向非空并指向已经放弃的内存的指针称为悬空指针,非常危险.仅仅因为它看似有效并不意味着它是正确的.

我还应该指出,有可能以捕获这些错误的方式实现free(),例如每次分配使用单独的页面,并在调用free()时取消映射页面(以便内存地址为不再是该过程的有效地址).这样的实现非常低效,但是在调试模式下有时会被某些编译器用来捕获悬空指针错误.


caf*_*caf 11

执行此操作后free(current),current(current->next存储的位置)指向的内存已返回到C库,因此您不应再访问它.

C库可以随时更改该内存的内容 - 这将导致current->next损坏 - 但它也可能不会更改部分或全部内容,特别是很快.这就是为什么它适用于某些环境,而不是其他环境.

这有点像驾驶红色交通灯.有时你会逃脱它,但有时候你会被一辆卡车碾过.