strlenを自作する
文字列の終わりは誰が決めているか
第2章では、malloc で確保した領域が「何バイトあるか」を自分で覚えておく必要がありました。ところが文字列だけは事情が違います。char の並びの最後に ヌル文字 '\0' という番人が置かれていて、長さを覚えていなくても終わりが分かるようになっています。
char name[7] = "Tanaka";この配列の中身は 'T' 'a' 'n' 'a' 'k' 'a' '\0' の7バイトです。見えている文字は6つですが、必ず1バイト余分に要ります。要素数を7ではなく6と書くと終端が入らず、文字列として扱った瞬間にどこまでも読み続けてしまいます。
'\0' は数値としては 0 です。'0'(ゼロという文字、値は48)とは別物なので、比べるときに書き間違えると条件が一生成立しません。
strlen は数えているだけ
strlen は長さをどこかに保存しているわけではありません。呼ばれるたびに先頭から '\0' が出るまで1バイトずつ歩いて、歩数を返しています。だから同じ文字列に対して何度も呼べば、そのたびに走査が走ります。
自分で書くと次のようになります。
int my_strlen(const char *s) {
int n = 0;
while (*s != '\0') {
n++;
s++;
}
return n;
}第1章でやったポインタの歩き方がそのまま出てきます。s++ で1文字ぶん進み、*s でそこの1バイトを見ています。char は1バイトなので、s++ は素直に隣の番地へ移ります。
引数に付いている const は「この関数は指す先を書き換えません」という宣言です。呼ぶ側から見ると、渡した文字列が壊されない保証になります。読むだけの引数には付ける習慣を付けておくと、あとで自分を助けます。
戻り値が '\0' を含まない意味
my_strlen("Tanaka") は 6 を返します。終端の1バイトは数に入りません。この「見えている文字数」と「必要なバイト数」が1ずれることが、このあとの回でずっと効いてきます。文字列をコピーする器を用意するときは strlen + 1 バイト要る、と体に入れておいてください。
なお、ここで扱う名前はアルファベットに限ります。日本語の文字は1文字が複数バイトになるので、strlen が返すのは文字数ではなくバイト数になります。この違いは今は脇に置いて、1文字1バイトの世界で仕組みを掴みます。
では、自作の my_strlen を書いて、純正の strlen と同じ答えになるか確かめてみましょう。
要件
- my_strlen という関数を自分で書く。引数は const char *、戻り値は int
- 添字 [] ではなくポインタを進める形で走査する
- 1行目に 自作の結果は6です の形で表示する
- 2行目に strlenの結果は6です の形で表示する
入出力例
main("Tanaka") → "自作の結果は6です
strlenの結果は6です"
main("Ito") → "自作の結果は3です
strlenの結果は3です"
main("Yamamotokurosawa") → "自作の結果は16です
strlenの結果は16です"