
C言語基礎|文字列の配列
「文字列が増えたら、配列でまとめてスッキリ管理! “文字列の配列”で一気に扱おう」
文字列が1つなら配列、たくさんなら「配列の配列」
C言語では、文字列は char の配列で表しましたね。
ということは、文字列を何個もまとめたいなら、文字配列を何個も並べるのが自然です。
つまり、文字列の配列 = 配列の配列(2次元配列) です。
これを理解すると、メニュー一覧・名前リスト・単語帳みたいな「文字列の集まり」を、きれいに扱えるようになりますよ。

文字列の配列とは何か(全体像)
文字列の配列のイメージ(2次元配列)

図の説明
- 1行が「1つの文字列」を表します。
- 各行の末尾には \0(ナル文字)が入り、文字列の終わりを示します。
- これを3行ぶん持てば「3個の文字列」をまとめて持てます。
代表的な宣言パターン(初期化あり/なし)
文字列の配列の宣言パターン
| 目的 | 宣言例 | 意味 |
|---|---|---|
| 初期化して用意 | char s[][8] = {"TOKYO", "OSAKA", "NAGOYA"}; | 行数は初期化子の個数から決まる。列数8なので最大7文字+終端\0 |
| 入力で埋める | char s[3][128]; | 行数を省略できない。1行あたり最大127文字+終端\0 |
表の説明
- 行数:文字列の個数
- 列数:1つの文字列を入れる箱の大きさ(終端\0 も含める)
サンプルプログラム
地名のリストを表示するプログラムです。
プロジェクト名:chap9-6-1 ソースファイル名:chap9-6-1.c
// 文字列の配列を表示する(初期化版)
#include <stdio.h>
int main(void)
{
char city[][10] = {"Tokyo", "Osaka", "Nagoya"};
puts("----- 都市リスト -----");
for (int i = 0; i < 3; i++) {
printf("city[%d] = %s\n", i, city[i]);
}
return 0;
}実行結果の例
----- 都市リスト -----
city[0] = Tokyo
city[1] = Osaka
city[2] = Nagoya配列の要素の正体を分解して理解しよう
この宣言をもう一度見ます。
char city[][10] = {"Tokyo", "Osaka", "Nagoya"};
city の「型」の見え方
| 名前 | 何の配列? | 要素1個の型 | 要素数 |
|---|---|---|---|
| city | 文字列の配列 | char[10] | 3 |
表の説明
- city は配列
- その要素(city[0], city[1], city[2])は、それぞれ char[10] の配列
- char[10] は 10文字分の箱なので、最大9文字+終端\0 まで格納できます
2つの添字で「文字」まで取り出せる
文字列の配列は 2次元配列なので、2つの添字で中身の1文字にアクセスできます。
添字アクセスの例
city[0] -> "Tokyo"
city[0][0] -> 'T'
city[0][1] -> 'o'

図の説明
- city[i] は「i番目の文字列(先頭アドレスのように扱える)」
- city[i][j] は「i番目の文字列の j文字目」
文字列をキーボードから読み込む(入力版)
次は、初期化せずに入力で3つの文字列を入れる例です。
プロジェクト名:chap9-6-2 ソースファイル名:chap9-6-2.c
Visual Studio でこのプログラムを実行するには、SDLチェック設定を変更しておく必要があります。
1.プロジェクト名を右クリックして、「プロパティ」をクリックします。
2.「C/C++」→「全般」→「SDLチェック」を「いいえ」に切り替えて「OK」をクリックします。
// 文字列の配列を読み込んで表示する
#include <stdio.h>
int main(void)
{
char name[3][32];
puts("3人のニックネームを入力してください。");
for (int i = 0; i < 3; i++) {
printf("name[%d] : ", i);
scanf("%31s", name[i]); // 最大31文字まで読み込む(終端\0の分を残す)
}
puts("----- 入力結果 -----");
for (int i = 0; i < 3; i++) {
printf("name[%d] = %s\n", i, name[i]);
}
return 0;
}ここが大事:なぜ & を付けないの?
scanf で文字列を読むとき、格納先には「書き込み先のアドレス」が必要です。
でも name[i] は配列なので、式として使うと先頭要素へのポインタのように扱われます。
だから &name[i] みたいにすると逆に型が合わず、危険になりやすいです。
scanf に渡す実引数の違い(感覚)
| 渡すもの | 意味 | だいたいの型のイメージ |
|---|---|---|
| name[i] | i行目の先頭に書き込む | char* のように扱われる |
| &name[i] | i行目「配列まるごと」へのアドレス | char (*)[32] のようになりがち |
表の説明
scanf の %s が欲しいのは「文字を並べて書ける先頭位置」です。
だから name[i] がちょうどよい、という理解でOKです。
登場する命令の書式と「何をする命令か」
puts の書式と役割
- 書式
puts(文字列); - 何をする?
文字列を表示し、最後に改行を付けます。見出し表示に便利です。
printf の書式と役割
- 書式
printf(書式文字列, 実引数1, 実引数2, ...); - 何をする?
書式に合わせて整形して表示します。%s で文字列を表示できます。
scanf の書式と役割(今回のポイントつき)
- 書式
scanf(書式文字列, 格納先, ...); - 何をする?
標準入力から読み取り、指定した格納先へ入れます。
%s は「空白までの文字列」を読み込みます。 - 安全のためのコツ
%31s のように最大文字数を指定すると、配列あふれを防ぎやすいです。
name[3][32] なら、終端\0の分も考えて 31 までが安心です。
つまずきポイント(実務でもよく出る)
注意点まとめ
| 注意 | 起きがちなこと | 対策 |
|---|---|---|
| 列数が足りない | 途中で切れる、または危険 | 最大文字数+1(終端\0)を確保 |
| scanf("%s") は空白で止まる | 苗字 名前 みたいに入力できない | 空白を含むなら別の方法を検討 |
| 入力が長すぎる | バッファオーバーフローの危険 | %31s のように幅指定を使う |
表の説明
「列数=最大文字数+1」は超重要です。+1 は終端\0 のためです。
演習問題
演習9-3:終了文字列で入力を止めて、入力済みだけ表示せよ
- 文字列の個数は 3 より大きくし、その値をオブジェクト形式マクロで定義する
- 入力で END!! を読んだら、読み込みをそこで終了
- 表示は END!! より前に入力されたものだけ
解答例
プロジェクト名:chap9-6-3 ソースファイル名:chap9-6-3.c
Visual Studio でこのプログラムを実行するには、SDLチェック設定を変更しておく必要があります。
1.プロジェクト名を右クリックして、「プロパティ」をクリックします。
2.「C/C++」→「全般」→「SDLチェック」を「いいえ」に切り替えて「OK」をクリックします。
#include <stdio.h>
#define MAX_WORDS 5
#define BUF_SIZE 32
int main(void)
{
char s[MAX_WORDS][BUF_SIZE];
int count = 0;
puts("単語を入力してください(END!! で終了)");
for (int i = 0; i < MAX_WORDS; i++) {
printf("s[%d] : ", i);
scanf("%31s", s[i]);
if (s[i][0] == 'E' && s[i][1] == 'N' && s[i][2] == 'D' && s[i][3] == '!' && s[i][4] == '!' && s[i][5] == '\0') {
break;
}
count++;
}
puts("----- 入力された単語 -----");
for (int i = 0; i < count; i++) {
printf("%s\n", s[i]);
}
return 0;
}解説
- MAX_WORDS で「最大何個読むか」を決めています。
- count は「END!! 以外で入力された個数」を覚えるための変数です。
- 表示は count 個だけにすることで、END!! 自体を出さずに済みます。
- scanf は %31s にして、BUF_SIZE 32 の範囲に収まるようにしています。
(※ ここでは文字列比較を分かりやすく手作業で書きました。次章以降で strcmp を学ぶともっとスッキリできます。)
