2015-09-28 49 views
0

如果我寫:爲什麼修改指向字串的指針的內容是錯誤的?

char *aPtr = "blue"; //would be better const char *aPtr = "blue" 
aPtr[0]='A'; 

我有一個警告。上面的代碼可以工作,但不是標準的,它有一個未定義的行爲,因爲它是一個只讀存儲器,在字符串處有一個指針。問題是: 這是爲什麼? 與此代碼相反:

char a[]="blue"; 
char *aPtr=a; 
aPtr[0]='A'; 

沒問題。我想在引擎蓋下了解發生了什麼

+1

http://stackoverflow.com/a/30661089/2912665 – CinCout

+0

無論是作爲重複關閉的問題還是由@HappyCoder提及的問題都非常類似於此問題。它們都處理字符串文字的類型。這是問**爲什麼**字符串文字有這種類型。 –

+0

不只是「會更好」; 「將是合法的」。你知道這已被問了一百萬次。 –

回答

1

第一個是指向由編譯器創建的只讀值並放在程序的只讀部分的指針。您不能修改該地址處的字符,因爲它們是隻讀的。

第二個創建一個數組並從初始化程序複製每個元素(有關更多詳細信息,請參閱this answer)。你可以修改數組的內容,因爲它是一個簡單的變量。

第一個以它的方式工作,因爲做任何事情都需要動態分配一個新變量,並且需要垃圾回收來釋放它。這不是C和C++的工作原理。

+0

好吧,如果我在第一種情況下理解編譯器把字符串litteral在區域常量中,因此修改該值不是不可能的。在第二種情況下,而不是數組在堆棧上。這是正確的? – Nick

+0

是的。在這兩種情況下,您聲明的變量都在堆棧中,但是不同類型的變量。在第一種情況下,變量只是指針「p」,它指向一個常量數組存儲在別處。在第二種情況下,變量是數組'a',它並不指向任何地方,它是堆棧中的數組。你可以修改'a [0]',因爲它是變量的一部分。你不能修改'p [0]'因爲它是一個常量。 –

+0

謝謝,現在一切都清楚了 – Nick

1

字符串文字不能被修改(沒有未定義的行爲)的主要原因是支持字符串文字合併。編譯器作者注意到很多程序都有相同的字符串文字重複多次 - 尤其是像模式字符串傳遞到fopen(例如,f = fopen("filename", "r");)和簡單格式字符串傳遞到printf(例如,printf("%d\n", a);)。

爲了節省內存,他們會避免爲這些字符串的每個實例分配單獨的內存。相反,他們會分配一個一塊內存,並指向它的所有指針。

在少數情況下,它們甚至比這更復雜,以合併甚至完全相同的文字。例如,考慮這樣的代碼:

printf("%s\t%d\n", a); 
/* ... */ 
printf("%d\n", b); 

在這種情況下,字符串文字不完全相同,但第二個是第一端部的相同部分。在這種情況下,他們仍然會分配一塊內存。一個指針指向內存的開始位置,另一個指向同一塊內存中的%d位置。

有可能(但沒有要求)字符串文字合併,當修改字符串文字時,說出你會得到什麼樣的行爲是不可能的。如果合併字符串文字,修改一個字符串文字可能會修改其他相同或相同的結尾。如果字符串文字不合並,修改一個將不會影響其他任何文字。

MMU增加了另一個維度:它們允許內存被標記爲只讀,所以嘗試修改字符串文字會導致某種信號 - 但只有當系統有MMU時(通常是可選的一次),也取決於編譯器/鏈接器是否決定將字符串文字放在內存中,它們是否標記爲常量。

由於他們無法定義修改字符串文字時的行爲,他們決定修改字符串文字會產生未定義的行爲。

第二種情況完全不同。在這裏你定義了一個數組char。很明顯,如果你定義了兩個單獨的數組,它們仍然是獨立的,不管內容如何,​​所以修改一個不會影響另一個。行爲是清楚的,而且一直都是這樣,所以這樣做給定了行爲。有問題的數組可能是從字符串文字初始化的事實不會改變這一點。

+0

即使沒有考慮合併,如果文字可以被修改什麼'char * foo(){return「foo」; } foo()[0] ='b'; puts(foo());'做?您可以根據任意調用者對它的處理方式來改變'foo()'的返回值,或者每次調用它時都需要分配一個新字符串,然後以某種方式垃圾回收。兩者都不是一個好的選擇。 –

+0

@JonathanWakely:雖然當然可以想象/假設(其他一些)其他原因,但在談論爲什麼要這樣做時,文字合併有着根本的不同:​​文字合併是一個真正討論過的問題(相當重要)在原來的C標準化過程中產生了原始的C89/90標準。 –

相關問題