原文:Exercise 36: Safer Stringshtml
譯者:飛龍git
我已經在練習26中,構建devpkg
的時候介紹了Better String庫。這個練習讓你從如今開始熟悉bstring
庫,而且明白C風格字符串爲何十分糟糕。以後你須要修改liblcthw
的代碼來使用bstring
。程序員
當人們談論C的問題時,「字符串」的概念永遠是首要缺陷之一。你已經用過它們,而且我也談論過它們的種種缺陷,可是對爲何C字符串擁有缺陷,以及爲何一直是這樣沒有明確的解釋。我會試着如今作出解釋,部分緣由是C風格字符串通過數十年的使用,有足夠的證據代表它們是個很是糟糕的東西。github
對於給定的任何C風格字符串,都不可能驗證它是否有效。vim
以'\0'
結尾的C字符串是有效的。安全
任何處理無效C字符串的循環都是無限的(或者形成緩衝區溢出)。數據結構
C字符串沒有肯定的長度,因此檢查它們的惟一方法就是遍歷它來觀察循環是否正確終止。函數
因此,不經過有限的循環就不可能驗證C字符串。oop
這個邏輯很是簡單。你不能編寫一個循環來驗證C字符串是否有效,由於無效的字符串致使循環永遠不會中止。就是這樣,惟一的解決方案就是包含大小。一旦你知道了大小,你能夠避免無限循環問題。若是你觀察練習27中我向你展現的兩個函數:學習
譯者注:檢驗C風格字符串是否有效等價於「停機問題」,這是一個很是著名的不可解問題。
void copy(char to[], char from[]) { int i = 0; // while loop will not end if from isn't '\0' terminated while((to[i] = from[i]) != '\0') { ++i; } } int safercopy(int from_len, char *from, int to_len, char *to) { int i = 0; int max = from_len > to_len - 1 ? to_len - 1 : from_len; // to_len must have at least 1 byte if(from_len < 0 || to_len <= 0) return -1; for(i = 0; i < max; i++) { to[i] = from[i]; } to[to_len - 1] = '\0'; return i; }
想象你想要向copy
函數添加檢查來確保from
字符串有效。你該怎麼作呢?你編寫了一個循環來檢查字符串是否已'\0'
結尾。哦,等一下,若是字符串不以'\0'
結尾,那它怎麼讓循環停下?不可能停下,因此無解。
不管你怎麼作,你都不能在不知道字符串長度的狀況下檢查C字符串的有效性,這裏safercopy
包含了程度。這個函數沒有相同的問題,由於他的循環必定會停止,即便你傳入了錯誤的大小,大小也是有限的。
譯者注:可是問題來了,對於一個C字符串,你怎麼獲取其大小?你須要在這個函數以前調用
strlen
,又是一個無限循環問題。
因而,bstring
庫所作的事情就是建立一個結構體,它老是包含字符串長度。因爲這個長度對於bstring
來講老是可訪問的,它上面的全部操做都會更安全。循環是有限的,內容也是有效的,而且這個主要的缺陷也不存在了。BString庫也帶有大量所需的字串操做,好比分割、格式化、搜索,而且大多數都會正確並安全地執行。
bstring
中也可能有缺陷,可是通過這麼長時間,可能性已經很低了。glibc
中也有缺陷,因此你讓程序員怎麼作纔好呢?
有不少改進後的字符串庫,可是我最喜歡bstrlib
,由於它只有一個程序集,而且具備大多數所需的字符串功能。你已經在使用它了,因此這個練習中你須要從Better String獲取兩個文件,bstrlib.c
和bstrlib.h
。
下面是我在liblcthw
項目目錄裏所作的事情:
$ mkdir bstrlib $ cd bstrlib/ $ unzip ~/Downloads/bstrlib-05122010.zip Archive: /Users/zedshaw/Downloads/bstrlib-05122010.zip ... $ ls bsafe.c bstraux.c bstrlib.h bstrwrap.h license.txt test.cpp bsafe.h bstraux.h bstrlib.txt cpptest.cpp porting.txt testaux.c bstest.c bstrlib.c bstrwrap.cpp gpl.txt security.txt $ mv bstrlib.h bstrlib.c ../src/lcthw/ $ cd ../ $ rm -rf bstrlib # make the edits $ vim src/lcthw/bstrlib.c $ make clean all ... $
在第14行你能夠看到,我編輯了bstrlib.c
文件,來將它移動到新的位置,而且修復OSX上的bug。下面是差別:
25c25 < #include "bstrlib.h" --- > #include <lcthw/bstrlib.h> 2759c2759 < #ifdef __GNUC__ --- > #if defined(__GNUC__) && !defined(__APPLE__)
我把包含修改成<lcthw/bstrlib.h>
,而後修復2759行ifdef
的問題。
這個練習很短,只是讓你準備好剩餘的練習,它們會用到這個庫。接下來兩個聯繫中,我會使用bstrlib.c
來建立Hashmap`數據結構。
你如今應該閱讀頭文件和實現,以後編寫tests/bstr_tests.c
來測試下列函數,來熟悉這個庫:
bfromcstr
從C風格字符串中建立一個bstring
。
blk2bstr
與上面相同,可是能夠提供緩衝區長度。
bstrcpy
複製bstring
。
bassign
將一個bstring
賦值爲另外一個。
bassigncstr
將bsting
的內容設置爲C字符串的內容。
bassignblk
將bsting
的內容設置爲C字符串的內容,可是能夠提供長度。
bdestroy
銷燬bstring
。
bconcat
在一個bstring
末尾鏈接另外一個。
bstricmp
比較兩個bstring
,返回值與strcmp
相同。
biseq
檢查兩個bstring
是否相等。
binstr
判斷一個bstring
是否被包含於另外一個。
bfindreplace
在一個bstring
中尋找另外一個,而且將其替換爲別的。
bsplit
將bstring
分割爲bstrList
。
bformat
執行字符串格式化,十分便利。
blength
獲取bstring
的長度。
bdata
獲取bstring
的數據。
bchar
得到bstring
中的字符。
你的測試應該覆蓋到全部這些操做,以及你從頭文件中發現的更多有趣的東西。在valgrind
下運行測試,確保內存使用正確。