Cによるプログラミング入門6
配列と文字列

 同じタイプのデータを複数扱いたいときがあります。たとえば、ユーザに整数を10個入力してもらい、そのデータをいろいろなことに使いたいとします。10個の整数値を保持しておくためには、10個のint型変数が必要になりますね。すると、

int a, b, c, d, e, f, g, h, i, j;

とすればよさそうです。しかし、ちょっと面倒です。変数の数が100個になったらどうでしょう。
 そのようなときに、配列というものが使えます。


 たとえば、

int a[10];

と書くと、10個のint型変数を同時に定義したことになります。その10個の変数とは、

a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8], a[9]

です。
 ここで、[]内の整数は添え字などとよばれるものです。添え字が1から10ではなく、0から9であることに注意してください。0から9で、確かに10個ですね。Cでは、数は0から数えるのです。
 このようなものを配列といい、a[0]、...a[9]のような個別の変数を配列要素などといいます。
 なお、Cの世界にはまっていくと、日常生活でも「1、2、3、...」とは数えなくなり、「0、1、2、...」と数えるようになるそうです。その内、他人に「なんでおまえ0から数えてんの?」と聞かれて、「えっ。君は違うのか?」と応えるようになるとか。いや、そこまでいけば立派です(?)。

 たとえば、ユーザに整数を5個入力してもらい、それを逆順に出力するプログラムを書いてみましょう。

/* reverse.c */
#include <stdio.h>

int main(void)
{
    int a[5];
    /*  a[0]、 a[1]、 a[2]、 a[3]、 a[4]が定義されたことになる */
    int i;
    for(i = 0; i < 5; i++){
        printf("整数を入力してください:");
        scanf("%d", &a[i]);
    }
    printf("入力された整数を逆順に出力します。\n");
    for(i = 4; i >= 0; i--){
        printf("%d\n", a[i]);
    }
    return 0;
}

Fig.1 reverse.exeの実行例

 for文では、ループ変数にiを使っています。たとえば、はじめのfor文では、、iが0から4まで動く間に、a[i]がa[0]からa[4]まで動いていくわけです。
 次のfor文の使い方はわかるでしょうか。iが4からはじまり、0以上の間、1ずつ減らして処理を進めているのです。その結果、iは4から0まで動き、a[4]、a[3]、...a[0]の順で出力されるのです。


 revers.cでは、ユーザに値を入力してもらいましたが、配列要素にはじめから値を入れておきたいこともあります。たとえば、

int a[5];
a[0] = 0;
a[1] = 100;
a[2] = 200;
a[3] = 300;
a[4] = 400;

などとすることもできますが、aを定義するときに、

int a[5] = {0, 100, 200, 300, 400};

としても、同じ結果になるように決められています。
 また、このようにすると、要素の数も(自動的に)わかるので、省略してもよいことになっています。たとえば、

int a[] = {0, 100, 200, 300, 400};

と書いても上と同じことになるわけです。ここで、aのあとの[]の中が空であることをよく見ておいてください。これは書き落しではなく、わざと書かなかったのです。それでも、よいのです。
(ただし、「= {0, 100, 200, 300, 400};」などを付けて、配列の要素に一度に値を与えるということは、配列を生成するときにしかできません。配列をいったん生成した後で、「= {0, 100, 200, 300, 400};」を付けるようなことはできないのです。)


 次に、配列の一般論に続いて、文字の配列と「文字列」の説明をしたいと思います。
 一般に、データの種類を型というのでした。普通の整数を表す型はintですね。文字を表す型はcharというものです。つまり、

char c;

とかけば、cが「文字のいれもの」になるわけです。(変数はデータのいれものでした。)
 実は、プログラム中の「文字」は、他のものと区別するため、「'」で囲むことになっています。そのため、cにはじめから「aという文字」を入れておくには、

char c = 'a';

などとします。
 文字はこのように扱えるのですが、「人の名前」のようなもの、つまり文字列(文字の列)はどうすればよいでしょう。コンピュータの世界では、文字と文字列は違うものなのです。
 文字列は、「文字がつながったもの」と考えることができるので、Cでは、charの配列に格納することにしています。つまり、文字列のいれものは、文字(char)の配列なのです。それは、たとえば、

char str[100];

のように書くことができます。
 これで「charの配列」、つまり「str[0]、str[1]、...str[99]という100個のcharのいれもの」ができたことになり、そこに文字列を格納できるのです。ただし、いくつか、注意があります。

 まず、Cでは、文字列の最後に「\0」というものをいれることにしているのです。こう書くと\(「国際的な環境」では円マークではなく、バックスラッシュ)と0(ゼロ)の2文字のように見えますが、プログラム内では、ヌル文字とよばれる1文字として扱われます。悩まないでください。そう決められているから、そうなのです。そして、このヌル文字が、文字列の最後を意味するのです。

 ちょっとわかりにくいと感じるかもしれません。しかし、重要なことなのです。
 たとえば、文字数を考えるときに、ヌル文字の存在を忘れるわけにはいきません。上のstrは「100個の要素を持つchar配列」なので、一見、全部で100文字までの文字列を格納できそうですが、そうではないのです。最後に必ず\0を入れることになっているので、実際の文字は、99文字しか入らないのです。
 なお、ここで言う、「文字数」は、半角英数字を考えた場合です。日本語の文字(全角文字)は、普通、半角英数字2文字分のスペース(メモリ上のスペース)に1文字だけ入れられることになっているのです。そのため、上のstrには、英数字なら99文字入るわけですが、日本語文字なら、その半分(割り切れない分は切捨て)ということなり、結局49文字しかはいらないわけです。
 はじめから、全角文字のみを入れる予定の場合、

char str2[101];

のように、要素数を奇数にする人もいます。こうすると、str2は、日本語の場合、101からヌル文字分の1を引いた100の半分、つまり、50文字を格納できることになるのです。ただし、全角文字に半角文字を混ぜることもできるので、気持ちの問題かもしれません。

 文字の配列に値を入れる(初期化する)には、intのときと同様にできます。ただし、文字は「'」で囲むことを忘れないでください。また、単に「複数の文字」ではなく「文字列」として扱う場合には、最後に「\0」を入れるのでした。すると、「hello」という文字列を配列に入れるには、

char str3[6] = {'h', 'e', 'l', 'l', 'o', '\0'};

とすればよいことになります。最後の「'\0'」を忘れないように。また、このように初期化するときには、配列の要素数は省略してよいので、これを

char str3[] = {'h', 'e', 'l', 'l', 'o', '\0'};

としても問題ありません。
 ただ、それにしても、ちょっと面倒ですね。実は、「hello」という文字列は"hello"という形で表せることになっています。そして、これを使って、

char str3[] = "hello";

と書いても、上のコードと同じ意味になるのです。このように書くと、hは6個(5個ではありません)の要素を持つようになり、それらが順番に、'h'、 'e'、 'l'、 'l'、 'o'、 '\0'で初期化されるのです。最後に自動的に「'\0'」が付くこともおぼえておいてください。そのようになっているのです。
 この文字列を出力するにはprintfが使えます。たとえば、

printf("私が最初にならった英単語は%sです。\n", str3);

というコードがあったとすると、「""」で囲まれた中の「%s」のところに、hの文字列が入れられ、

私が最初にならった英単語はhelloです。

という文字列が出力されることになります。
 整数の出力のときは「%d」というものを使いましたね。「%s」は文字列を意味するマークなのです。

 ちなみに、「文字」は「%c」で表されます。たとえば、上のstr3に対して

printf("第0番の文字は%cです。\n", str3[0]);

とすると、

第0番の文字はhです。

と出力されるのです。
 str3[0]は、'h'、 'e'、 'l'、 'l'、 'o'、 '\0'という要素を持つ文字の配列で、配列の要素番号は0からはじまるため、str[3]は「'h'」なのです。そこで、「'h'」が、printfの「%d」のところに入れられ、出力されるわけです。

 ここでサンプルを見てみましょう。

/* mojiretsu.c */
#include <stdio.h>

int main(void)
{
    char str3[] = "hello";
    int i;
    printf("私が最初にならった英単語は%sです。\n", str3);
    printf("\nその...\n\n");
    for(i = 0; i < 5; i++){
        printf(" 第%d番の文字は%c\n", i, str3[i]);
    }
    printf("\nです。\n");
    return 0;
}

Fig.2 mojiretsu.exeの実行画面

 文字ごとの出力は少し工夫しました。「printf(" 第%d番の文字は%c\n", i, str3[i]);」とすると、iが%dのところ、str3[i]が%cのところに入れられて出力されるのです。


 最後に、ユーザから文字列の入力を受け取る方法を説明して、今回は終わりにしましょう。
 ちょっと復習すると、int型変数xに整数の入力を受け取るなら、

scanf("%d", &x);

でした。同様に、「char str[100];」のように「文字列のいれもの」を用意したら、

scanf("%s", str);

とすればよいことになっています。「%sは文字列を意味する」のです。これで、ユーザが入力した文字列に自動的に「\0」が付けられたものが、strに格納されるのです。
 ただ、よく見ると、strは、「&str」ではなく、ただの「str」となっています。文字列の入力を受け取るには、このように書くのです。そう認めていただければ、とりあえずそれでよいのですが、「intのときは&を付けるのに、文字列のときに&を付けないのはなぜだろう」と悩むかもしれません。
 実は、これは初心者には一番難しいところです。その説明は、簡単には、次のようになります。

  1.scanfには、データを格納する変数のメモリ上の場所を与える。
  2.たとえば、xをint型変数とすると、xの位置は&xで与えられる。
  3.配列の場合、配列を表す名前が「配列のあたまの位置」を表す。

 配列は、いわば「複数の変数の集まり」です。したがって、「配列の名前」は、普通の変数の名前とは少し異なり、特別なものなのです。
 しかし、初心者に、この説明だけではわかりにくいと思います。より詳しくは、次回に説明します。今重要なことは、「scanf("%s", str);」で、ユーザが入力した文字列を、strに格納できる、ということです。これを使って、まず、慣れてみてください。

 たとえば、次のような、サンプルが書けます。これは、「名前を聞いて、あいさつするプログラム」です。

/* name.c */
#include <stdio.h>

int main(void)
{
    char str[101];
    printf("お名前を入力してください。\n");
    scanf("%s", str);
    printf("%sさん、こんにちは。\n", str);
    return 0;
}

Fig.3 nameの実行
(ユーザは、「猫田猫夫」と入力した。)

課題6


C入門目次

前のページ  後のページ