C言語の基本|char配列に文字列を設定する

文字を並べ、最後に終わりの印を置く。char配列の仕組みを知れば、C言語の文字列設定がすっきり分かります。

C言語では、文字列をchar型の配列として扱います。文字列専用の基本型があるわけではなく、配列の各要素へ文字を順番に格納することで、単語や文章を表現します。

char配列へ文字列を設定する方法は、1つだけではありません。

  • 文字列リテラルを使って初期化する
  • 文字定数を1つずつ並べて初期化する
  • 配列サイズを指定して初期化する
  • 宣言後に各要素へ文字を代入する
  • strcpyを使って文字列をコピーする
  • 2次元配列へ複数の文字列を設定する

どの方法を使う場合でも、文字列の最後にナル文字 \0 が必要です。

たとえば、Codeは画面上では4文字ですが、char配列にはC、o、d、e、\0という5つの値が格納されます。そのため、配列サイズを決めるときは、表示する文字数に1を加えなければなりません。

ここでは、char配列へ文字列を設定する代表的な方法と、配列サイズの考え方、宣言後に文字列を入れる方法、複数の文字列を扱う方法を順番に見ていきます。

char配列に文字列を設定する仕組み

次の宣言では、wordというchar配列を用意し、Codeという文字列で初期化しています。

char word[] = "Code";

配列の内部には、次のように文字が格納されます。

添字格納される値役割
0C1文字目
1o2文字目
2d3文字目
3e4文字目
4\0文字列の終端

文字列をchar配列へ設定するということは、文字を順番に格納し、その最後に \0 を置くことだと考えると分かりやすいでしょう。

図1:char配列に格納される文字とナル文字

この図から分かること

Codeという文字列を保存するには、見えている4文字だけでなく、文字列の終わりを示す \0 の領域も必要です。

必要な配列サイズは、次のように計算します。

必要な配列サイズ = 表示する文字数 + 1

最後の1要素が、\0を格納する場所です。

ナル文字 \0 の役割

ナル文字 \0 は、文字列の終わりを示す特別な文字です。画面には表示されませんが、C言語で文字列を扱ううえで重要な役割を持っています。

printfで書式指定子 %s を使うと、配列の先頭から1文字ずつ読み進め、\0が見つかったところで表示を終了します。

処理の流れは次のとおりです。

  • 配列の先頭要素を読む
  • 読み取った文字を表示する
  • 次の要素へ進む
  • \0を見つけたら表示を終了する

\0が格納されていない場合、どこまでが文字列なのか判断できません。配列の外側まで読み進め、不要な文字が表示されたり、予期しない動作が発生したりする可能性があります。

文字列リテラルで初期化する

char配列へ固定の文字列を設定するときは、文字列リテラルを使った初期化が最も分かりやすい方法です。

char language[] = "C17";

この場合、languageの内部には次の値が入ります。

添字
0C
11
27
3\0

要素数を省略しているため、コンパイラが初期値を調べて必要な配列サイズを決めます。C17は3文字なので、\0を含めた配列サイズは4です。

文字列リテラルによる初期化には、次の特徴があります。

項目内容
書きやすさ短く分かりやすく書ける
\0の設定自動的に追加される
要素数省略できる
向いている場面宣言時に内容が決まっている場合

固定の文字列を設定するだけなら、この方法が扱いやすいでしょう。

文字を1つずつ並べて初期化する

char配列は、ほかの配列と同じように、初期値を1要素ずつ並べることもできます。

char language[] = {'C', '1', '7', '\0'};

この書き方でも、languageにはC17という文字列が格納されます。

文字列リテラルを使う場合との違いは、\0を自分で指定する点です。

初期化方法\0の設定特徴
文字列リテラル自動簡潔に書ける
文字定数を並べる手動配列の内部構造が見えやすい

次の初期化では、最後に \0 がありません。

char language[] = {'C', '1', '7'};

C、1、7という3文字は格納されますが、文字列の終端がないため、安全な文字列としては扱えません。

文字を1つずつ並べる場合は、最後に \0 を入れることが大切です。

配列サイズを指定して初期化する

配列サイズを明示して、文字列リテラルで初期化することもできます。

char language[10] = "C17";

この配列には10要素分の領域があります。

添字格納される値
0C
11
27
3\0
4~90

文字列リテラルで配列を初期化したとき、初期値が指定されていない残りの要素は0で初期化されます。

配列を大きめに用意すると、あとから現在より長い文字列を格納できる余地を残せます。ただし、要素数が10なら、格納できる文字列は最大9文字です。残りの1要素は \0 のために必要になります。

文字列に必要な最小サイズ

文字列を安全に保存するために必要な最小サイズを整理すると、次のようになります。

保存する文字列表示文字数必要な最小サイズ
A12
Cat34
Code45
Hello56
Compiler89

必要な最小サイズは、常に表示文字数より1つ多くなります。

配列サイズが足りない初期化

次の配列には、Codeの4文字分しかありません。

char word[4] = "Code";

文字列として扱うには、C、o、d、eに加えて \0 が必要です。そのため、必要な配列サイズは5になります。

サイズを明示するなら、次のように書きます。

char word[5] = "Code";

要素数をコンパイラに決めてもらう場合は、次のように書けます。

char word[] = "Code";

初期値の長さに合わせて配列を作るだけなら、要素数を省略する方法が簡単です。

宣言後に文字列を直接代入できない

char配列は、宣言したあとに文字列リテラルを = でまとめて代入できません。

次のコードは正しくありません。

char word[10];
word = "Code";

int型などの通常の変数なら、宣言後に値を代入できます。

int number;
number = 10;

しかし、配列名を代入演算子の左側に置き、配列全体をまとめて入れ替えることはできません。

char配列へ文字列を設定する方法は、タイミングによって次のように分かれます。

設定するタイミング使用できる方法
宣言と同時文字列リテラルによる初期化
宣言と同時文字定数を並べた初期化
宣言したあと各要素への代入
宣言したあとstrcpyによるコピー

宣言後に1文字ずつ代入する

宣言済みのchar配列には、添字を使って1文字ずつ代入できます。

char word[5];

word[0] = 'C';
word[1] = 'o';
word[2] = 'd';
word[3] = 'e';
word[4] = '\0';

最後の要素へ \0 を代入することで、wordはCodeという文字列になります。

この方法は、次のような処理に向いています。

  • 条件によって文字を選びながら文字列を作る
  • 繰り返し処理で文字を順番に格納する
  • 文字列の一部分を変更する
  • 実行中に文字列を少しずつ組み立てる

固定された文字列を宣言時に設定するだけなら、文字列リテラルで初期化するほうが簡潔です。

strcpyで文字列をコピーする

宣言済みのchar配列へ文字列全体を設定したい場合は、標準ライブラリ関数のstrcpyを利用できます。

#include <string.h>

char word[10];
strcpy(word, "Code");

strcpyの基本形は次のとおりです。

strcpy(コピー先, コピー元);
要素内容
コピー先文字列を受け取るchar配列
コピー元コピーする文字列
string.hstrcpyを使用するために必要なヘッダ

strcpyは、コピー元に含まれる \0 までコピー先へ格納します。

Codeをコピーする場合、コピーされる内容は C、o、d、e、\0です。そのため、コピー先には最低でも5要素が必要になります。

strcpyを使うときの注意点

strcpyは、コピー先の配列サイズを自動では確認しません。

次のコードでは、必要な5要素分の領域が用意されています。

char word[5];
strcpy(word, "Code");

一方、次の配列には4要素しかありません。

char word[4];
strcpy(word, "Code");

これでは \0 まで格納できず、配列の範囲を超えて書き込む可能性があります。

範囲外へ書き込むと、次のような問題につながります。

  • ほかの変数の値を壊す
  • 文字列の表示が崩れる
  • プログラムが異常終了する
  • 実行環境によって動作が変わる

strcpyを使用するときは、コピー元の文字数に1を加えたサイズ以上の配列を用意しましょう。

図2:初期化とstrcpyによる設定の違い

この図から分かること

宣言と同時であれば、文字列リテラルを使ってchar配列を初期化できます。

宣言が終わったchar配列には、文字列リテラルを = で直接代入できません。あとから文字列全体を設定したい場合は、strcpyなどを使って配列内へ文字をコピーします。

どちらの方法でも、完成した文字列の最後には \0 が格納されます。

初期化とstrcpyを使い分ける

文字列を設定するタイミングによって、適した方法が変わります。

方法使用するタイミング\0の設定向いている場面
文字列リテラルによる初期化宣言と同時自動最初から文字列が決まっている
文字定数を並べる初期化宣言と同時手動配列内部を明示したい
要素ごとの代入宣言後手動文字列を1文字ずつ作る
strcpyによるコピー宣言後コピーされる文字列全体をあとから設定する

固定の初期値なら、文字列リテラルによる初期化が分かりやすいでしょう。プログラムの実行中に内容を決める場合は、要素ごとの代入やstrcpyが候補になります。

2つの方法で処理名を設定する

初期化とstrcpyで処理名を設定するプログラム

ファイル名:10_12_1.c

#include <stdio.h>
#include <string.h>

int main(void)
{
    /* 宣言と同時に文字列リテラルで初期化する */
    char process1[] = "Compile";

    /* 配列を宣言したあとに文字列をコピーする */
    char process2[12];
    strcpy(process2, "Execute");

    printf("最初の処理は%sです。\n", process1);
    printf("次の処理は%sです。\n", process2);

    return 0;
}
実行結果の例
最初の処理はCompileです。
次の処理はExecuteです。

プログラムの詳しい解説

このプログラムでは、2つの方法でchar配列へ文字列を設定しています。

最初の配列は、宣言と同時に初期化しています。

char process1[] = "Compile";

Compileは7文字です。末尾の \0 を含めるため、process1の要素数は8になります。

添字01234567
Compile\0

要素数を省略しているので、コンパイラが必要なサイズを決めます。

2つ目の配列では、先に12要素分の領域を用意しています。

char process2[12];

この時点では、関数内にあるprocess2の要素は不定値です。続いてstrcpyを使い、Executeという文字列をコピーしています。

strcpy(process2, "Execute");

Executeは7文字なので、\0を含めて8要素必要です。process2には12要素あるため、文字列全体を格納できます。

表示には、文字列用の書式指定子 %s を使っています。

printf("最初の処理は%sです。\n", process1);

printfは配列の先頭から \0 の直前までを文字列として表示します。

文字列の一部分を書き換える

char配列で作った文字列は、添字を使って1文字ずつ変更できます。

char word[] = "Code";
word[0] = 'M';

この処理によって、wordの内容はModeになります。

添字変更前変更後
0CM
1oo
2dd
3ee
4\0\0

変更したのは先頭の文字だけです。末尾の \0 はそのまま残っているため、変更後も正しい文字列として表示できます。

途中に \0 を設定した場合

char配列の途中へ \0 を代入すると、その位置が新しい文字列の終端になります。

char word[] = "Code";
word[2] = '\0';

配列内部は次の状態になります。

添字
0C
1o
2\0
3e
4\0

書式指定子 %s でwordを表示すると、最初の \0 がある位置で処理が終わるため、Coだけが表示されます。

添字3にはeが残っていますが、最初の \0 より後ろにあるため文字列には含まれません。

配列サイズと文字列の長さは別のもの

次の配列を考えてみましょう。

char word[10] = "Code";

配列には10要素ありますが、表示される文字列はCodeの4文字です。

項目
配列の要素数10
表示文字数4
最初の \0 の添字4
残りの領域添字5~9

配列サイズは、文字を保存できる領域全体の大きさです。一方、文字列の長さは、配列の先頭から最初の \0 までに並んでいる文字数です。

2次元のchar配列で複数の文字列を扱う

1つの文字列を保存するなら、1次元のchar配列を使います。

char color[] = "Red";

複数の文字列をまとめて管理したい場合は、2次元のchar配列を利用できます。

char colors[3][6] = {
    "Red",
    "Green",
    "Blue"
};

この配列には、3つの文字列が入っています。

行の添字文字列表示文字数必要な要素数
0Red34
1Green56
2Blue45

最も長いGreenは5文字なので、\0を含めると6要素必要です。そのため、列数を6にしています。

行数と列数の意味

複数の文字列を格納するchar配列は、次の形で考えられます。

char 配列名[文字列の個数][1文字列分の配列サイズ];
位置意味
1つ目の要素数保存する文字列の個数
2つ目の要素数1つの文字列に使える要素数
1つ目の添字どの文字列を選ぶか
2つ目の添字文字列内のどの文字を選ぶか

たとえば、colors[1]はGreenという文字列を表します。

printf("%s\n", colors[1]);

colors[1][2]まで指定すると、Greenの3文字目であるeを表します。

printf("%c\n", colors[1][2]);

初期化時は行数を省略できる

初期化子がある場合、2次元char配列の行数を省略できます。

char colors[][6] = {
    "Red",
    "Green",
    "Blue"
};

初期値として3つの文字列が書かれているため、コンパイラは行数を3と判断します。

列数は省略できません。

char colors[][] = {
    "Red",
    "Green",
    "Blue"
};

この書き方では、1行分の領域が何要素なのか判断できません。多次元配列では、初期化時に先頭の要素数は省略できますが、それより後ろの要素数は指定する必要があります。

図3:2次元char配列で複数の文字列を管理する

この図から分かること

2次元char配列では、1行が1つの文字列に対応します。

行数は保存できる文字列の個数です。列数は1つの文字列を保存するための配列サイズで、最も長い文字列の文字数に1を加えて決めます。

短い文字列を保存した行には未使用の領域が残りますが、各文字列の末尾に \0 が入っていれば、printfと %s で正しく表示できます。

1文字、1つの文字列、複数の文字列の違い

扱うデータ宣言例使用するデータ構造
1文字char mark = 'A';char型変数
1つの文字列char word[] = "Code";1次元char配列
複数の文字列char words[][6] = {"Red", "Blue"};2次元char配列

1次元char配列では、添字を1つ指定すると1文字を取り出せます。

2次元char配列では、添字を1つ指定すると1行分の文字列になり、添字を2つ指定するとその中の1文字になります。

1文字ずつ代入して文字列を作る

配列の各要素へ文字を代入してメッセージを作るプログラム

ファイル名:10_12_2.c

#include <stdio.h>

int main(void)
{
    char message[5];

    /* 配列の各要素へ1文字ずつ代入する */
    message[0] = 'G';
    message[1] = 'o';
    message[2] = '!';
    message[3] = '!';
    message[4] = '\0';

    printf("作成したメッセージは%sです。\n", message);

    return 0;
}
実行結果の例
作成したメッセージはGo!!です。

プログラムの詳しい解説

最初に、5要素のchar配列を宣言しています。

char message[5];

Go!!は4文字です。文字列として扱うには、末尾の \0 を含めて5要素必要になります。

各要素には、次のように1文字ずつ代入しています。

message[0] = 'G';
message[1] = 'o';
message[2] = '!';
message[3] = '!';

この時点では、G、o、!、!という4文字が並んでいます。しかし、文字列の終端はまだ設定されていません。

最後に、message[4]へ \0 を代入します。

message[4] = '\0';

これでmessageは、Go!!という文字列として完成します。

添字格納される値
0G
1o
2!
3!
4\0

printfはmessage[0]から順番に文字を読み、message[4]の \0 を見つけたところで表示を終了します。

\0を書かなかった場合

最後の要素に \0 を代入しなかった場合、文字列の終了位置が決まりません。

char message[5];

message[0] = 'G';
message[1] = 'o';
message[2] = '!';
message[3] = '!';

message[4]は不定値のままです。この状態で %s を使って表示すると、次のような問題が起こる可能性があります。

  • Go!!の後ろに不要な文字が表示される
  • 配列の外側まで読み取られる
  • 実行するたびに表示結果が変化する
  • プログラムが異常な動作をする

要素ごとの代入で文字列を作るときは、最後に必ず \0 を格納しましょう。

よくある間違いを確認する

間違い起こる問題正しい考え方
表示文字数と同じ配列サイズにする\0を格納できない文字数に1を加える
文字を並べるときに \0 を省略する文字列の終端がなくなる最後の要素へ \0 を入れる
宣言後に = で文字列全体を代入する配列全体には代入できないstrcpyまたは要素ごとの代入を使う
小さい配列へstrcpyでコピーする配列の範囲外へ書き込むコピー元が収まる配列を用意する
2次元配列の列数を小さくする長い文字列を格納できない最大文字数に1を加える
%cと %s を混同する正しく表示できない1文字は %c、文字列は %s を使う

char配列を扱うときの確認手順

char配列へ文字列を設定するときは、次の順番で確認するとミスを減らせます。

  • 保存したい文字列が何文字あるか確認する
  • \0を含めた配列サイズを用意する
  • 宣言時の初期化か、宣言後の設定かを確認する
  • 宣言後なら、要素ごとの代入かstrcpyを選ぶ
  • strcpyを使う場合は、コピー先の大きさを確認する
  • 複数の文字列なら、最も長い文字列に合わせて列数を決める
  • printfでは文字列に %s、1文字に %c を使う

C言語の文字列は、char型の文字が順番に並び、最後に \0 が置かれた配列です。この内部構造を意識すると、文字列リテラルによる初期化、strcpyによるコピー、1文字ずつの代入、2次元配列による複数文字列の管理を自然に使い分けられるようになります。