愚かな法学C練習36:より安全な文字列

4859 ワード

練習36:より安全な文字列
原文:Exercise 36:Safer Strings
ドラゴン
既に練習26でdevpkgを構築しているときにBetter Stringライブラリを紹介しています.この練習はあなたに今からbstringライブラリを熟知させ、Cスタイルの文字列がなぜ非常に悪いのかを理解させます.その後、liblcthwのコードを変更してbstringを使用する必要があります.
なぜCスタイルの文字列は非常に悪いのか
人々がCの問題について話すとき、「文字列」の概念は永遠に最も重要な欠陥の一つである.あなたはすでにそれらを使ったことがあり、私もそれらの様々な欠陥について話したことがありますが、なぜC文字列に欠陥があるのか、なぜずっとこのようにしているのかについて明確な説明がありません.私は今説明してみます.一部の原因はCスタイルの文字列が数十年の使用を経て、非常に悪いものであることを示す十分な証拠があります.
指定されたCスタイル文字列では、有効かどうかを検証することはできません.
  • '\0'で終わるC文字列が有効です.
  • 無効なC文字列を処理する任意のループは無限である(またはバッファオーバーフローをもたらす).
  • C文字列には長さが指定されていないので、ループが正しく終了しているかどうかを確認する唯一の方法は、ループを巡回することです.
  • であるため、限られたループを介さずにC文字列を検証することは不可能である.

  • この論理は非常に簡単だ.無効な文字列がループを停止させないため、C文字列が有効であるかどうかを検証するループを作成することはできません.このように、唯一の解決策はサイズを含むことです.大きさが分かれば、無限ループの問題を避けることができます.練習27で私があなたに示した2つの関数を観察すると、
    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が一番好きです.プログラムセットが1つしかなく、必要な文字列機能がほとんどあるからです.すでに使用しているので、この練習ではBetter Stringから2つのファイル、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上のバグを修復したことがわかります.次の違いは、次のとおりです.
    25c25
    < #include "bstrlib.h"
    ---
    > #include 
    2759c2759
    < #ifdef __GNUC__
    ---
    > #if defined(__GNUC__) && !defined(__APPLE__)
    
    に修正し、2759行ifdefの問題を修正しました.
    このライブラリの使用を学習する
    この練習は短いですが、残りの練習を用意させるだけで、このライブラリに使用されます.次の2つの連絡では、bstrlib.cを使用してHashmap`データ構造を作成します.
    ヘッダファイルと実装を読んで、tests/bstr_tests.cを作成して次の関数をテストして、このライブラリを熟知する必要があります.bfromcstr
    Cスタイル文字列からbstringを作成します.blk2bstr
    上記と同じですが、バッファ長を指定できます.bstrcpy
    コピーbstring.bassign
    1つのbstringを別の値に割り当てます.bassigncstr bstingの内容をC文字列の内容に設定します.bassignblk bstingの内容をC文字列の内容に設定しますが、長さは指定できます.bdestroy bstringを破棄します.bconcat
    1つのbstringの末尾にもう1つを接続します.bstricmp
    2つのbstringを比較し、戻り値はstrcmpと同じである.biseq
    2つのbstringが等しいかどうかを確認します.binstr
    1つのbstringが別のものに含まれているかどうかを判断する.bfindreplace
    1つのbstringにおいて別のものを探し、それを別のものに置き換える.bsplit bstringbstrListに分割する.bformat
    文字列フォーマットを実行すると便利です.blength bstringの長さを取得します.bdata bstringのデータを取得します.bchar bstringの文字を取得します.
    あなたのテストは、これらの操作と、最初のファイルから発見されたもっと面白いものに上書きする必要があります.valgrindでテストを実行し、メモリが正しく使用されていることを確認します.