2011-05-21 27 views
1

我一直在寻找所有的互联网上的这个问题的答案(见主题的帖子)。我被问了两次确切的问题。一旦参加了公司的面试,并且曾经是朋友,我无法找到我的生活的答案。为什么printf()会阻止发生崩溃?

在没有调试器的情况下调试时,实际上遇到过多次这种错误,并且只是使用print语句来隔离错误。我无法回想任何确切的情况,尽管我已经体验过它。如果任何人都可以提供链接或引用,或者指向我使用print语句调试代码时可能导致错误停止的printf()源代码中的某些内容,我将非常感谢良好的阅读。

谢谢 马修Hoggan

我目前正在读所提供的链接,但进一步的谈话我已经发布了一些我弱的尝试以调查:

好了,我已经开始对自己身边玩尝试回答我自己的问题,但事情仍然没有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; 
} 

回答

7

添加printf()可以改变错误的行为有几个原因。一些更常见的可能是:

  • 改变执行的定时(特别是用于螺纹错误)
  • 改变存储器使用模式(编译器可能改变栈是如何使用)
  • 改变如何寄存器被使用

例如,未初始化的局部变量可能被分配给一个寄存器。在添加printf()之前,使用未初始化的变量并获取寄存器中的垃圾值(可能是之前调用rand()的结果,因此它确实是不确定的)。添加printf()会导致在printf()printf()中使用该寄存器总是会将该寄存器设置为0(或其他)。现在你的越野车程序仍然是bugy,但具有不同的行为。也许这种行为恰好是温和的。

+0

除了Michael Burr所说的之外,请参阅本文中有关使用printf()作为调试方法的缺点:http://www.oopweb.com/CPP/Documents/DebugCPP/Volume/techniques。html – yasouser 2011-05-21 15:41:04

+0

谢谢你的好阅读yasouser。我之前已经应用了一些这些调试技术。如果我进行输出调试,我倾向于赞成像流一样的非缓冲输出。我现在只进行了大约3年的编程,而当我第一次启动时,遇到了“printf()”错误。 – 2011-05-23 06:00:30

+1

另一件事是'printf'是编译器一无所知的函数调用。因此,编译器假定它可能会修改任何可能知道其地址的变量。这意味着寄存器副本在调用'printf'之前刷新并在其后重新加载。这可能会在多线程代码中导致显着的行为差异。 (它就像一个记忆障碍。) – 2011-08-26 00:13:49

1

我以前见过它,例如在Java中,在情况下,当另一个线程试图访问对象认为具有初始化代码不完整已经被创建。 System.out.println()减慢了另一个线程,足以完成初始化。