2012-01-22 41 views
10

我試圖理解爲什麼下面的代碼片段是給分段錯誤:strtok的分段錯誤

void tokenize(char* line) 
{ 
    char* cmd = strtok(line," "); 

    while (cmd != NULL) 
    { 
     printf ("%s\n",cmd); 
     cmd = strtok(NULL, " "); 
    } 
} 

int main(void) 
{ 
    tokenize("this is a test"); 
} 

我知道的strtok()實際上並不在字符串文字記號化,但在這種情況下,line直接指向字符串"this is a test",該字符串在內部是char的數組。是否有任何標記line而不復制到數組中?

+2

多德 - 「這是一個測試」是一個字符串文字。這意味着它是* READ ONLY *「字符數組」。您甚至可能試圖修改它,而不會在某些平臺上崩潰。但它絕對是一個禁忌的* ANY *平臺:) – paulsm4

回答

14

問題是你正試圖修改一個字符串文字。這樣做會導致程序的行爲未定義。

說你不允許修改字符串文字是過分簡化。說字符串文字是const是不正確的;他們不是。

警告:請注意以下事項。

字符串文字"this is a test"char[15]類型的表達式(長度14,加上終止'\0'的1)。在大多數情況下,包括這一個,這樣的表達式被隱含地轉換爲指向數組的第一個元素的指針,類型爲char*

試圖修改字符串引用的數組的行爲是未定義的 - 不是因爲它是const(不是),而是因爲C標準明確指出它未定義。

一些編譯器可能允許你擺脫這一點。你的代碼實際上可能會修改與文字相對應的靜態數組(這可能會在以後造成很大的混淆)。

大多數現代編譯器,不過,將存儲在只讀存儲器陣列 - 不是物理的ROM,但在那個年代由虛擬內存系統免受修改的內存區域。試圖修改這種內存的結果通常是分段錯誤和程序崩潰。

那麼爲什麼不是字符串文字const?因爲你真的不應該試圖修改它們,所以它肯定是有意義的 - 而且C++確實會生成字符串文字const。原因是歷史的。它是由1989年的ANSI C標準出臺之前,const關鍵字不存在(雖然它由一些編譯器之前,可能實現)。所以ANSI前的程序可能是這樣的:

#include <stdio.h> 

print_string(s) 
char *s; 
{ 
    printf("%s\n", s); 
} 

main() 
{ 
    print_string("Hello, world"); 
} 

有沒有辦法來強制執行print_string不允許修改字符串由s指出這樣的事實。 ANSI C中的字符串文字const將破壞現有的代碼,ANSI C委員會非常努力地避免這樣做。自那時以來,沒有一個很好的機會對語言做出這樣的改變。 (C++的設計者,主要是Bjarne Stroustrup,並不擔心向後兼容C.)

+0

很好的解釋! – ademar111190

+1

downvoter照顧解釋? –

2

正如你所說,你不能修改字符串文字,這是strtok所做的。你所要做的

char str[] = "this is a test"; 
tokenize(str); 

這將創建數組strthis is a test\0初始化它,並把它的指針到tokenize

0

我相信你會被打得這麼...但「strtok()」本質上是不安全的,並且容易出現訪問違規等情況。

這裏,答案几乎肯定是使用字符串常量。

試試這個:

void tokenize(char* line) 
{ 
    char* cmd = strtok(line," "); 

    while (cmd != NULL) 
    { 
     printf ("%s\n",cmd); 
     cmd = strtok(NULL, " "); 
    } 
} 

int main(void) 
{ 
    char buff[80]; 
    strcpy (buff, "this is a test"); 
    tokenize(buff); 
} 
+1

如果你要打開的strtok的不安全性質,我們不妨記住,函數strncpy比strcpy的安全得多。雖然strcpy對於編譯時常量字符串是完全安全的,但後來的重構可能會將strcpy調用變成緩衝區溢出漏洞。 –

1

Strok修改它的第一個參數,以便來標記它。因此,您不能將它傳遞給文字字符串,因爲它的類型爲const char *,因此無法修改,因此是未定義的行爲。您必須將字符串文字複製到可修改的字符數組中。

2

試圖標記編譯時常量字符串會導致分段錯誤的原因很充分:常量字符串位於只讀內存中。

C編譯器將編譯時常量字符串烘焙到可執行文件中,操作系統將它們加載到只讀內存(* nix ELF文件中的.rodata)中。由於該內存被標記爲只讀,並且由於strtok寫入您傳遞給它的字符串中,所以會出現分段錯誤以便寫入只讀內存。

1

你試圖通過你的「...在內部是一個數組char備註?

"this is a test"內部是char數組的事實根本不會改變任何東西。它仍然是一個字符串文字(所有字符串文字都是不可修改的char數組)。您的strtok仍會嘗試標記字符串文字。這就是它崩潰的原因。

0

我剛剛嘗試使用printf打印令牌(在您的情況下爲cmd)出現Segmentation Fault錯誤成爲NULL。