C言語入門|文字列を扱うための共通文化

ここまでの節で、私たちは C言語の文字列について、少しずつ正体を明らかにしてきました。

  • 文字列の実体は char 配列であること
  • 文字列の終わりは \0 で決まること
  • \0 がなければ文字列として成立しないこと

これらはすべて、C言語における「仕様」でもあり、「考え方」でもあります。

そして実は、ここで学んだ内容は
一部の人だけが知っていればよい知識ではありません。

C言語に関わる開発者全員が、
無意識のうちに共有している「共通文化」なのです。

文字列という「文化」

C言語の世界では、すべての文字列について、

業界ルールに沿ったメモリの使い方を前提とする

という文化が存在します。

この文化は、誰かが勝手に決めたローカルルールではありません。
C言語の標準ライブラリ、構文、慣習、設計思想そのものが、
この文化を前提として作られているのです。

たとえば printf 関数の %s は、

  • char 配列の先頭から読み始め
  • \0 に到達した時点で
  • 「ここまでが文字列」と判断する

という動作をします。

これは「printf が親切だから」ではありません。
C言語の世界では、それが当たり前だからです。

標準関数も文化の上に成り立っている

printf だけではありません。

この文字列文化を前提として作られているものには、次のようなものがあります。

分類内容
表示printf の %s
コピーstrcpy, strncpy
比較strcmp
長さ取得strlen
連結strcat

これらの関数はすべて、

  • 文字列は \0 で終わる
  • \0 より後ろは文字列ではない

という共通認識のもとで動作しています。

もし \0 が存在しなければ、
これらの関数は どこまで処理すればよいのか判断できません。

なぜ「文化」と呼ぶのか

「仕様」や「ルール」と言われると、

  • 文法で強制される
  • 書かなければコンパイルエラーになる

という印象を持つかもしれません。

しかし、文字列に関するルールは違います。

  • 文法上は書けてしまう
  • コンパイルも通ってしまう
  • 実行もできてしまう

それでも、
守らなければ即バグになる

こうした性質を持つため、
C言語ではこれを「文化」「常識」として共有してきました。

文字列文化に根ざした代表例

この文字列文化に強く根ざしている代表的なものが、次の2つです。

  • 文字列リテラル
  • 文字列操作の標準関数

このあとで順に見ていきますが、
どちらも「ヌル終端があること」を前提に設計されています。

文字列リテラルは文化を自動で守ってくれる

ここで、1つ安心できる話があります。

私たちはこれまで、文字列を使うたびに
\0 を明示的に書いてきたわけではありません。

それでも問題が起きなかったのは、

文字列リテラルそのものが
文字列文化を自動で守ってくれる存在だから

です。

同じ意味になる3つの書き方

次の3行は、すべて同じ意味になります。

char str[1024] = "hello";
char str[1024] = {'h', 'e', 'l', 'l', 'o', '\0'};
char str[1024] = {104, 101, 108, 108, 111, 0};

文字列リテラルを使うと、

  • 最後に \0 を自動で付ける
  • 開発者が意識しなくても文化を守る

という処理が、
C言語の構文として保証されているのです。

「自動的に末尾に追加されるゼロ」

ここで重要なポイントを整理しておきましょう。

項目内容
自動で \0 が付く文字列リテラルを使ったときだけ
自分で管理が必要char 配列を直接操作するとき

つまり、

文字列リテラルは安全
文字列操作は油断すると危険

というわけです。

文字列宣言でよくある誤り

では、次のコードを見てみましょう。

char str[5] = "hello";

一見、問題なさそうに見えますが、
これは 非常によくある危険なミス です。

なぜ危険なのか

hello は 5文字ですが、

  • 文字列として成立するには
  • 最後に \0 が必要

つまり、必要な要素数は 6 です。

char str[6] = "hello";

としなければなりません。

「1024文字入る」と思ってはいけない理由

これは、かつて結んだ String 型のルールにも直結します。

ルール意味
1024文字入ると思ってはいけない\0 の分が必要

1024 要素の char 配列に、
1024 文字の文字列を入れると、
\0 が入りきらずに溢れてしまいます。

文字列配列を安全に宣言する方法

こうしたミスを防ぐために、
おすすめしたい構文があります。

char str[] = "初期値";

この書き方を使うと、

  • 必要な要素数が自動計算される
  • \0 を含めたサイズが確保される

ため、宣言時のオーバーランを防げます。

それでも油断はできない

ただし、これは 万能の安全策ではありません。

char str[] = "hello";
char longstr[] = "helloworld";
memcpy(str, longstr, 11);

このように、

  • 宣言時は安全でも
  • 後から上書きすると

簡単にオーバーランは発生します。

文字列文化を身につけるということ

ここまでの内容をまとめると、
C言語で文字列を扱うということは、

char 配列を
文字列文化に従って
常に慎重に扱う

ということにほかなりません。

これを「面倒」と感じるか、
「制御できて気持ちいい」と感じるかで、
C言語との付き合い方は大きく変わります。

次の記事へ

次は、

  • 文字列リテラルのもう少し深い挙動
  • ポインタとの関係
  • 書き換え可能かどうかの違い

を見ていきます。

文字列文化を理解した今なら、
次の話もずっとクリアに見えるはずです。