5.4 配列初期化子
5.4.1 配列変数の初期化
通常の変数の宣言では、同時に変数の値を初期化することができました。配列も、これと同様に宣言時に初期化することができます。配列のように、単純な型(int や char など)ではなく、単純な型を集合させたような記憶領域を合成体と呼びます。合成体を初期化するには大括弧 { } で初期値のリストを指定します。これは、次のような構文になります。
型 変数名[要素数] = { 第1要素値 , 第2要素値 , 第3要素値 , ... };
リストで指定する各要素の初期値の数は、配列の要素数を超えてはいけません。逆に、初期値の数が配列のサイズよりも少ない場合は、残りの要素がゼロで初期化されます。例えば、次は int 型の配列を初期化子付きで定義しています。
int iArray[4] = { 10 , 100 , 1000 , 10000 };
この配列は、配列の先頭から 10 , 100 , 1000 , 10000 という値で初期化されます。このように、配列の各要素の値があらかじめ決定されているのであれば、初期化子を用いた方がソースコードがスリムになります。
#include <stdio.h> int main() { int iArray[4] = { 10 , 100 , 1000 , 10000 } , iCount; for(iCount = 0 ; iCount < 4 ; iCount++) printf("iArray[%d] = %d\n" , iCount , iArray[iCount]); return 0; }
コード1は、iArray 配列変数を宣言と同時に初期化しています。for ステートメントを用いてこの配列の値を表示させているので、プログラムを実行すれば、配列が正しく初期化されていることを確認できるでしょう。
リストの値が配列に初期値として与えられているのがわかりますね。もちろん浮動小数点でも初期化方法は同じです。
char 型配列を文字列として初期化したい場合、ひとつは連続した文字定数として初期化する方法があります。このとき、C言語の文字列のお約束である、配列末尾を NULL 文字のゼロにすることを忘れてはなりません。
#include <stdio.h> int main() { char chStr[6] = { 'K' , 'i' , 't' , 't' , 'y' , 0 }; printf("%s\n" , chStr); return 0; }
コード2の配列変数 chStr は初期化子で文字列を格納しています。初期化リストでは、文字列の各文字を、文字定数として指定し、その末尾には整定数 0 を指定しています。予想通り、このプログラムを実行すると Kitty という文字が画面に表示されます。
char 型の配列変数を文字配列として初期化する場合、二重引用符で囲んで初期化することができます。リテラル文字列は末尾に暗黙的にNULL 文字が含まれているので、その分のサイズを割り当てることも忘れないで下さい。日本語などの ASCII 以外の文字を使う場合は、文字コードに依存しますが、通常は 1 文字が 2 バイトで表現されるので注意が必要です。
#include <stdio.h> int main() { char chStr[6] = "Kitty"; printf("%s\n" , chStr); return 0; }
コード3は、プログラム的にはコード2とまったく同じです。chStr の初期化に連続した文字定数ではなく、リテラル文字列を用いている点でコード2と異なっています。文字配列の char 型配列変数を初期化するには、これがもっとも便利な書き方でしょう。
5.4.2 多次元配列の初期化
多次元配列の初期化を試みる場合、1 次元配列の初期化よりも多少複雑になります。多次元の場合は、その次元数だけ大括弧を { } 指定して、各次元用のリストを入れ子にして初期化することができます。例えば、2 次元配列の場合は次のように初期化します。
int iArray[2][2] = { { 1 , 2 } , { 3 , 4 } };
この場合、最初の { は iArray の初期化を表し、次の { は iArray[0] の初期化リストという構造になっています。すなわち { 1 , 2 } は iArray[0][0] と iArray[0][1] を初期化します。同様に、次の { 3 , 4 } は iArray[1] を初期化しています。配列の数よりもリストの数が少ない場合、残りの部分は 0 で初期化されます。
#include <stdio.h> int main() { int iCount1 , iCount2; int iArray[3][3] = { { 2 , 4 } , { 8 , 16 , 32 } }; for(iCount1 = 0 ; iCount1 < 3 ; iCount1++) { for(iCount2 = 0 ; iCount2 < 3 ; iCount2++) { printf("iArray[%d][%d] = %d\n" , iCount1 , iCount2 , iArray[iCount1][iCount2]); } } return 0; }
配列の初期化子に注目してください。 { 2 , 4 } は iArray[0] を初期化していますが iArray[0][2] の初期値が存在しないため iArray[0][2] は 0 で初期化されています。次の行の { 8 , 16 , 32 } は iArray[1] を初期化しています。初期化子はここで終了しているため iArray[2] の初期化リストは存在しません。そのため iArray[2] は全てが 0 で初期化されています。これは、実行結果を見て明らかです。
コード4で示した初期化方法は { } を入れ子にしていましたが、次のようにして単一のリストで初期化することも可能です。
int iArray[2][2] = { 1 , 2 , 3 , 4 };
この場合、配列の先頭から順にリストの値で初期化されます。最初は iArray[0] から初期化され、リストの先頭の 2 要素が使用されます。すなわち iArray[0][0] が 1 で、iArray[0][1] が 2 で初期化されるのです。そして、次の要素から iArray[1] が初期化されるという順番になります。この場合も、やはり配列の数よりもリストの値が少なければ、残りは 0 で初期化されます。
#include <stdio.h> int main() { int iCount1 , iCount2; int iArray[3][3] = { 2 , 4 , 8 , 16 , 32 , 64 , 128 , 256 }; for(iCount1 = 0 ; iCount1 < 3 ; iCount1++) { for(iCount2 = 0 ; iCount2 < 3 ; iCount2++) { printf("iArray[%d][%d] = %d\n" , iCount1 , iCount2 , iArray[iCount1][iCount2]); } } return 0; }
配列が、リストの先頭から順に正しく初期化され地えることが確認できます。初期化子で指定している値は 8 つであり、配列の要素数 9 よりも少ないため、最後の iArray[2][2] は 0 で初期化されています。こうした多次元配列の初期化は、例えば文字列テーブルを配列で実現する時などに便利でしょう。
#include <stdio.h> int main() { char chStr[3][8] = { "Kitty" , "Kitten" , "Feline" }; printf("%s : %s : %s\n" , chStr[0] , chStr[1] , chStr[2]); return 0; }
このプログラムは、多次元配列で文字列の配列を実現しています。文字列はそれ自体が文字配列なので、文字列の配列を作成するためには自動的に 2 次元配列を作らなければならないということになるのです。
5.4.3 要素数の省略
初期化子を指定している配列の場合、コンパイラは初期化リストから配列のようその数を想定することができます。そのため、0 で初期化する余分な要素を保有したい場合を除いて、初期化子を指定する配列のサイズを直接指定するのは冗長であると考えられます。初期化式から配列の要素数を限定できる場合、配列の宣言でサイズを省略することができます。ただし、その場合でも [ ] を省略することはできません。
int iArray[ ] = { ... }
これは、文字列を配列変数に初期化するときなどに役に立つでしょう。要素数を省略することで、誤ったサイズを割り当ててしまうような間違いを避けることができます。
#include <stdio.h> int main() { char chStr[] = "Kitty on your lap"; printf("%s\n" , chStr); return 0; }
コード7は、chStr 配列変数の要素数を省略しています。コンパイラは初期化子 "Kitty on your lap" を配列に格納するために必要なサイズを計算し、最適な要素数を割り当ててくれます。