笨辦法學(xué)C 練習(xí)36:更安全的字符串

練習(xí)36:更安全的字符串

原文:Exercise 36: Safer Strings

譯者:飛龍

我已經(jīng)在練習(xí)26中绑莺,構(gòu)建devpkg的時(shí)候介紹了Better String庫(kù)演熟。這個(gè)練習(xí)讓你從現(xiàn)在開(kāi)始熟悉bstring庫(kù),并且明白C風(fēng)格字符串為什么十分糟糕趁猴。之后你需要修改liblcthw的代碼來(lái)使用bstring

為什么C風(fēng)格字符串十分糟糕

當(dāng)人們談?wù)揅的問(wèn)題時(shí)富纸,“字符串”的概念永遠(yuǎn)是首要缺陷之一集畅。你已經(jīng)用過(guò)它們,并且我也談?wù)撨^(guò)它們的種種缺陷,但是對(duì)為什么C字符串擁有缺陷枯夜,以及為什么一直是這樣沒(méi)有明確的解釋弯汰。我會(huì)試著現(xiàn)在做出解釋,部分原因是C風(fēng)格字符串經(jīng)過(guò)數(shù)十年的使用湖雹,有足夠的證據(jù)表明它們是個(gè)非常糟糕的東西咏闪。

對(duì)于給定的任何C風(fēng)格字符串,都不可能驗(yàn)證它是否有效摔吏。

  • '\0'結(jié)尾的C字符串是有效的鸽嫂。
  • 任何處理無(wú)效C字符串的循環(huán)都是無(wú)限的(或者造成緩沖區(qū)溢出)。
  • C字符串沒(méi)有確定的長(zhǎng)度征讲,所以檢查它們的唯一方法就是遍歷它來(lái)觀察循環(huán)是否正確終止据某。
  • 所以,不通過(guò)有限的循環(huán)就不可能驗(yàn)證C字符串诗箍。

這個(gè)邏輯非常簡(jiǎn)單癣籽。你不能編寫(xiě)一個(gè)循環(huán)來(lái)驗(yàn)證C字符串是否有效,因?yàn)闊o(wú)效的字符串導(dǎo)致循環(huán)永遠(yuǎn)不會(huì)停止滤祖。就是這樣筷狼,唯一的解決方案就是包含大小。一旦你知道了大小匠童,你可以避免無(wú)限循環(huán)問(wèn)題桑逝。如果你觀察練習(xí)27中我向你展示的兩個(gè)函數(shù):

譯者注:檢驗(yàn)C風(fēng)格字符串是否有效等價(jià)于“停機(jī)問(wèn)題”,這是一個(gè)非常著名的不可解問(wèn)題俏让。

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函數(shù)添加檢查來(lái)確保from字符串有效楞遏。你該怎么做呢?你編寫(xiě)了一個(gè)循環(huán)來(lái)檢查字符串是否已'\0'結(jié)尾首昔。哦寡喝,等一下,如果字符串不以'\0'結(jié)尾勒奇,那它怎么讓循環(huán)停下预鬓?不可能停下,所以無(wú)解赊颠。

無(wú)論你怎么做格二,你都不能在不知道字符串長(zhǎng)度的情況下檢查C字符串的有效性,這里safercopy包含了程度竣蹦。這個(gè)函數(shù)沒(méi)有相同的問(wèn)題顶猜,因?yàn)樗难h(huán)一定會(huì)中止,即使你傳入了錯(cuò)誤的大小痘括,大小也是有限的长窄。

譯者注:但是問(wèn)題來(lái)了滔吠,對(duì)于一個(gè)C字符串,你怎么獲取其大心尤铡疮绷?你需要在這個(gè)函數(shù)之前調(diào)用strlen,又是一個(gè)無(wú)限循環(huán)問(wèn)題嚣潜。

于是冬骚,bstring庫(kù)所做的事情就是創(chuàng)建一個(gè)結(jié)構(gòu)體,它總是包含字符串長(zhǎng)度懂算。由于這個(gè)長(zhǎng)度對(duì)于bstring來(lái)說(shuō)總是可訪問(wèn)的唉韭,它上面的所有操作都會(huì)更安全。循環(huán)是有限的犯犁,內(nèi)容也是有效的属愤,并且這個(gè)主要的缺陷也不存在了。BString庫(kù)也帶有大量所需的字串操作酸役,比如分割住诸、格式化、搜索涣澡,并且大多數(shù)都會(huì)正確并安全地執(zhí)行贱呐。

bstring中也可能有缺陷,但是經(jīng)過(guò)這么長(zhǎng)時(shí)間入桂,可能性已經(jīng)很低了奄薇。glibc中也有缺陷,所以你讓程序員怎么做才好呢抗愁?

使用 bstrlib

有很多改進(jìn)后的字符串庫(kù)馁蒂,但是我最喜歡bstrlib,因?yàn)樗挥幸粋€(gè)程序集蜘腌,并且具有大多數(shù)所需的字符串功能沫屡。你已經(jīng)在使用它了,所以這個(gè)練習(xí)中你需要從Better String獲取兩個(gè)文件撮珠,bstrlib.cbstrlib.h沮脖。

下面是我在liblcthw項(xiàng)目目錄里所做的事情:

$ 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文件芯急,來(lái)將它移動(dòng)到新的位置勺届,并且修復(fù)OSX上的bug。下面是差異:

25c25
< #include "bstrlib.h"
---
> #include <lcthw/bstrlib.h>
2759c2759
< #ifdef __GNUC__
---
> #if defined(__GNUC__) && !defined(__APPLE__)

我把包含修改為<lcthw/bstrlib.h>娶耍,然后修復(fù)2759行ifdef的問(wèn)題免姿。

學(xué)習(xí)使用該庫(kù)

這個(gè)練習(xí)很短,只是讓你準(zhǔn)備好剩余的練習(xí)伺绽,它們會(huì)用到這個(gè)庫(kù)养泡。接下來(lái)兩個(gè)聯(lián)系中嗜湃,我會(huì)使用bstrlib.c來(lái)創(chuàng)建Hashmap`數(shù)據(jù)結(jié)構(gòu)奈应。

你現(xiàn)在應(yīng)該閱讀頭文件和實(shí)現(xiàn)澜掩,之后編寫(xiě)tests/bstr_tests.c來(lái)測(cè)試下列函數(shù),來(lái)熟悉這個(gè)庫(kù):

bfromcstr

從C風(fēng)格字符串中創(chuàng)建一個(gè)bstring杖挣。

blk2bstr

與上面相同肩榕,但是可以提供緩沖區(qū)長(zhǎng)度。

bstrcpy

復(fù)制bstring惩妇。

bassign

將一個(gè)bstring賦值為另一個(gè)株汉。

bassigncstr

bsting的內(nèi)容設(shè)置為C字符串的內(nèi)容。

bassignblk

bsting的內(nèi)容設(shè)置為C字符串的內(nèi)容歌殃,但是可以提供長(zhǎng)度乔妈。

bdestroy

銷(xiāo)毀bstring

bconcat

在一個(gè)bstring末尾連接另一個(gè)氓皱。

bstricmp

比較兩個(gè)bstring路召,返回值與strcmp相同。

biseq

檢查兩個(gè)bstring是否相等波材。

binstr

判斷一個(gè)bstring是否被包含于另一個(gè)股淡。

bfindreplace

在一個(gè)bstring中尋找另一個(gè),并且將其替換為別的廷区。

bsplit

bstring分割為bstrList唯灵。

bformat

執(zhí)行字符串格式化,十分便利隙轻。

blength

獲取bstring的長(zhǎng)度埠帕。

bdata

獲取bstring的數(shù)據(jù)。

bchar

獲得bstring中的字符玖绿。

你的測(cè)試應(yīng)該覆蓋到所有這些操作搞监,以及你從頭文件中發(fā)現(xiàn)的更多有趣的東西。在valgrind下運(yùn)行測(cè)試镰矿,確保內(nèi)存使用正確琐驴。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市秤标,隨后出現(xiàn)的幾起案子绝淡,更是在濱河造成了極大的恐慌,老刑警劉巖苍姜,帶你破解...
    沈念sama閱讀 221,635評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件牢酵,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡衙猪,警方通過(guò)查閱死者的電腦和手機(jī)馍乙,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,543評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門(mén)布近,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人丝格,你說(shuō)我怎么就攤上這事撑瞧。” “怎么了显蝌?”我有些...
    開(kāi)封第一講書(shū)人閱讀 168,083評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵预伺,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我曼尊,道長(zhǎng)酬诀,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 59,640評(píng)論 1 296
  • 正文 為了忘掉前任骆撇,我火速辦了婚禮瞒御,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘神郊。我一直安慰自己肴裙,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,640評(píng)論 6 397
  • 文/花漫 我一把揭開(kāi)白布屿岂。 她就那樣靜靜地躺著践宴,像睡著了一般。 火紅的嫁衣襯著肌膚如雪爷怀。 梳的紋絲不亂的頭發(fā)上阻肩,一...
    開(kāi)封第一講書(shū)人閱讀 52,262評(píng)論 1 308
  • 那天,我揣著相機(jī)與錄音运授,去河邊找鬼烤惊。 笑死,一個(gè)胖子當(dāng)著我的面吹牛吁朦,可吹牛的內(nèi)容都是我干的柒室。 我是一名探鬼主播,決...
    沈念sama閱讀 40,833評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼逗宜,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼雄右!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起纺讲,我...
    開(kāi)封第一講書(shū)人閱讀 39,736評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤擂仍,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后熬甚,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體逢渔,經(jīng)...
    沈念sama閱讀 46,280評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,369評(píng)論 3 340
  • 正文 我和宋清朗相戀三年乡括,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了肃廓。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片智厌。...
    茶點(diǎn)故事閱讀 40,503評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖盲赊,靈堂內(nèi)的尸體忽然破棺而出铣鹏,到底是詐尸還是另有隱情,我是刑警寧澤角钩,帶...
    沈念sama閱讀 36,185評(píng)論 5 350
  • 正文 年R本政府宣布吝沫,位于F島的核電站呻澜,受9級(jí)特大地震影響递礼,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜羹幸,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,870評(píng)論 3 333
  • 文/蒙蒙 一脊髓、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧栅受,春花似錦将硝、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,340評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至而芥,卻和暖如春律罢,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背棍丐。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,460評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工误辑, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人歌逢。 一個(gè)月前我還...
    沈念sama閱讀 48,909評(píng)論 3 376
  • 正文 我出身青樓巾钉,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親秘案。 傳聞我的和親對(duì)象是個(gè)殘疾皇子砰苍,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,512評(píng)論 2 359

推薦閱讀更多精彩內(nèi)容