Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/list/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 关于编译器及其工作方式的问题_C++_C_Compiler Construction - Fatal编程技术网

C++ 关于编译器及其工作方式的问题

C++ 关于编译器及其工作方式的问题,c++,c,compiler-construction,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; } 但我也遇到过(

这是释放单链表内存的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->next分配给current。但很明显,如果我用其他编译器(例如,书的作者使用的编译器)编译这段代码,程序就会工作。所以问题是,为什么用特定的编译器编译的代码可以工作?是因为编译器把指令放在二进制文件中,记住了当前->下一步的地址,尽管我释放了当前,而我的VC++没有。我只是想了解编译器是如何工作的。

第二个例子是错误的代码-它不应该在被释放后引用当前的
current
。在许多情况下,这似乎是可行的,但这是未定义的行为。使用像
valgrind
这样的工具将清除这样的bug


请引用您在中看到的任何书籍,因为它需要更正。

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

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

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

在执行
释放(当前)
后,
当前
指向的内存(其中存储了
当前->下一个
)已返回到C库,所以你不应该再访问它了

C库可以随时更改该内存的内容-这将导致
current->next
被损坏-但它也可能不会更改部分或全部内容,尤其是在这么短的时间内。这就是为什么它在某些环境中有效,而在其他环境中无效


这有点像开车闯红灯。有时你会侥幸逃脱,但有时你会被卡车辗过。

了解编译器如何工作的最好方法不是询问它们如何处理无效代码。你需要读一本关于编译的书(实际上不止一本)。一个开始的好地方是查看参考资料。

实际上,这是C运行时,而不是编译器。无论如何,后面的代码包含未定义的行为-不要这样做。它在某些实现上对某人有效,但正如您所看到的,它在您的实现上崩溃得很糟糕。它也会悄悄地破坏某些东西

对于后者可能起作用的原因,可能的解释是,在某些实现中,
free()
不会修改块内容,也不会立即将内存块返回到操作系统,因此取消引用指向块的指针仍然是“合法的”,块中的数据仍然完好无损

是因为编译器把指令放在二进制文件中,记住了当前->下一步的地址,尽管我释放了当前,而我的VC++没有

我不这么认为

我只是想了解编译器是如何工作的

下面是一个使用GCC编译器的示例(我没有VC++)

如果它是一个类,并且有一个析构函数,那么在我们使用delete操作符释放对象在内存中占用的空间之前,应该调用它


销毁对象并释放其内存空间后,您不能再引用current->next(当前->下一步)了。

请告诉我们这本书,这样我们就可以避免使用它,并建议不要使用它。C primer(所有版本均为第5版、第4版..此错误无勘误表)查看此主题:C运行时用某种模式填充已释放块的字节会发生什么(MSVC的0xfeeefeee)。但这只发生在调试版本中,如果您在发行版中尝试它,它很可能会起作用。这仍然是一个未定义的行为,请放弃任何推荐此的书。C primer plus(所有版本,第五、第四版…)谢谢-我知道印度大学用于C编程的书非常糟糕(Kanetkar、Balaguruswami等)但我猜问题更普遍。将最后4个空闲的地址放入DR0-DR3寄存器并在所有寄存器上设置读取断点可能更容易。
struct film { film* next; };

int main() {
  film* current = new film();
  delete current;

  return 0;
}

;Creation
movl    $4, (%esp)   ;the sizeof(film) into the stack (4 bytes)
call    _Znwj        ;this line calls the 'new operator' 
                     ;the register %eax now has the pointer
                     ;to the newly created object

movl    $0, (%eax)   ;initializes the only variable in film

;Destruction
movl    %eax, (%esp) ;push the 'current' point to the stack
call    _ZdlPv       ;calls the 'delete operator' on 'current'