2010-06-22 133 views
4

如果我們聲明char * p="hello";那麼既然它寫在數據段中,我們不能修改p點的內容,但是我們可以修改指針本身。但我發現在C陷阱與誤區 安德魯·柯尼希 這個例子AT & T貝爾實驗室 美利山,新澤西州07974c字符指針問題

的例子是

char *p, *q; 
p = "xyz"; 
q = p; 
q[1] = ’Y’; 

q將指向包含字符串XYZ內存。所以p,因爲p和q指向相同的內存。

怎麼回事真的,如果我提到的第一條語句也是如此.. 同樣地,我跑到下面的代碼

main() 
{ 
char *p="hai friends",*p1; 
p1=p; 
while(*p!='\0') ++*p++; 
printf("%s %s",p,p1); 
} 

,並得到了輸出 ibj!gsjfoet

請解釋如何在這兩個我們是否可以修改內容? 在此先感謝

+1

謹防陰暗面 – 2010-06-22 06:15:37

+0

修改字符串字面導致不確定的行爲的內容,這意味着任何事情都可能發生的。 – 2010-06-22 06:21:01

+0

我們需要多少字符串文字問題? – 2010-06-22 08:09:23

回答

5

你同樣的例子使我的系統上段故障。

您正在運行到未定義的行爲在這裏。 .data(注意,字符串文字也可能在.text之內)不一定是不可變的 - 不能保證機器會根據操作系統和編譯器來保護該內存(通過頁表)。

+0

你使用什麼操作系統/編譯器? – drfrogsplat 2010-06-22 06:22:23

+0

我也有一個seg故障;在WinXP上使用g ++ – fizzbuzz 2010-06-22 07:15:51

4

只有你的操作系統可以保證數據部分的內容是隻讀的,甚至包括設置段限制和訪問標誌以及使用遠指針等,所以並不總是這樣。

C本身沒有這樣的限制;在一個平坦的內存模型(幾乎所有的32位操作系統都使用這些日子)​​中,您的地址空間中的任何任何字節都可能是可寫的,甚至在您的代碼部分中也是如此。如果你有一個指向main()的指針,還有一些關於機器語言的知識,以及一個設置正確的操作系統(或者說,沒有阻止它),你可能會重寫它,直到返回0.請注意,這都是一種黑魔法,並且很少有意地進行,但它是使C成爲系統編程的強大語言的一部分。

0

修改字符串文字是一個壞主意,但這並不意味着它可能無法正常工作。

一個非常好的理由不會讓你的編譯器獲得同一個字符串的多個實例並使它們指向同一塊內存。所以如果在你的代碼的其他地方定義了「xyz」,你可能會無意中破壞其他代碼,並期望它是不變的。

1

這取決於編譯器是否有效。

86是一個von Neumann architecture(相對於Harvard),所以有(「數據」,並在基本水平「程序」存儲器之間沒有明顯的差異,即,編譯器不被迫成具有不同類型的程序vs數據存儲器,所以不會必然限制任何變量的一個或另一個)。

所以一個編譯器可以允許字符串的修改而另一個沒有。

我的猜測是一個更寬鬆編譯(例如CL,則MS的Visual Studio C++編譯器)將允許此,而更嚴格編譯(例如GCC)不會。如果你的編譯器允許的話,沒準它有效地改變你的代碼是這樣的:

... 
char p[] = "hai friends"; 
char *p1 = p; 
... 
// (some disassembly required to really see what it's done though) 

也許讓新C/C++程序員用更少的限制/減少混亂錯誤代碼「良好意願」。 (這是否是一個「好事」是不怎麼樣辯論,我會繼續我的意見大多是這個職位的:P)

出於興趣,你用什麼編譯器?

+0

x86確實能夠將內存頁面標記爲只讀,所以.data和.text之間的區別在於.text幾乎總是不會爲應用程序啓用寫入權限。 – nategoose 2010-06-23 03:03:20

0

你的程序也適用我的系統(Windows + cygwin的)上。然而,標準說你不應該那樣做,儘管結果沒有定義。

繼從書中摘錄Ç:一本參考手冊5/E,33頁,

你不應該試圖修改保存字符串常量的人物,因爲可以是隻讀存儲器只有

char p1[] = "Always writable"; 
char *p2 = "Possibly not writable"; 
const char p3[] = "Never writable"; 

p1行將始終工作; p2行可能工作或可能導致運行時錯誤; p3將始終導致編譯時錯誤。

0

在修改字符串常量可能你的系統上,這是你的平臺的怪癖,而不是語言的保證。實際的C語言不知道.data節或.text節。這就是所有的實現細節。

在某些嵌入式系統,你甚至不會有一個文件系統包含有.text段的文件。在一些這樣的系統中,你的字符串文字將被存儲在ROM中,而試圖寫入ROM會使設備崩潰。

如果您編寫的代碼依賴於未定義的行爲,並且只能在您的平臺上運行,那麼可以保證,遲早有人會認爲將它移植到某個不工作的新設備是個好主意你期望的方式。當發生這種情況時,一羣憤怒的嵌入式開發人員會追捕你並刺傷你。

0

p有效地指向只讀存儲器。分配給數組p的結果指向可能是未定義的行爲。僅僅因爲編譯器讓你擺脫它並不意味着它是好的。

看看從C-FAQ這個問題:comp.lang.c FAQ list · Question 1.32

問:什麼是這些初始化之間 區別?

char a[] = "string literal"; 
char *p = "string literal"; 

我的程序崩潰,如果我嘗試分配 一個新值P [I]。

A:(在C 源一個雙引號字符串正式術語 )字符串文字可以在兩個略微 不同的方式使用:

  1. 作爲初始值設定爲字符數組,如炭 的聲明中的[],它指定的初始值的字符 該數組中(並且, 如果必要的話,它的尺寸)。
  2. 在別的地方,它變成字符, 的無名,靜態數組,這無名數組可以被存儲在 只讀存儲器,並且其因此 可以不一定是 修改。在表達式上下文中, 與往常一樣將數組轉換爲 指針(請參閱第6節),因此 第二個聲明將p 初始化爲指向未命名數組的第一個 元素。

一些編譯器具有開關 控制字符串文字 是否可寫或不(用於編譯舊 代碼),以及一些可以具有選項來 原因字符串文字是正式 視爲常量字符的陣列(爲 更好的錯誤捕捉)。

0

我認爲在使用C,C++或其他低級語言時,要理解一個非常重要的一般概念時會產生很大的困惑。在一個低層次的語言有一個隱含的假設不是程序員知道他/她在做什麼,使沒有編程錯誤

這個假設允許語言的實施者只是忽略如果程序員違反規則將會發生什麼。最終的效果是,在C或C++沒有「運行時錯誤」保障......如果你做壞事只是它的沒有定義(「未定義的行爲」是法律術語的術語)什麼事情發生。可能會發生崩潰(如果你非常幸運),或者可能顯然沒有任何東西(不幸的是,大多數情況下......可能會在完全有效的地方發生一次百萬條指令的崩潰)。

例如,如果你訪問數組外,你會得到一個崩潰,可能是沒有,甚至可能是一個守護進程會出來你的鼻子(這是「鼻守護」你可能在互聯網上找到)。編寫編譯器的人不在考慮這個問題。

只是從來沒有做到這一點(如果你關心寫像樣的程序)。

誰使用低級語言的額外負擔是,你必須學會​​所有的規則非常好,你決不能違反他們。如果違反規則,你不能指望「運行時錯誤天使」來幫助你......只有「未定義的行爲守護進程」出現在那裏。

1

在過去的日子裏,當他們的着作「C語言編程語言」中的K & R所描述的C語言成爲了「標準」時,你所描述的完全可以。事實上,一些編譯器跳過了這個循環來使字符串文字可寫。在初始化時,他們會費力地將文本段中的字符串複製到數據段。

即使是現在,GCC有一個標誌,以恢復這種行爲:-fwritable-strings

1
main() 
{ 
int i = 0; 
char *p= "hai friends", *p1; 
p1 = p; 
while(*(p + i) != '\0') 
    { 
     *(p + i); 
     i++; 
    } 
printf("%s %s", p, p1); 
return 0; 
} 

此代碼將使輸出:海友海友

+0

*(p + i)在while循環中是不必要的。 – 2015-03-11 15:57:13