2009-06-10 25 views
3

許多年輕程序員都這樣做,我學會了在代碼中的不同點插入大量「here1」,「here2」等打印到控制檯語句的用處弄清楚我的程序何時會出錯。這種強力調試技術爲我的CS研究節省了很多次。但是,當我開始用C編程時,我偶然發現了一個有趣的問題。如果我試着運行C編程:seg故障,printf和相關的怪癖

void* test; 

printf("hello world"); 
test[5] = 234; 

當然,我得到了一個段錯誤,用於沒有爲malloc測試testChar的內存。然而,從邏輯上來說,在seg故障發生之前會打印出「hello world」,因爲這是代碼的流向,但根據我的經驗,seg故障總是首先發生,而「hello world 「永遠不會被打印到控制檯上。 (我無法測試這個確切的例子,但是我已經在Linux機器上多次使用gcc遇到過這種情況)。我猜這與編譯器重新安排一些事情和/或printf使用某種不同步刷新的緩衝區,因此不會立即生效。這完全是我的猜測,因爲我真的不知道爲什麼會發生。在我使用的任何其他語言中,無論「testChar = ...」行產生什麼問題,「hello world」都會被打印出來,因此我可以確定問題所在。

我的問題是爲什麼當我編程C時會發生這種情況?爲什麼不是先打印出你好的世界?其次,是否有比此更好的C編程調試技術來實現相同的基本功能?在中,一種簡單/直觀的方式來查找是問題的代碼行?

編輯:我偶然發現了一個實例哈哈。我現在擁有的應該會導致段錯誤。這很有趣,通常當我不要想要一個segfault我得到一個,現在當我真的想要一個我寫法律代碼!

回答

9

您發佈的代碼是完全合法的,不應該導致段錯誤 - 不需要malloc任何東西。您的問題必須在別的地方 - 請發佈導致問題的最小代碼示例。

編輯:您現在編輯的代碼具有完全不同的含義。但是,「hello world」沒有顯示的原因是輸出緩衝區沒有被刷新。嘗試addinig

fflush(stdout); 

printf後。

關於定位問題的根源,你有兩個選擇:

  • 從優通過您的代碼使用__FILE____LINE__ C宏
  • 學習使用調試器灑printfs輸出 - 如果你的平臺支持核心轉儲,您可以使用核心映像找到錯誤的位置。
+0

我曾經這麼想過。*儘管我正在精神上,但會劃傷頭部。 – 2009-06-10 13:10:59

+0

對不起,大聲笑,我解決了我的代碼,這是一個真正的問題。我需要任何會導致段錯誤的東西。 – JoeCool 2009-06-10 13:11:34

+0

「哦,這是壞的廢話」將被存儲在符號區(?)中,並且指針被存儲在testChar中...您可以使用調試器查看它。 – stefanB 2009-06-10 13:12:06

1

此代碼不應該段錯誤。你只是將一個指向字符串的指針賦給一個指針變量。如果你是例如,情況會有所不同使用strcpy複製帶有無效指針的東西。

未顯示的消息可能是由於緩衝的I/O。打印換行符\n或致電fflush刷新輸出緩衝區。

0

你有兩個問題。首先是你的(原始)代碼不會段錯誤。將該字符串常量分配給char指針是完全有效的。但現在讓我們暫時擱置一下,假裝你在那裏放了段錯誤。

然後它通常是一個緩衝區,一個在C運行時庫和一個在操作系統本身。你需要刷新它們。

要做到這一點是(在UNIX中,沒有完全確定有關Linux中的fsync,但你要保證,除非系統本身關閉此西港島線最終會發生),最簡單的方法:

printf ("DEBUG point 72\n"); fflush (stdout); fsync (fileno (stdout)); 

我在UNIX中經常這樣做,它確保將C運行時庫刷新到UNIX(fflush),並將UNIX緩衝區同步到磁盤(fsync),如果stdout不是終端設備,或者您正在爲一個不同的文件句柄。

5

printf寫入標準輸出,它被緩衝。有時候,在你的程序崩潰之前,這個緩衝區不會被刷新,所以你永遠不會看到輸出。有兩種方法可以避免這種情況:

  1. 使用fprintf(stderr, "error string");因爲stderr未被緩衝。
  2. 在printf調用後添加對fflush(stdout);的調用。

正如Neil和其他人所說的那樣,編寫的代碼很好。也就是說,直到您開始修改testChar指向的緩衝區。

2

輸出在默認情況下被緩衝,segfault發生在輸出實際寫入stdout之前。嘗試:

(錯誤默認情況下未緩衝。)

3

「作爲,一個簡單/直觀的方式找到的代碼行是一個問題嗎?」

使用gdb(或任何其他調試器)。

要找到你的程序賽格故障您-g選項編譯(包括調試符號)從GDB運行應用程序,它會停在賽格故障。

然後,您可以使用bt命令查看回溯,以查看您在哪一點得到了seg故障。

例如:

> gdb ./x 
(gdb) r 
Starting program: /proj/cpp/arr/x 
Program received signal EXC_BAD_ACCESS, Could not access memory. 
Reason: KERN_PROTECTION_FAILURE at address: 0x00000000 
0x000019a9 in willfail() at main.cpp:22 
22   *a = 3; 
(gdb) bt 
#0 0x000019a9 in willfail() at main.cpp:22 
#1 0x00001e32 in main() at main.cpp:49 
(gdb) 
0
void* test; 

printf("hello world"); 
test[5] = 234; 

其可能的「Hello World」是由系統緩存的地方,並沒有立即顯示在屏幕上。它的存儲等待一個進程/線程/任何負責屏幕寫入的機會,都有機會處理它。而它的等待(並可能緩衝其他數據輸出),你的功能正在完成。它來自非法訪問和段錯誤。