Entry

本日のやなやつ - strsub() 修正してみた

2009年02月13日

こっそり添削ってのも,我ながらやなやつだと思うんですけど,こちらの話から。

文字列から部分文字列を取得します。
strsub には文字列と開始位置、取得文字列長を渡します。

codeなにがし::C言語 strsub

ちょっとこのコードには危なっかしさを感じてしまう。多分,さらっと書いたからこうなってるんだろうけれど,こゆ便利コードは色々と気を配らないと,後々ひどいことになってしまいます。具体的に挙げるとこんな感じ。

  • 変更しないことが分かっている入力ポインタには const をつけよう。
  • 文字数や位置を表す数値は,(singned) int ではなく size_t を使おう。
  • 与えられた入力から得られるポインタが,不正な場所を指さないようにチェックしよう。
  • calloc(3) の戻り値はしっかり検査しよう。
  • どうしようもない不正な入力に対しては,assert(3) を活用しよう。

数値を (signed) int でなく size_t にするのは,負数がくると面倒だからです。文字列長の上限が(通常)2^32 バイト(4GB)に拡張されるってのもあるけれど,32bit OS でユーザが使える仮想アドレス空間は,大抵 2GB に制限されるので,こっちはあまり意味がない。

それと,これはよく話に出るんですけど,assert(3) を使うとチェック処理の分だけ遅くなるんでね?とかいった話があります。これ,あまり知られてないのかもしれないけれど,assert(3) は NDEBUG を定義してコンパイルすると,その分のコードは生成されないんですね。リリース用にコンパイルする場合,NDEBUG を付きでコンパイルしておけば,assert(3) はなかったことになります。

というわけで,やばげな条件チェックでは assert(3) をガンガン利用しましょう。つか,あたしがそうしてるだけなんですけど。

つことで直してみた。

#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

char *
strsub(const char *string, size_t start, size_t nbyte)
{
    char *str = NULL;
    const char *ptr = NULL;
    size_t len;

    assert(string != NULL);
    len = strlen(string);
    assert(len > start);
    assert(nbyte > 0);
    assert(nbyte <= len);
    assert(len >= start + nbyte);
    str = (char *)calloc((nbyte + 1), sizeof(char));
    if (str != NULL) {
        ptr = string + start;
        strncpy(str, ptr, nbyte);
    }

    return str;
}

////////////////////////////////////////////////////////////////////////////////
void
xfree(void *ptr)
{
    if (ptr != NULL) {
        free(ptr);
    }
}

void
put(const char *str)
{
    if (str != NULL) {
        puts(str);
    } else {
        puts("failed");
    }
}

int
main(int argc, char *argv[])
{
    char *str;

    /**************************************************************************
        normal case
     *************************************************************************/
    /* test 1 */
    str = strsub("1234567", 0, 7);
    put(str);
    xfree(str);
    /* test 2 */
    str = strsub("1234567", 2, 5);
    put(str);
    xfree(str);
    /**************************************************************************
        abnormal case
     *************************************************************************/
#if 0
    /* test 3 (nbyte > len) */
    str = strsub("1234567", 0, 8);
    put(str);
    xfree(str);
#endif
#if 0
    /* test 4 (start + nbyte > len) */
    str = strsub("1234567", 1, 7);
    put(str);
    xfree(str);
#endif
#if 0
    /* test 5 (string == NULL) */
    str = strsub(NULL, 0, 7);
    put(str);
    xfree(str);
#endif
#if 0
    /* test 6 (nbyte == 0) */
    str = strsub("1234567", 0, 0);
    put(str);
    xfree(str);
#endif

    return 0;
}

本日の格言。

テストコードは本体コードの3倍書け。

いや,今思いついただけなんですが。

Trackback
Trackback URL:
Ads
About
Search This Site
Ads
Categories
Recent Entries
Log Archive
Syndicate This Site
Info.
クリエイティブ・コモンズ・ライセンス
Movable Type 3.36
Valid XHTML 1.1!
Valid CSS!
ブログタイムズ

© 2003-2012 AIAN