官术网_书友最值得收藏!

2.2.3 空字符結尾錯誤

另一個常見的問題是字符串沒有正確地以空字符結尾。一個字符串正確地以空字符結尾,是指在數組最后一個元素處或在它之前存在一個空終結符。如果一個字符串沒有以空字符結尾,程序可能會被欺騙,導致在數組邊界之外讀取或寫入數據。

字符串必須在數組的最后一個元素的地址處或在它之前包含一個空終止字符,才可以安全地作為標準字符串處理函數如strcpy()函數或strlen()函數的參數被傳遞。空終止字符之所以是必要的,是因為前面這些函數以及其他由C標準定義的字符串處理函數,都依賴于它的存在來標記字符串的結尾。同樣,如果程序對一個字符數組迭代循環的終止條件取決于為字符串分配的內存內是否存在一個空終止字符,字符串也必須以空字符結尾。


1  size_t i;
2  char ntbs[16];
3  /* ... */
4  for (i = 0; i < sizeof(ntbs); ++i) {
5    if (ntbs[i] == '\0') break;
6    /* ... */
7  }

下面的程序在微軟Visual C++2010中能通過編譯,但在警告級別/W3下會對使用strncpy()和strcpy()發出警告。如果_FORTIFY_SOURCE宏被定義為一個非零值,它在Linux下還會(在運行時)由GCC診斷。


1  int main(void) {
2    char a[16];
3    char b[16];
4    char c[16];
5    strncpy(a, "0123456789abcdef", sizeof(a));
6    strncpy(b, "0123456789abcdef", sizeof(b));
7    strcpy(c, a);
8    /* ... */
9  }

在這個程序中,三個字符數組(a[]、b[]和c[])被聲明為16個字節。雖然strncpy()到a僅限于寫sizeof(a)(16個字節),但由于strncpy()函數的歷史和標準的行為,導致結果字符串不是以空字符結尾的。

根據C標準,strncpy()函數從源數組復制不超過n個字符(空字符后的字符不會被復制)到目標數組。因此,如本例所示,如果源數組中的前n個字符中不存在空字符,那么其結果字符串將不會是以空字符結尾的。

strncpy()到b也有類似的結果。這取決于編譯器如何分配存儲空間,a[]后的存儲空間可能碰巧存在一個空字符,但是這是編譯器未指定的,并在本例中是不太可能的,尤其是存儲空間是緊密堆積的時候。其結果是strcpy()到c可能寫得遠遠超出了數組界限,因為a[]中存儲的字符串不是正確地以空字符結尾的。

《C安全編碼標準》[Seacord 2008]包括“STR 32-C.按要求提供空字節結尾的字符串”。請注意,該規則并不排除使用字符數組。例如,即使在調用strncpy()之后,存儲在ntbs字符數組中的字符串可能不是正確地以空字符結尾的,下面的程序片段也沒有什么錯。


1  char ntbs[NTBS_SIZE];
2
3  strncpy(ntbs, source, sizeof(ntbs)-1);
4  ntbs[sizeof(ntbs)-1] = '\0';

與本章中描述的其他字符串操作錯誤一樣,空字符結尾錯誤也很難檢測,它們會潛伏在部署好的代碼中,直至遇到一組特別的輸入而導致發生錯誤。編寫代碼不能依賴于編譯器如何分配內存,因為這在編譯器的下個版本中很可能發生改變。

主站蜘蛛池模板: 十堰市| 青阳县| 宁强县| 伊通| 务川| 广元市| 岗巴县| 即墨市| 科技| 屏山县| 沙坪坝区| 澜沧| 梁河县| 洛阳市| 托克逊县| 田东县| 江阴市| 永善县| 清新县| 达尔| 满城县| 文水县| 昆明市| 涿鹿县| 休宁县| 丰都县| 宜兰县| 鞍山市| 乐陵市| 塘沽区| 东丽区| 故城县| 云阳县| 随州市| 宾川县| 乐昌市| 彭州市| 吉首市| 惠来县| 葫芦岛市| 长子县|