2012-04-06 89 views
11

我是學習C++的學生,我想了解null終止字符數組是如何工作的。假設我定義一個字符數組像這樣:C++ char數組null終結符位置

char* str1 = "hello world"; 

正如預期的那樣,strlen(str1)等於11,它是空終止。

如果上述字符數組中的所有11個元素都填充了字符「hello world」,C++將null終止符放在哪裏?它實際上是分配一個長度爲12而不是11的數組,第12個字符是'\0'CPlusPlus.com似乎表明,11的人會需要是'\0',除非它確實是分配12

想我做到以下幾點:

// Create a new char array 
char* str2 = (char*) malloc(strlen(str1)); 

// Copy the first one to the second one 
strncpy(str2, str1, strlen(str1)); 

// Output the second one 
cout << "Str2: " << str2 << endl; 

此輸出Str2: hello worldatcomY╗°g♠↕,我以爲是C++閱讀內存在指針char* str2指向的位置,直到它遇到它認爲是空字符的內容。

但是,如果我再這樣做:

// Null-terminate the second one 
str2[strlen(str1)] = '\0'; 

// Output the second one again 
cout << "Terminated Str2: " << str2 << endl; 

它輸出Terminated Str2: hello world預期。

但不寫str2[11]意味着我們正在寫的str2分配的內存空間之外,因爲str2[11]是第12個字節,但我們只分配11個字節?

運行此代碼似乎不會導致任何編譯器警告或運行時錯誤。這在實踐中是否安全?使用malloc(strlen(str1) + 1)而不是malloc(strlen(str1))會更好嗎?

+1

不,11個字符中的一個確實是'\ 0' ...只是在開玩笑:-) – hirschhornsalz 2012-04-06 23:28:30

+1

既然你在學習,從一開始就知道你正在學習的東西的名字是很重要的。表達式char * str1 =「hello world」;'沒有定義字符數組,而是指向一個文字的*指針*(順便說一句,從'const char *'到'char *'的轉換被棄用了,所以編譯器應該已經警告過你)。 * literal *本身是一個*常量*的*數組*,它帶有一個空終止符,但是您定義的變量是一個*指針*。 – 2012-04-07 00:24:18

回答

11

對於字符串文字,編譯器實際上爲\0元素預留了額外的char元素。

// Create a new char array 
char* str2 = (char*) malloc(strlen(str1)); 

這是新C程序員犯的一個常見錯誤。當爲char*分配存儲空間時,您需要分配字符數+ 1個以存儲\0。這裏不分配額外的存儲裝置,這條線也是違法的你實際上是寫過去,你分配的內存結束

// Null-terminate the second one 
str2[strlen(str1)] = '\0'; 

這裏。分配X元素時,您可以訪問的最後一個合法字節是內存地址偏移量X - 1。寫入X元素會導致未定義的行爲。它通常會起作用,但卻是一顆定時炸彈。

寫正確的方法是如下

size_t size = strlen(str1) + sizeof(char); 
char* str2 = (char*) malloc(size); 
strncpy(str2, str1, size); 

// Output the second one 
cout << "Str2: " << str2 << endl; 

在這個例子中實際上沒有必要的str2[size - 1] = '\0'。函數strncpy將用空終止符填充所有額外的空格。這裏僅存在size - 1str1元件,從而在陣列中的最後一個元素是不需要的,並且將被填充\0

+0

在你的例子中,明確定義'size_t size = strlen(str1)+ sizeof(char)'的目的是什麼?使用'malloc(strlen(str1)+1)'是否可以,因爲我們知道char是1個字節? – 2012-04-06 23:34:22

+1

@JohnMahoney有兩個原因我使用'尺寸'本地。首先是表現。 'strlen'函數雖然不貴,但是O(N)並且由於字符串沒有改變,所以沒有理由多次運行它。 '+ sizeof(char)'部分主要是風格。 '+ 1'做同樣的事情,我只是更喜歡更明確的'sizeof(char)'符號 – JaredPar 2012-04-06 23:35:55

+1

更好:'char * str2 = malloc(str1)+ 1);如果(str2 == NULL){/ *句柄分配失敗* /} strcpy(str2,str1);''sizeof(char)是1定義的。 'strncpy'碰巧在這種情況下工作,但它不*僅僅是'strcpy'的「安全」版本。 – 2012-04-07 00:56:06

6

它實際上是分配一個長度爲12而不是11的數組,第12個字符是'\ 0'嗎?

是的。

但不寫str2[11]意味着我們正在寫的str2分配的內存空間之外,因爲str2[11]是第12個字節,但我們只分配11個字節?

是的。

使用malloc(strlen(str1) + 1)而不是malloc(strlen(str1))會更好嗎?

是的,因爲第二種形式不足以將字符串複製到。

運行此代碼似乎不會導致任何編譯器警告或運行時錯誤。

在最簡單的情況下檢測這一點是一個非常困難的問題。所以編譯器作者根本就不打擾。


這類複雜的就是爲什麼你應該使用std::string,而不是純粹的C風格的字符串,如果你正在寫C++。它是如此簡單:

std::string str1 = "hello world"; 
std::string str2 = str1; 
1

字面"hello world"char陣列看起來像:

{ 'h', 'e', 'l', 'l', 'o', ' ', 'w', 'o', 'r', 'l', 'd', '\0' } 

所以,是的,字面是12個char S IN的大小。

此外,由於strlen返回字符串的長度,不包括NUL終止符,因此malloc(strlen(str1))正在爲少於1個字節的字節分配內存。寫入str[strlen(str1)]正在寫入超出您分配的內存量的1個字節。

你的編譯器不會告訴你,但如果你通過的valgrind或類似的程序您的系統上運行您的程序,它會告訴你,如果你所訪問的內存,你不應該。

1

對於一個標準的C字符串被存儲串數組的長度總是一個字符長,則長度字符串中的字符。因此,您的"hello world"字符串的字符串長度爲11,但需要一個包含12個條目的支持數組。

原因是這些字符串被讀取的方式。處理這些字符串的函數基本上逐個讀取字符串的字符,直到找到終止字符'\0'並在此處停止。如果這個字符缺少這些功能,只要繼續讀取內存,直到它們碰到一個受保護的內存區域,這個內存區域會導致主機操作系統終止應用程序,或者直到他們找到終止字符。

此外,如果您初始化長度爲11的字符數組並將其寫入字符串"hello world"將產生大量問題。因爲陣列預計至少保留12個字符。這意味着內存中數組後面的字節被覆蓋。導致不可預知的副作用。

此外,當您使用C++時,您可能需要查看std:string。如果您使用C++並且提供更好的字符串處理,則可以訪問此類。這可能值得研究。

2

我想你很困惑strlen的返回值。它返回字符串的長度,它不應該與包含該字符串的數組的大小相混淆。考慮這個例子:

char* str = "Hello\0 world"; 

我在字符串的中間添加了一個空字符,這是完全有效的。這裏數組的長度爲13(12個字符+最終的空字符),但strlen(str)將返回5,因爲在第一個空字符之前有5個字符。 strlen只是計算字符,直到找到空字符。

所以,如果我用你的代碼:

char* str1 = "Hello\0 world"; 
char* str2 = (char*) malloc(strlen(str1)); // strlen(str1) will return 5 
strncpy(str2, str1, strlen(str1)); 
cout << "Str2: " << str2 << endl; 

的str2的數組將有長度爲5,並且不會被空字符終止(因爲strlen不計的話)。這是你的預期嗎?

+0

[相似問題](https://stackoverflow.com/questions/10050228/c-char-array-null-terminator-location) – user3583535 2017-06-20 11:10:26

0

我想你需要知道的是,char數組從0開始,直到數組長度爲1,並且在位置數組長度上有終止符('\ 0')。
你的情況:

str1[0] == 'h'; 
str1[10] == 'd'; 
str1[11] == '\0'; 

這就是爲什麼是正確STR2 [strlen的(STR1)] = '\ 0';
strncpy之後的輸出問題是因爲它複製了11個元素(0..10),因此您需要手動輸入終結符(str2 [11] ='\ 0')。