2011-05-21 28 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()減慢了另一個線程,足以完成初始化。