2012-07-09 105 views
19

剛剛被檢查在gdb以下幾點:C類優化

char *a[] = {"one","two","three","four"}; 
char *b[] = {"one","two","three","four"}; 
char *c[] = {"two","three","four","five"}; 
char *d[] = {"one","three","four","six"}; 

,我得到以下幾點:

(gdb) p a 
$17 = {0x80961a4 "one", 0x80961a8 "two", 0x80961ac "three", 0x80961b2 "four"} 
(gdb) p b 
$18 = {0x80961a4 "one", 0x80961a8 "two", 0x80961ac "three", 0x80961b2 "four"} 
(gdb) p c 
$19 = {0x80961a8 "two", 0x80961ac "three", 0x80961b2 "four", 0x80961b7 "five"} 
(gdb) p d 
$20 = {0x80961a4 "one", 0x80961ac "three", 0x80961b2 "four", 0x80961bc "six"} 

我真的很驚訝,字符串指針等效相同話。我原以爲每個字符串都會被分配到自己的內存中,而不管它是否與另一個數組中的字符串相同。

這是某種編譯器優化的例子,還是這種類型的字符串聲明的標準行爲?

+1

「堆棧」在這個問題中甚至來自哪裏?如果你將'a','b','c'和'd'聲明爲局部變量,你必須在你的問題中這麼說。 – AnT 2012-07-09 17:07:15

+0

是 - 它們是在函數中聲明的自動持續時間的局部變量,因此在堆棧中 – bph 2012-07-09 17:10:52

+2

是的。這是編譯器優化的一個例子。 – Jack 2012-07-09 17:27:04

回答

24

它被稱爲「字符串池」。它在Microsoft Compilers中是可選的,但不在GCC中。如果關閉MSVC中的字符串池,那麼不同陣列中的「相同」字符串將被複制,並具有不同的內存地址,因此會佔用額外(不必要的)50個左右的靜態數據字節。

編輯:gcc 確實實際上有一個選項,-fwritable-strings禁用字符串池。這個選項的作用是雙重的:它允許字符串文字被覆蓋,並禁用字符串池。因此,在你的代碼中,設置這個標誌可以允許有些危險的代碼

/* Overwrite the first string in a, so that it reads 'xne'. Does not */ 
/* affect the instances of the string "one" in b or d */ 
*a[0] = 'x'; 
+4

在GCC中(至少4.7),禁用池的開關是-fno-merge-constants。 – dbrank0 2014-01-20 13:25:13

+3

@ dbrank0注意[gcc不再支持fwritabe-srings](https://gcc.gnu.org/gcc-4.0/changes.html),將這兩個註釋都添加到您的答案中是理想的。 – 2014-10-09 14:20:42

7

(我假設你abcd聲明爲局部變量,這是你的籌碼相關預期的原因。)用C

字符串文字具有靜態存儲持續時間。他們從來沒有被分配到「堆棧」上。它們總是分配在全局/靜態存儲器中,並且只要程序運行就「永遠」生活。

您的a,b,cd陣列被分配在堆棧上。存儲在這些數組中的指針指向靜態內存。在這種情況下,對於相同的單詞是相同的指針是沒有什麼不尋常的。

編譯器是否會將相同的文字合併爲一個取決於編譯器。一些編譯器甚至有一個控制這種行爲的選項。字符串文字總是隻讀的(這就是爲什麼在數組中使用const char *類型更好),因此,直到您開始依賴實際的指針值,它們是否合併或不合並。

P.S.出於好奇:即使這些字符串文字分配在堆棧上,爲什麼你會期望相同的文字被多次「實例化」呢?

+1

偉大的東西 - 這幫助我瞭解了很多,沒有完全理解字符串文字和它的相關存儲時間 - 我錯誤地認爲字符串只是作爲局部變量(自動)在堆棧上 – bph 2012-07-09 17:07:45

+2

我沒有意識到,說兩個(或多個)引用相同的字符串*必須*解析到相同的內存位置。編譯器可以(也有一些)爲每個字符串文字分配存儲空間,即使有些是「重複的」。請參閱@Josh提到的「字符串池」。 – 2012-07-09 17:12:34