笨辦法學C 練習36:更安全的字符串

練習36:更安全的字符串

原文:Exercise 36: Safer Stringshtml

譯者:飛龍git

我已經在練習26中,構建devpkg的時候介紹了Better String庫。這個練習讓你從如今開始熟悉bstring庫,而且明白C風格字符串爲何十分糟糕。以後你須要修改liblcthw的代碼來使用bstring程序員

爲何C風格字符串十分糟糕

當人們談論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

有不少改進後的字符串庫,可是我最喜歡bstrlib,由於它只有一個程序集,而且具備大多數所需的字符串功能。你已經在使用它了,因此這個練習中你須要從Better String獲取兩個文件,bstrlib.cbstrlib.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下運行測試,確保內存使用正確。

相關文章
相關標籤/搜索