- C和C++安全編碼(原書第2版)
- (美)Robert C.Seacord
- 1038字
- 2020-10-30 17:56:38
2.1.7 計算字符串大小
為防止緩沖區溢出和其他一些運行時錯誤,正確地計算字符串大小是必不可少的。使用不正確的字符串大小會導致緩沖溢出,例如,會分配一個大小不充足的緩沖區。《C安全編碼標準》[Seacord 2008],“STR31-C.保證字符串的存儲空間具有容納字符數據和空終結符的足夠空間”提到了這個問題。數組和字符串的幾個重要屬性,對于正確分配空間,并防止緩沖區溢出是至關重要的。
大小(size)
分配給數組的字節數(等于sizeof(array))。
計數(count)
在數組中的元素數目(等于在Visual Studio 2010中的_countof(array))。
長度(length)
在空終結符之前的字符數。
混淆這些概念經常會導致C和C++程序中的嚴重錯誤。C標準保證,類型為char的對象由單個字節組成。因此,一個字符數組的大小等于一個char數組的計數(這也是數組的界限)。長度是在空終結符之前的字符數。對于一個正確地以null結尾的char類型的的字符串,其長度必然是小于或等于其大小減1。
當寬字符串被誤認為是窄字符串或多字節字符串時,可能會不正確地計算其大小。C標準定義的wchar_t是一個整數類型,其值的范圍可以代表所支持的語言環境中最大的擴展字符集的所有成員的不同編碼。Windows會使用UTF-16字符編碼,所以wchar_t的大小通常為兩個字節。Linux和OS X(GCC/g++以及Xcode中)使用UTF-32字符編碼,所以wchar_t的大小通常為4個字節。在大多數平臺上,wchar_t的大小至少是兩個字節,因此,wchar_t數組的大小已不再等于對同一個數組的計數。作其他假定的程序可能包含錯誤。例如,在下面的程序片段中,錯誤地使用strlen()函數來確定一個寬字符串的大小:
1 wchar_t wide_str1[] = L"0123456789"; 2 wchar_t *wide_str2 = (wchar_t *)malloc(strlen(wide_str1) + 1); 3 if (wide_str2 == NULL) { 4 /* handle error */ 5 } 6 /* ... */ 7 free(wide_str2); 8 wide_str2 = NULL;
當編譯此程序時,Microsoft Visual Studio 2012將生成一個不兼容的類型警告并終止翻譯。GCC4.7.2雖然也生成一個不兼容的類型警告但卻能繼續編譯。
對一個以空字符結尾的字節字符串,strlen()函數對終止空字節前面的字符數量進行計數(長度)。然而,寬字符可以包含空字節,尤其是從ASCII字符集獲取時,如在這個例子中。因此strlen()函數將返回在字符串中的第一個空字節前的字節數。
在下面的程序片段中,正確地使用wcslen()函數來確定一個寬字符串的大小,但此長度沒有乘以sizeof(wchar_t)。
1 wchar_t wide_str1[] = L"0123456789"; 2 wchar_t *wide_str3 = (wchar_t *)malloc(wcslen(wide_str1) + 1); 3 if (wide_str3 == NULL) { 4 /* 處理錯誤 */ 5 } 6 /* ... */ 7 free(wide_str3); 8 wide_str3 = NULL;
下面的程序片段正確地計算了容納寬字符串的一個副本所需的字節數(包括終止字符):
01 wchar_t wide_str1[] = L"0123456789"; 02 wchar_t *wide_str2 = (wchar_t *)malloc( 03 (wcslen(wide_str1) + 1) * sizeof(wchar_t) 04 ); 05 if (wide_str2 == NULL) { 06 /* 處理錯誤 */ 07 } 08 /* ... */ 09 free(wide_str2); 10 wide_str2 = NULL;
《C安全編碼標準》[Seacord 2008],“STR31-C.保證字符串的存儲空間具有容納字符數據和空終結符的足夠空間”,正確地提供了關于計算寬字符串大小的補充信息。