🔤 文字列
🎯 ヌル終端の役割を理解し、必要な配列サイズを正しく見積もれる
char s[6] = "hello"; で6個ぶん必要だった理由を明かします。文字列の最後には、必ずヌル文字 '\0' が置かれるからです。
s[0] s[1] s[2] s[3] s[4] s[5]
+-----+-----+-----+-----+-----+-----+
| 'h' | 'e' | 'l' | 'l' | 'o' |'\0' |
+-----+-----+-----+-----+-----+-----+
ここまでが文字列、という印'\0' は値が0の文字です。数字の '0'(値48)とは別物なので、混同しないでください。画面には何も表示されず、ただ「ここで終わり」を意味します。
なぜ印が必要なのか
配列を渡されただけでは、どこまでが文字列なのか誰にも分かりません。Cは配列の長さを覚えていないからです。そこで終わりに目印を置くという約束を作りました。printf の %s も strlen も、この印に出会うまで前から順に読み進めます。
#include <stdio.h>
#include <string.h>
int main(void)
{
char s[6] = "cat";
printf("%zu\n", sizeof(s)); // 6 ← 箱の数(確保した大きさ)
printf("%zu\n", strlen(s)); // 3 ← 文字数('\0' の手前まで)
return 0;
}sizeof と strlen の違いは頻出です。箱の数と中身の文字数は別、と覚えてください。char s[6] = "cat"; の場合、s[3] が '\0' で、s[4] と s[5] も 0 で埋められます。
下のビジュアライザで、1文字ずつ '\0' を探しに行く様子を見てみましょう。
🧪 ここはメモリの中身を1ステップずつ追う「メモリビジュアライザ」の場面です(アプリ本体で動きます)。
印を失うとどうなるか
char s[5] = "hello"; // 5文字ぴったり。'\0' が入らない!
printf("%s\n", s); // どこまで読むか分からず暴走するこれはコンパイルが通ってしまいます。printf は '\0' を探して配列の外まで読み進め、たまたま見つかった 0 までを表示します。文字化けするか、境界外アクセスで異常終了するか、運が良ければ何事もなく動く——おなじみの未定義動作です。
罠・注意
'\0' と '0' と 0 と "0" はすべて別物です。'\0' は終端、'0' は文字のゼロ(値48)、0 は数値、"0" は1文字の文字列(2バイト)。s[n] = '\0'; を書く必要があります。書き忘れると文字列として使えません。strlen は呼ばれるたびに先頭から数え直します。ループの条件に i < strlen(s) と書くと、毎回数え直しが走って遅くなります。長さは先に変数へ入れておきましょう。'\0' を書き込むと、そこが新しい終わりになります。後ろの文字は消えていませんが、文字列としては見えなくなる——この性質は t09-08 の定石で活用します。まとめ: 文字列の最後には必ず '\0'。配列は「文字数+1」で確保し、sizeof(箱の数)と strlen(文字数)を区別する。
文字列の終わりを示すために置かれる、値0の特殊な文字'\0'のこと。これが無いと、どこまでが文字列かコンピュータが判断できなくなる。strlenなどの関数は、この'\0'を目印にして文字列の長さを数えている。
配列の要素数を超えた添字にアクセスしてしまう重大な事故のこと。コンパイルは通ってしまうため、実行時に予期せぬ動作を引き起こす。他の変数の領域を書き換えてしまい、原因の分かりにくいバグにつながりやすい。
もっと先へ:ポインタ・メモリ・ファイル入出力・セキュアコーディングを含む全26トラックと、段位検定・模試のフルセットは完全版に収録しています。