第23回
データ構造(2)~文字列という特殊な配列

文字列を初期化する関数

文字列が単にchar型配列でしかないことによって、プログラマーのちょっとした勘違いがいろいろと面白い現象を引き起こします。「面白い」と書きましたが、実務ではとんでもないバグともなり得るため、注意が必要です。そのような事態を回避するには、とにかく「宣言したら初期化する」ことです。

スペースで初期化する

文字列の扱いに関するミスを防ぐためには、文字列を宣言したらまず初期化する──ことに尽きます。

strcpy関数で何らかの文字列定数を設定してしまうのもひとつの方法です。例えば以下のように、スペースで埋めることも考えられます。
char buffer[] = "                ";
このようにすれば、16個のスペースと終端のNULLを含めた都合17バイトの配列が生成され、それをbufferという名前で扱えます。しかし、必要数だけのスペースを""で囲んだ文字列定数として代入するソースは、あまりスマートではありません。

初期化用の関数を用意する

そこで、リスト3のような「文字列を指定した数の'\0'で埋める」関数を作っておけば、その後に要素数以下のバイト数の文字列をコピーした場合、その後ろはすべて終端を示すNULL('\0')となります。第1引数に対象とするchar型配列名、第2引数に要素数を設定します。

ソース中の
s[i] = '\0';
の箇所を
s[i] = ' ';
とすれば、NULLの代わりにスペースで埋めることもできます。

他の言語では、文字列変数を宣言するとその中身が数値の0やスペースで埋められます。Cでは「文字列を宣言する」という処理自体が存在しないため、このような関数を用意して『char型配列を文字列として宣言したら、次に必ず初期化する』というスタイルを採るのが安全です。

なお、リスト3のstrinit関数では配列の要素を指定するために[]を使っていますが、これはポインタを使うとさらにスマートで、さらに高速な処理を行えます。ただ、現時点ではポインタについて詳しく説明していないため、このような形を紹介しておくことにします。

リスト3:文字列の要素を指定した数のNULLで埋める関数
void strinit(const char s[], int num)
{
  int i;

  for (i = 0; i < num; i++) {
    s[i] = '\0';
  }
}

あとがき

hiropの『ちょっと気になる専門用語』~《文字列》

「文字列」という言葉は、実はかなり特殊なコンピュータ用語です。パソコンが一般に普及した1990年代のはじめ頃、雑誌や書籍で頻繁に登場するようになって、今では多くの人が何の疑問も抱かずにこの「文字列」という言葉を受け入れています。

しかし、そもそも「文字が複数個並んだもの」という状態を示す「文字列」という言葉は、人間の自然な言語生活にとって無意味な存在です。なぜなら
今日はよい天気です。
という『ある意味を持った言葉』も
%#?だ6!&
という『何の意味も持たない記号列』も、プログラムでは同じ文字列として受け入れることができるからです。

普通の人間が普通に生活している状況で、果たして「%#?だ6!&」のような言葉を受け入れることができるでしょうか?

文字列は、必ずしも「文」ではありません。並んだ文字に意味があるかどうかには一切関係なく、コンピュータはそれを文字列として画面に表示したり、入力として受け入れたりします。

しかし人間は、意味のない言葉を受け入れることはできません。文章だけではなく、画像も音声も、コンピュータは人間の扱う情報を様々な形で処理できるようになりましたが、そこに何らかの意味を見出すかどうかを意識している訳ではありません。「文字列」と「文」が異なるものであるという現実は、そのことを如実に物語っています。

文章ではなく文字列という表現が存在するのは、コンピュータがまだまだ人間に近付いていないことを示す証拠だと思います。