
C言語基礎|文字列の配列
文字列がたくさん登場したら、配列で並べる?それともポインタで束ねる?――選び方でメモリ効率も自由度も変わる!
文字列が1つだけなら、char 配列や char * で十分でした。
でも現実のプログラムでは「メニュー一覧」「エラーメッセージ一覧」「曜日名」「コマンド名」みたいに、文字列の集まりを扱うことがよくあります。
そこで登場するのが 文字列の配列です。
実は文字列の表現には「配列による文字列」と「ポインタによる文字列」がありましたよね。
文字列の集まりも同じで、次の2通りで実現できます。
- 配列による文字列の配列(2次元配列:文字の箱を並べる)
- ポインタによる文字列の配列(ポインタ配列:文字列の先頭住所を並べる)
この違いを、表と図でしっかり整理していきましょう。

文字列の配列は2通りある
2つの実現方法の全体比較
| 実現方法 | 宣言例 | 正体 | 文字列の長さ | メモリの特徴 | よくある用途 |
|---|---|---|---|---|---|
| 配列による文字列の配列 | char a[][6] = {"DOG", "CAT", "BIRD"}; | char の2次元配列(固定長の箱×個数) | 最大長に合わせて固定 | すべて連続配置、短い文字列は空きが出る | 書き換える予定がある文字列群、固定サイズで扱いたい |
| ポインタによる文字列の配列 | const char *p[] = {"RED", "GREEN", "BLUE"}; | ポインタの配列(住所札の集合) | バラバラでOK | 文字列本体は別領域、並びは連続とは限らない | 表示専用の定数文字列、差し替えや並び替えをしたい |
この表の説明
- 左は「箱を並べる」ので 整列して管理しやすいけど空きが出やすい
- 右は「住所札を並べる」ので 効率よく可変長を扱えるけど配置は散らばる
という違いです。
サンプルプログラム
プロジェクト名:chap11-3-1 ソースファイル名:chap11-3-1.c
#include <stdio.h>
int main(void)
{
char animal[][6] = {"DOG", "CAT", "BIRD"}; // 配列による文字列の配列(最大5文字+終端)
const char *color[] = {"RED", "GREEN", "BLUE"}; // ポインタによる文字列の配列
puts("【配列で持つ文字列一覧】");
for (int i = 0; i < 3; i++)
printf("animal[%d] = %s\n", i, animal[i]);
puts("【ポインタで持つ文字列一覧】");
for (int i = 0; i < 3; i++)
printf("color[%d] = %s\n", i, color[i]);
return 0;
}実行結果(例)
【配列で持つ文字列一覧】
animal[0] = DOG
animal[1] = CAT
animal[2] = BIRD
【ポインタで持つ文字列一覧】
color[0] = RED
color[1] = GREEN
color[2] = BLUE配列による文字列の配列(2次元配列)
どういう構造?
宣言はこうでした。
- 形式:char 変数名[行数][列数];
- 今回:char animal[][6]
ここでの「列数 6」は、1つの文字列を入れる箱の大きさです。
"DOG" は 3文字ですが、終端の '\0' が必要なので 最低4。
今回は余裕を持って 6 にしています(最大5文字+'\0')。
2次元配列(箱が連続して並ぶ)

この図の説明
- 各行 animal[i] が「固定長の文字列ボックス」
- 短い文字列だと末尾に 未使用領域が残る。
- その代わり、全体は きっちり連続配置されます。
メモリ量のイメージ
- 占有サイズ:行数×列数 バイト
例:3×6 = 18 バイト(char は1バイト想定)
ポインタによる文字列の配列(ポインタ配列)
どういう構造?
宣言はこうでした。
- 形式:char *変数名[要素数];
- 今回:const char *color[]
この配列の中身 color[i] は、文字列そのものではなく 先頭文字へのポインタです。
つまり「文字列の住所札が3枚並んでいる」状態です。
ポインタ配列(住所札+文字列本体)

この図の説明
- color の配列領域:ポインタが3つ並ぶ。
- 文字列本体:別の場所(静的領域)に置かれることが多い。
- 文字列同士が隣り合うとは限らないので、前後関係を当てにできません。
メモリ量のイメージ
- ポインタ配列:sizeof(char *) × 要素数
- 文字列本体:各リテラルのバイト数(終端 '\0' 含む)の合計
※ sizeof(char *) は環境依存(64bit環境なら8バイトが多い)です。
2つの [ ] で2次元っぽく見える理由
ポインタ配列 color は「2次元配列ではない」のに、color[i][j] のように書けます。
color[i][j] の意味
| 表記 | 実際に起きていること |
|---|---|
| color[i] | i番目の文字列の先頭を指すポインタ |
| color[i][j] | i番目の文字列の j文字目(= *(color[i] + j)) |
この表の説明
- 最初の [i] で「どの文字列か」を選ぶ
- 次の [j] で「その文字列の何文字目か」を選ぶ
という2段階のアクセスになっています。
どちらを選ぶ?判断のコツ
選び方の実務目線
| やりたいこと | おすすめ | 理由 |
|---|---|---|
| 文字列を書き換えたい | 2次元配列 | 配列の中身は自分の領域なので安全に編集しやすい。 |
| 文字列の長さがバラバラで無駄を減らしたい | ポインタ配列 | 必要な長さだけ確保され、空きが出にくい。 |
| 文字列の並び替えをしたい | ポインタ配列 | 住所札を並べ替えるだけで済む。 |
| サイズが固定で扱いやすくしたい | 2次元配列 | 1行の長さが一定で処理が単純になる。 |
登場する命令(関数)の書式と役割
puts
- 書式:puts(文字列);
- 何をする命令?:文字列を表示して、最後に改行を付ける
- 特徴:printf よりシンプル(書式指定はできない)
printf
- 書式:printf(書式文字列, 引数1, 引数2, ...);
- 何をする命令?:書式に従って表示する
- 今回の主役:%s(char へのポインタを受け取り、'\0' まで表示)
演習問題
演習11-2
サンプルプログラムの for 文の 3 を埋め込みにせず、計算で求めるように書きかえよ。
解答例(配列要素数を計算)
#include <stdio.h>
int main(void)
{
char animal[][6] = {"DOG", "CAT", "BIRD"};
const char *color[] = {"RED", "GREEN", "BLUE"};
int animal_count = (int)(sizeof(animal) / sizeof(animal[0]));
int color_count = (int)(sizeof(color) / sizeof(color[0]));
puts("【配列で持つ文字列一覧】");
for (int i = 0; i < animal_count; i++)
printf("animal[%d] = %s\n", i, animal[i]);
puts("【ポインタで持つ文字列一覧】");
for (int i = 0; i < color_count; i++)
printf("color[%d] = %s\n", i, color[i]);
return 0;
}解説(ここが大事)
- sizeof(animal) は「2次元配列全体のバイト数」
- sizeof(animal[0]) は「1行分(char[6])のバイト数」
- 割り算で「行数」が出ます
同様に color も、配列全体 ÷ 1要素(ポインタ1個)で要素数が出ます。
※この方法は「配列として見えている範囲」で有効です。関数の引数にすると配列はポインタに変換されるので注意です。
