Debugging 为什么printf()可以阻止崩溃的发生?

Debugging 为什么printf()可以阻止崩溃的发生?,debugging,visual-c++,Debugging,Visual C++,我一直在互联网上寻找这个问题的答案(见帖子主题)。这个问题我已经被问过两次了。一次是公司面试,一次是朋友面试,我一生都找不到答案 事实上,在没有调试器的情况下进行调试,并且仅使用print语句隔离错误时,我多次遇到此错误。我记不起任何确切的情况,尽管我肯定我经历过。如果有人能提供链接或引用,或指向printf()源代码中的某些内容,这些内容在使用print语句调试代码时可能会导致错误停止发生,我将非常感谢您的阅读 谢谢,, 马修·霍根 我目前正在阅读提供的链接,但为了进一步交流,我已经发布了一些

我一直在互联网上寻找这个问题的答案(见帖子主题)。这个问题我已经被问过两次了。一次是公司面试,一次是朋友面试,我一生都找不到答案

事实上,在没有调试器的情况下进行调试,并且仅使用print语句隔离错误时,我多次遇到此错误。我记不起任何确切的情况,尽管我肯定我经历过。如果有人能提供链接或引用,或指向printf()源代码中的某些内容,这些内容在使用print语句调试代码时可能会导致错误停止发生,我将非常感谢您的阅读

谢谢,, 马修·霍根

我目前正在阅读提供的链接,但为了进一步交流,我已经发布了一些我薄弱的调查尝试:

好的,所以我已经开始玩我自己,试图回答我自己的问题,但事情对我来说仍然不是100%清楚。下面是g++编译器使用-S选项输出程序集而不是可执行文件的输出。同样的C++代码也会在下面发布。我的目标是尝试并重新创建一个简单的场景,然后尝试并根据指令检测处理器级别可能发生的情况。因此,让我们假设在“call printf”汇编代码(我假设它是从存储在/usr/lib或另一个lib目录中的库文件链接而来)之后,我尝试访问一个空指针(不在代码中),或者一些传统上会使程序崩溃的其他形式的操作。我假设我必须找出printf在指令方面做了什么,才能更深入地了解这一点

.file   "assembly_test_printf.cpp"

        .section    .rodata

.LC0:

    .string "Hello World"

    .text

.globl main

    .type   main, @function

main:

.LFB0:

    .cfi_startproc

    .cfi_personality 0x0,__gxx_personality_v0

    pushl   %ebp

    .cfi_def_cfa_offset 8

    movl    %esp, %ebp

    .cfi_offset 5, -8

    .cfi_def_cfa_register 5

    andl    $-16, %esp

    subl    $32, %esp

    movl    $0, 28(%esp)

    movl    $.LC0, (%esp)

    call    printf

    movl    28(%esp), %eax

    leave

    ret

    .cfi_endproc

.LFE0:

    .size   main, .-main

    .ident  "GCC: (Ubuntu/Linaro 4.4.4-14ubuntu5) 4.4.5"

    .section    .note.GNU-stack,"",@progbits

等效C++代码:

#include <stdio.h>

int main ( int argc, char** argv ) {

    int x = 0;

    printf ("Hello World"); 

    return x;
}
#包括
int main(int argc,字符**argv){
int x=0;
printf(“你好世界”);
返回x;
}

我以前见过,例如在Java中,当另一个线程尝试访问假定已创建的对象时,初始化代码未完成。System.out.println()会减慢另一个线程的运行速度,足以完成初始化。

添加
printf()
可以改变bug的行为。一些更常见的可能是:

  • 更改执行时间(特别是线程错误)
  • 更改内存使用模式(编译器可能会更改堆栈的使用方式)
  • 更改寄存器的使用方式

例如,未初始化的局部变量可能被分配到寄存器。在添加
printf()
之前,使用未初始化的变量并获取寄存器中的垃圾值(可能是先前调用
rand()
的结果,因此它实际上是不确定的)。添加
printf()。现在,您的buggy程序仍然是buggy,但具有不同的行为。也许这种行为恰好是良性的。

除了Michael Burr所说的,请参阅这篇关于使用printf()作为调试方法的缺点的文章:感谢您的好书yasouser。我以前应用过其中一些调试技术。如果我进行输出调试,我倾向于像流这样的非缓冲输出。我现在只做了大约3年的编程,当我第一次开始编程时遇到了“printf()”错误。另一件事是,“printf”是一个编译器不知道的函数调用。因此,编译器假设它可能修改其地址可能已知的任何变量。这意味着在调用“printf”之前刷新寄存器副本,并在调用之后重新加载。这可能会导致多线程代码中存在显著的行为差异。(它就像一个记忆屏障。)