
C言語の基本|2次元配列の文字列を表示する
1行ずつ取り出せば、複数の文字列もすっきり表示。2次元char配列の読み方を身につけましょう。
C言語では、1つの文字列をchar配列で表します。複数の文字列をまとめて管理したい場合は、char型の2次元配列が便利です。
たとえば、次のようなデータをまとめて保存できます。
- 複数の駅名
- 商品名の一覧
- 曜日の名前
- ユーザー名
- 状態を表すラベル
2次元char配列は、行と列を持つ表のような構造です。文字列を保存するときは、1行に1つの文字列を入れると考えます。
たとえば、次の配列には3つの駅名が入っています。
char stations[][10] = {"Tokyo", "Ueno", "Omiya"};それぞれの行は、次の文字列を表します。
| 行 | 表す文字列 |
|---|---|
| stations[0] | Tokyo |
| stations[1] | Ueno |
| stations[2] | Omiya |
stations[0]はTという1文字ではなく、Tokyoという1行分の文字列を表します。
この考え方が分かると、printfと %s を使った表示や、for文による繰り返し表示も自然に理解できるようになります。
2次元char配列の各行は1つの文字列
まず、次の2次元char配列を見てみましょう。
char animals[][7] = {"Dog", "Rabbit", "Panda"};この配列には、Dog、Rabbit、Pandaという3つの文字列が格納されています。
| 行番号 | 配列の指定 | 格納される文字列 |
|---|---|---|
| 0 | animals[0] | Dog |
| 1 | animals[1] | Rabbit |
| 2 | animals[2] | Panda |
最初の要素数は、文字列の個数を表します。2つ目の要素数は、1つの文字列を格納するための配列サイズです。
char 配列名[文字列の個数][1文字列分の配列サイズ];今回の宣言では行数を省略していますが、初期値が3つあるため、コンパイラが3行の配列だと判断します。
列数は7です。そのため、各行には7個のchar型要素が用意されます。
図1:2次元char配列の1行と1つの文字列

この図から分かること
2次元char配列では、1行が1つの文字列として使われます。
animals[0]にはDog、animals[1]にはRabbit、animals[2]にはPandaが格納されています。
各行には同じ数の要素が用意されます。Rabbitは6文字なので、文字列の終端を表す \0 を含めると7要素必要です。この最も長い文字列に合わせて、列数を7にしています。
配列の内部では文字が1つずつ並んでいる
animalsの内部を表にすると、次のようになります。
| 行 | 要素0 | 要素1 | 要素2 | 要素3 | 要素4 | 要素5 | 要素6 |
|---|---|---|---|---|---|---|---|
| animals[0] | D | o | g | \0 | 0 | 0 | 0 |
| animals[1] | R | a | b | b | i | t | \0 |
| animals[2] | P | a | n | d | a | \0 | 0 |
Dogは3文字なので、要素3に \0 が入ります。それより後ろの要素も、文字列リテラルによる初期化で値が指定されなかったため0になります。
Rabbitは6文字なので、最後の要素6に \0 が入ります。列数が7より小さいと、Rabbitを文字列として格納するための領域が足りません。
行と列の役割
2次元char配列では、行と列の役割を分けて考えると分かりやすくなります。
| 項目 | 役割 |
|---|---|
| 行 | どの文字列を選ぶか |
| 列 | 文字列内のどの文字を選ぶか |
| 行数 | 保存できる文字列の個数 |
| 列数 | 1つの文字列を保存するための配列サイズ |
animals[1]は、2行目の文字列Rabbitを表します。
animals[1]さらに列の添字まで指定すると、Rabbitの中にある1文字を表します。
animals[1][0]animals[1][0]は、Rabbitの先頭文字Rです。
animals[1][2]animals[1][2]は、Rabbitの3文字目bです。
1行全体と1文字の違い
2次元char配列では、添字をいくつ指定するかによって意味が変わります。
| 書き方 | 表すもの | 表示に使う書式指定子 |
|---|---|---|
| animals[0] | 1行目の文字列Dog | %s |
| animals[1] | 2行目の文字列Rabbit | %s |
| animals[0][0] | 1行目の先頭文字D | %c |
| animals[1][2] | 2行目の3文字目b | %c |
文字列全体を表示する場合は、行だけを指定します。
printf("%s\n", animals[0]);1文字を表示する場合は、行と列の両方を指定します。
printf("%c\n", animals[0][0]);%sで1行分の文字列を表示する
2次元char配列の各行は、printfと %s を使って表示できます。
printf("%s\n", animals[0]);
printf("%s\n", animals[1]);
printf("%s\n", animals[2]);実行結果は次のようになります。
Dog
Rabbit
Panda%sは、指定された位置から文字を順番に読み取り、\0が見つかったところで表示を終了します。
animals[0]を指定した場合は、次の順番で読み取られます。
- Dを表示する
- oを表示する
- gを表示する
- \0を見つける
- 表示を終了する
animals[0]は、1行目の先頭位置を表します。そのため、%sに渡すと、1行目に格納されている文字列全体を表示できます。
char型の2次元配列と数値の2次元配列
int型の2次元配列では、通常、行と列の両方を指定して1つの数値を取り出します。
int numbers[2][3] = {
{10, 20, 30},
{40, 50, 60}
};
printf("%d\n", numbers[0][1]);この場合、numbers[0][1]が表す20を表示します。
char型の2次元配列でも、1文字を取り出すなら行と列の両方を指定します。ただし、文字列として表示するときは、1行全体を指定できます。
| 配列 | 指定方法 | 表すデータ |
|---|---|---|
| int型2次元配列 | numbers[0][1] | 1つの整数 |
| char型2次元配列 | animals[0][1] | 1つの文字 |
| char型2次元配列 | animals[0] | 1つの文字列 |
for文で各行を順番に表示する
文字列を1つずつ個別に表示することもできます。
printf("%s\n", animals[0]);
printf("%s\n", animals[1]);
printf("%s\n", animals[2]);ただし、文字列の数が増えると、同じようなprintfを何度も書かなければなりません。
そこで、for文を使います。
for (int i = 0; i < 3; i++) {
printf("%s\n", animals[i]);
}変数iは、表示する行を表します。
| iの値 | 表示する行 | 表示される文字列 |
|---|---|---|
| 0 | animals[0] | Dog |
| 1 | animals[1] | Rabbit |
| 2 | animals[2] | Panda |
iを0から2まで変化させることで、3行に格納されている文字列を順番に表示できます。
図2:for文で文字列を1行ずつ表示する流れ

この図から分かること
for文の変数iは、2次元char配列の行番号として使えます。
iが0ならanimals[0]、iが1ならanimals[1]、iが2ならanimals[2]が選ばれます。それぞれを %s で表示することで、各行に格納された文字列を順番に取り出せます。
この場合、列を処理するための内側のfor文は必要ありません。%sが、各行の先頭から \0 までをまとめて表示するからです。
なぜ二重ループを使わなくてもよいのか
2次元配列を扱うと聞くと、必ず二重ループが必要だと思うかもしれません。
文字を1つずつ処理する場合は、行と列を動かす二重ループを使えます。
for (int i = 0; i < 3; i++) {
for (int j = 0; animals[i][j] != '\0'; j++) {
printf("%c", animals[i][j]);
}
printf("\n");
}この処理では、内側のfor文が各文字を1つずつ表示しています。
一方、文字列全体を %s で表示する場合は、内側のループをprintfに任せられます。
for (int i = 0; i < 3; i++) {
printf("%s\n", animals[i]);
}| 表示方法 | ループ | 特徴 |
|---|---|---|
| %sで表示 | 外側のfor文だけ | 1行をまとめて表示できる |
| %cで表示 | 二重ループ | 1文字ずつ処理できる |
単純に文字列全体を表示するなら、%sを使う方法が分かりやすく簡潔です。
列数の決め方
2次元char配列で特に注意したいのが、列数です。
次の配列を考えてみましょう。
char animals[][7] = {"Dog", "Rabbit", "Panda"};各文字列に必要なサイズは次のとおりです。
| 文字列 | 文字数 | \0を含む必要サイズ |
|---|---|---|
| Dog | 3 | 4 |
| Rabbit | 6 | 7 |
| Panda | 5 | 6 |
最も長いRabbitには7要素必要です。そのため、列数を7にしています。
列数は、次の考え方で決められます。
列数 = 最長の文字列の文字数 + 1最後の1要素が、\0を格納するための領域です。
列数に余裕を持たせる場合
列数は、必要な最小サイズより大きくしても構いません。
char animals[][10] = {"Dog", "Rabbit", "Panda"};この場合、各行に10要素分の領域が用意されます。
| 行 | 文字列 | 使用する要素数 | 残る領域 |
|---|---|---|---|
| 0 | Dog | 4 | 6 |
| 1 | Rabbit | 7 | 3 |
| 2 | Panda | 6 | 4 |
使用する要素数には、\0も含まれています。
列数を大きくするとメモリの使用量は増えますが、あとから少し長い文字列を格納できる余裕を持たせられます。
行数を省略できる理由
初期化する文字列が書かれている場合は、行数を省略できます。
char animals[][7] = {"Dog", "Rabbit", "Panda"};初期値が3つあるので、コンパイラは3行必要だと判断できます。
次の2つの宣言は、同じ行数と列数を持ちます。
char animals[3][7] = {"Dog", "Rabbit", "Panda"};
char animals[][7] = {"Dog", "Rabbit", "Panda"};一方、列数は省略できません。
char animals[][] = {"Dog", "Rabbit", "Panda"};列数が分からなければ、1行あたりの大きさや、次の行が始まる位置を決められないからです。
| 要素数 | 初期化時に省略できるか | 理由 |
|---|---|---|
| 行数 | できる | 初期値の個数から判断できる |
| 列数 | できない | 1行分の大きさを決めるために必要 |
3つの駅名を順番に表示する
2次元char配列に格納した駅名を表示するプログラム
ファイル名:10_13_1.c
#include <stdio.h>
int main(void)
{
/* 複数の駅名を2次元char配列で初期化する */
char stations[][10] = {
"Tokyo",
"Ueno",
"Omiya"
};
/* 各駅名を1行ずつ順番に表示する */
for (int i = 0; i < 3; i++) {
printf("%d番目の駅名: %s\n", i + 1, stations[i]);
}
return 0;
}実行結果の例
1番目の駅名: Tokyo
2番目の駅名: Ueno
3番目の駅名: Omiyaプログラムの詳しい解説
最初に、3つの駅名を2次元char配列へ格納しています。
char stations[][10] = {
"Tokyo",
"Ueno",
"Omiya"
};初期値として3つの文字列があるため、行数は3になります。
| 行 | 格納される文字列 |
|---|---|
| stations[0] | Tokyo |
| stations[1] | Ueno |
| stations[2] | Omiya |
列数は10です。最も長いTokyoとOmiyaは5文字なので、\0を含めても6要素あれば保存できます。今回は少し余裕を持たせて10要素にしています。
表示にはfor文を使っています。
for (int i = 0; i < 3; i++) {
printf("%d番目の駅名: %s\n", i + 1, stations[i]);
}iの値は0、1、2と変化します。
| 繰り返し | iの値 | stations[i] | 表示番号 |
|---|---|---|---|
| 1回目 | 0 | Tokyo | 1 |
| 2回目 | 1 | Ueno | 2 |
| 3回目 | 2 | Omiya | 3 |
配列の添字は0から始まりますが、人に見せる番号は1から始めたほうが自然です。そのため、表示番号にはi + 1を使っています。
stations[i]が表しているもの
for文の中にあるstations[i]は、i行目の文字列全体を表します。
iが0のときは、次の指定になります。
stations[0]これはTokyoという文字列です。
iが1のときは、次の指定になります。
stations[1]これはUenoという文字列です。
printfの %s は、stations[i]の先頭から \0 が見つかるまでを表示します。そのため、行の中にある文字列全体が出力されます。
1文字を取り出す場合
駅名の中から1文字だけを表示したい場合は、行と列の両方を指定します。
printf("%c\n", stations[0][0]);このコードでは、Tokyoの先頭文字Tが表示されます。
printf("%c\n", stations[1][1]);このコードでは、Uenoの2文字目eが表示されます。
| 指定 | 表すデータ | 表示結果 |
|---|---|---|
| stations[0] | 1行目の文字列 | Tokyo |
| stations[0][0] | 1行目の1文字目 | T |
| stations[1] | 2行目の文字列 | Ueno |
| stations[1][1] | 2行目の2文字目 | e |
%sと %cを使い分ける
文字列全体と1文字では、printfで使う書式指定子が異なります。
| 表示するデータ | 指定例 | 書式指定子 |
|---|---|---|
| 文字列全体 | stations[i] | %s |
| 1文字 | stations[i][j] | %c |
文字列全体を %c で表示したり、1文字を %s で表示したりすると、正しい結果になりません。
データの形に合わせて、%sと %cを使い分けましょう。
図3:1行の文字列と1つの文字の違い

この図から分かること
stations[i]のように行だけを指定すると、その行に格納された文字列全体を表します。この場合は %s を使って表示します。
stations[i][j]のように行と列の両方を指定すると、文字列内の1文字を表します。この場合は %c を使って表示します。
添字の数を見ることで、文字列全体を扱っているのか、1文字だけを扱っているのかを判断できます。
3つの動作状態を表示する
2次元char配列に格納した動作状態を表示するプログラム
ファイル名:10_13_2.c
#include <stdio.h>
int main(void)
{
/* 複数の動作状態を2次元char配列で初期化する */
char states[][7] = {
"Ready",
"Active",
"Stop"
};
/* 各動作状態を順番に表示する */
for (int i = 0; i < 3; i++) {
printf("動作状態: %s\n", states[i]);
}
return 0;
}実行結果の例
動作状態: Ready
動作状態: Active
動作状態: Stopプログラムの詳しい解説
statesは、3つの動作状態を保存する2次元char配列です。
char states[][7] = {
"Ready",
"Active",
"Stop"
};各行には、次の文字列が入っています。
| 行 | 文字列 | 文字数 | \0を含む必要サイズ |
|---|---|---|---|
| states[0] | Ready | 5 | 6 |
| states[1] | Active | 6 | 7 |
| states[2] | Stop | 4 | 5 |
最も長いActiveは6文字です。末尾の \0 を含めると7要素必要なので、列数を7にしています。
for文では、iを0から2まで変化させています。
for (int i = 0; i < 3; i++) {
printf("動作状態: %s\n", states[i]);
}1回目はstates[0]、2回目はstates[1]、3回目はstates[2]が %s に渡されます。
このように、行数だけ繰り返せば、すべての文字列を順番に表示できます。
文字列の個数と列数を混同しない
2次元char配列では、行数と列数を取り違えないことが大切です。
char states[3][7];この宣言の3と7には、次の意味があります。
| 数値 | 意味 |
|---|---|
| 3 | 保存できる文字列の個数 |
| 7 | 1つの文字列に使える要素数 |
3文字の文字列を7個保存するという意味ではありません。最大6文字までの文字列を3個保存できる配列です。7要素のうち、少なくとも1要素は \0 のために必要になります。
文字列を1行ずつ表示するときの基本形
2次元char配列の文字列を順番に表示する基本形は、次のようになります。
for (int i = 0; i < 行数; i++) {
printf("%s\n", 配列名[i]);
}この形では、次のように役割を分けます。
| 部分 | 役割 |
|---|---|
| i | 表示する行番号 |
| i < 行数 | 配列の範囲内だけ繰り返す |
| 配列名[i] | i行目の文字列 |
| %s | \0まで文字列を表示する |
行数が3なら、iの範囲は0から2までです。条件をi <= 3にすると、存在しない4行目へアクセスしてしまうため注意しましょう。
よくあるつまずき
| つまずきやすい点 | 実際の考え方 |
|---|---|
| words[i]を1文字だと思う | words[i]はi行目の文字列 |
| words[i][j]を文字列だと思う | words[i][j]は1文字 |
| 文字列全体を %c で表示する | 文字列全体には %s を使う |
| 1文字を %s で表示する | 1文字には %c を使う |
| 列数を文字数と同じにする | \0の分を加える |
| 行数と列数を逆に考える | 行数は文字列の個数、列数は1文字列分のサイズ |
| for文をi <= 行数にする | i < 行数にする |
| 行数と列数を両方省略する | 省略できるのは初期化時の行数だけ |
コードを読むときの確認手順
2次元char配列の文字列を表示するコードでは、次の順番で確認すると理解しやすくなります。
- 配列にいくつの文字列が入っているか確認する
- 1行あたりの列数を確認する
- 最も長い文字列と \0 が収まるか確認する
- 行ごとの文字列を表す配列名[i]を見つける
- 文字列全体には %s が使われているか確認する
- 1文字の場合は配列名[i][j]と %c が使われているか確認する
- for文が0から行数未満まで繰り返しているか確認する
2次元char配列を読むときは、まず1行が1つの文字列という見方を持つことが大切です。この見方ができれば、配列名[i]で文字列を選び、printfと %s で表示する流れがすっきり理解できるようになります。
