【初心者向け】C言語のchar配列を完全解説|文字列操作の基本から応用まで

1. はじめに

C言語は、システム開発や組み込み開発の現場で今なお広く使われている言語です。その中でも「char配列」は、文字列を扱うための最も基本的かつ重要な構文要素の一つです。

C言語には、文字列型が標準で存在しません。その代わりに、文字の配列(char配列)を使って文字列を表現します。これは初心者にとって非常に直感的とは言えないため、char配列の理解がC言語学習における大きな壁になることもしばしばです。

また、char配列とcharポインタ(char*)の使い分けや、ヌル文字(\0)の存在など、細かな仕様を理解していないと、予期せぬバグの原因になります。

この記事では、「C言語 char 配列」というテーマにフォーカスし、基本的な使い方から応用的なテクニック、そしてよくあるエラーの回避方法までをわかりやすく解説します。

これからC言語を本格的に学ぼうとしている方や、char配列について復習したい方は、ぜひ最後までご覧ください。次章ではまず、char配列の定義と基本的な仕組みについて説明します。

2. char配列とは?

C言語において「char配列」とは、文字(char型)を複数個まとめて格納するための配列です。これは、文字列を扱う上で基本となる構造です。

char型とは?

C言語では、char型は1文字を表現するためのデータ型です。たとえば、次のように1文字の変数を定義できます。

char c = 'A';

このように、'A' のような シングルクォートで囲まれた1文字は、char型として定義されます。

char配列の基本構文

複数の文字を格納するには、char型の配列を使います。以下のように定義します:

char str[6] = {'H', 'e', 'l', 'l', 'o', ' '};

この配列は、"Hello" という5文字の文字列にヌル文字()を加えた、6文字分のメモリ領域を確保しています。

ヌル文字(’ ‘)の重要性

C言語では、文字列の終端を示すためにヌル文字 ' ' を使います。この記号がないと、文字列操作関数が正しく動作せず、予期しない動作をする可能性があります。

char str[] = "Hello"; // 自動的に末尾に ' ' が追加される

上記のように、ダブルクォートで囲った文字列リテラルを使用すれば、コンパイラが自動的に ' ' を末尾に追加してくれます。

配列のサイズと注意点

char配列を使う際は、必要な文字数 + 1(ヌル文字分) のサイズを確保するのが基本です。サイズ不足の配列に文字列を代入すると、バッファオーバーフローの原因になり、プログラムが異常終了する可能性もあります。

侍エンジニア塾

3. char配列の宣言と初期化

char配列を使うためには、まず適切な宣言と初期化が必要です。ここでは、C言語におけるchar配列の基本的な宣言方法から、初期化の仕方、そして動的メモリの利用についても解説します。

静的な宣言と初期化

最も基本的な方法として、配列サイズを明示して宣言し、1文字ずつ初期化する方法があります。

char str[6] = {'H', 'e', 'l', 'l', 'o', ' '};

このようにすると、str"Hello" という文字列として扱うことができます。重要なのは、末尾に必ず ' ' を含めることです。

文字列リテラルによる初期化

C言語では、以下のように文字列リテラルを使った簡易的な初期化も可能です。

char str[] = "Hello";

この場合、配列のサイズは自動的に "Hello" + ' ' の6文字分になります。文字列の内容を変更したい場合には、char配列として宣言しておくことで、安全に書き換えが可能になります。

サイズを指定してリテラルを代入

配列サイズを指定しつつ、文字列リテラルで初期化することも可能ですが、サイズ不足には要注意です。

char str[5] = "Hello"; // ❌ エラーの原因(サイズ不足)

上記のように、"Hello" は5文字+1文字(’ ‘)の合計6文字を必要とします。したがって、最低でも char str[6] にする必要があります。

動的メモリ確保(malloc)の利用

より柔軟に文字列を扱いたい場合には、malloc を使って動的にchar配列を確保する方法があります。

#include <stdlib.h>
#include <string.h>

char* str = malloc(6 * sizeof(char));
strcpy(str, "Hello");

この方法では、メモリを動的に確保し、そのポインタを通じて文字列を扱います。使用後は、free(str); を使ってメモリを解放することを忘れてはいけません。

4. 文字列の操作

C言語では、char配列を用いた文字列操作において、標準ライブラリの関数を活用するのが一般的です。ここでは、基本的な文字列操作の関数とその使い方について、具体的な例とともに解説します。

文字列のコピー:strcpy

strcpyは、1つの文字列を別のchar配列にコピーする関数です。

#include <string.h>

char src[] = "Hello";
char dest[10];
strcpy(dest, src);

注意点: destに十分なサイズが確保されていないと、バッファオーバーフローの原因になります。サイズはコピーする文字列の長さ+1(ヌル文字)を確保しましょう。

文字列の結合:strcat

strcatは、2つの文字列を結合します。第2引数の内容を第1引数の末尾に追加します。

#include <string.h>

char str1[20] = "Hello";
char str2[] = " World";
strcat(str1, str2);

この結果、str1"Hello World" となります。こちらも、第1引数の配列に結合後の全体サイズが収まる余裕があることが前提です。

文字列の長さを取得:strlen

strlen関数は、文字列の長さ(ヌル文字を除く文字数)を返します。

#include <string.h>

char str[] = "Hello";
int len = strlen(str); // len = 5

ヌル文字 はカウントされないため、C言語に慣れていない人は注意が必要です。

文字列の比較:strcmp

2つの文字列が同じかどうかを比較するには、strcmpを使用します。

#include <string.h>

char str1[] = "Hello";
char str2[] = "World";

if (strcmp(str1, str2) == 0) {
    // 等しい
} else {
    // 異なる
}

この関数は、等しい場合に0を返し、異なる場合は文字コードの差を返します。

文字列の検索:strchrstrstr

特定の文字や部分文字列を検索するには、以下のような関数が使えます。

#include <string.h>

char str[] = "Hello World";

// 文字の検索
char *ptr1 = strchr(str, 'o'); // 最初の'o'へのポインタ

// 部分文字列の検索
char *ptr2 = strstr(str, "World"); // "World"の開始位置へのポインタ

見つからなかった場合、どちらの関数も NULL を返します。

5. char配列とポインタの違い

C言語において文字列を扱う際、char配列charポインタ(char*)は似ているようで、実際には異なる性質を持ちます。この違いを正しく理解することで、メモリの誤使用や予期せぬバグを防ぐことができます。

宣言の違い

まずは宣言方法の違いを見てみましょう。

char str1[] = "Hello";  // char配列
char *str2 = "Hello";   // charポインタ

str1実体を持つ配列であり、メモリ上に6バイト(”Hello” + ‘ ‘)が確保されます。一方、str2文字列リテラルが格納されているメモリ領域へのポインタです。

書き換え可能性の違い

char配列であるstr1は、配列内の文字を自由に変更できます。

str1[0] = 'h'; // OK

しかし、char* str2 = "Hello"; のように文字列リテラルにポインタでアクセスする場合、その内容を変更するのは未定義動作です。

str2[0] = 'h'; // ❌ 未定義動作(実行時エラーの可能性)

メモリ構造の違い

  • char配列スタック上に確保されるローカルな実体です。
  • charポインタは、定数領域(読み取り専用)ヒープ領域(mallocなど)を指すことがあり、より柔軟ではありますが、メモリの扱いに注意が必要です。

サイズの取得に関する違い

配列の場合、sizeof(str1) は配列の全体のバイト数を返します。

char str1[] = "Hello";
printf("%lu", sizeof(str1)); // → 6(' '含む)

一方、ポインタでは sizeof(str2)ポインタそのもののサイズ(通常4〜8バイト)を返すため、配列サイズを取得する目的では使えません

char *str2 = "Hello";
printf("%lu", sizeof(str2)); // → 8(64bit環境)

 

まとめ:使い分けのポイント

項目char配列charポインタ
内容の変更可能原則不可(リテラルの場合)
サイズ取得sizeof()で正確に取得可strlen()等が必要
書き換え用途向いている向いていない(読み取り専用)
柔軟性固定サイズ柔軟だが注意が必要

6. 関数へのchar配列の渡し方

C言語では、配列を関数に渡す際に「値渡し」ではなく「ポインタ渡し」が行われます。これは char配列 も同様で、関数に渡すときには配列の先頭アドレス(ポインタ)が渡されることになります。

この仕組みを理解することは、関数間で文字列を操作したり、内容を変更したりする場面で非常に重要です。

基本例:char配列を引数に渡す

#include <stdio.h>

void printString(char str[]) {
    printf("文字列: %s
", str);
}

int main() {
    char greeting[] = "Hello";
    printString(greeting);
    return 0;
}

この例では、printString 関数に char[] 型の引数を渡していますが、実際には char* として受け取られます。つまり、char str[]char *str と同じ意味になります。

内容を書き換える関数

関数内で配列の内容を書き換える場合も、渡されたアドレスを通じて直接データを操作することができます。

#include <stdio.h>

void toUpperCase(char str[]) {
    for (int i = 0; str[i] != ' '; i++) {
        if ('a' <= str[i] && str[i] <= 'z') {
            str[i] = str[i] - ('a' - 'A');
        }
    }
}

int main() {
    char text[] = "hello world";
    toUpperCase(text);
    printf("%s
", text); // 出力: HELLO WORLD
    return 0;
}

このように、配列の中身を変更したい場合でも、ポインタとして扱われるため、関数内の操作が呼び出し元に反映されます。

配列サイズの管理

C言語では、関数に配列を渡すときにサイズ情報は一緒に渡されません。そのため、安全に操作するためにはサイズも引数で渡すのがベストプラクティスです。

void printChars(char str[], int size) {
    for (int i = 0; i < size; i++) {
        printf("%c ", str[i]);
    }
    printf("
");
}

また、strlen 関数を使って文字列長を動的に取得することも一般的です。ただし、ヌル終端されていない配列には使わないよう注意しましょう。

constを使った読み取り専用引数

文字列を関数内で変更しない場合は、const char* を使って読み取り専用であることを明示するのが望ましい書き方です。

void printConstString(const char *str) {
    printf("%s
", str);
}

これは、意図しない書き換えを防ぎ、関数の仕様を明確に伝えることにもつながります。

7. 実践例:文字列の逆順表示

ここでは、これまでに学んできた char配列 の知識を活用して、文字列を逆順に表示するプログラムを実際に作成してみましょう。

目的

ユーザーが入力した文字列を、末尾から先頭に向かって1文字ずつ出力します。これは、配列操作や文字列長の取得、ループ処理の練習として非常に有効です。

サンプルコード

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

void printReversed(char str[]) {
    int len = strlen(str);
    for (int i = len - 1; i >= 0; i--) {
        putchar(str[i]);
    }
    putchar('\n');
}

int main() {
    char text[100];

    printf("文字列を入力してください:");
    fgets(text, sizeof(text), stdin);

    // 改行文字を除去(fgets使用時の対策)
    size_t len = strlen(text);
    if (len > 0 && text[len - 1] == '\n') {
        text[len - 1] = '\0';
    }

    printf("逆順に表示すると:");
    printReversed(text);

    return 0;
}

解説

  • fgets 関数を使うことで、空白を含む文字列も安全に取得できます。
  • 入力の末尾に付加される ' '(改行文字)を取り除く処理も忘れずに行います。
  • strlen() を使って文字列の長さを取得し、逆順ループで1文字ずつ表示します。

実行例

文字列を入力してください:OpenAI
逆順に表示すると:IAnepO

応用のヒント

この処理は、文字列の回文判定スタック構造の理解など、アルゴリズムを学ぶ上での応用にもつながります。また、ポインタ演算を使った書き換えも可能なので、より高度な練習題材にもなります。

8. よくあるエラーとその対処法

C言語で char配列 を扱う際、初心者から上級者まで陥りやすい落とし穴がいくつか存在します。ここでは、頻出するエラーとその防止策・解決策を具体的に解説します。

1. ヌル終端(

2. バッファサイズ不足

)の付け忘れ

最も多いミスのひとつが、文字列の末尾にヌル文字(\0)を付け忘れることです。

char str[5] = {'H', 'e', 'l', 'l', 'o'}; // ❌ 末尾に'\0'なし
printf("%s\n", str); // 未定義の動作

対処法:

char str[6] = {'H', 'e', 'l', 'l', 'o', '\0'}; // ✅ 正しい

または、文字列リテラルを使用することで自動的にヌル文字が追加されます。

3. 文字列リテラルの書き換え

strcpystrcatなどの関数で、コピー先の配列サイズが足りていないと、メモリ破壊(バッファオーバーフロー)を引き起こします。

char str1[5];
strcpy(str1, "Hello"); // ❌ 配列サイズ5に6文字コピー

対処法:

  • コピー先には十分なサイズを確保する(例:「文字数 + 1」)
  • より安全なstrncpyの使用も検討する
char str1[6];
strncpy(str1, "Hello", sizeof(str1) - 1);
str1[5] = '\0'; // 念のため末尾にヌル文字を明示

4. fgetsの改行文字の処理忘れ

char *str = "Hello"; のように宣言されたポインタは、書き換え不可の領域を指している可能性があります。これに書き込みを行うと、実行時エラーになります。

char *str = "Hello";
str[0] = 'h'; // ❌ 実行時にSegmentation fault

対処法:

char str[] = "Hello"; // ✅ 書き換え可能な配列として宣言
str[0] = 'h';

5. ポインタと配列の混同

fgetsを使って文字列を取得した場合、末尾に改行文字(\n)が残る点に注意が必要です。

fgets(str, sizeof(str), stdin);
// strには "Hello\n\0" のような内容が入る

対処法:

size_t len = strlen(str);
if (len > 0 && str[len - 1] == '\n') {
    str[len - 1] = '\0';
}

9. まとめ

見た目が似ているため、char*char[]を混同して扱い、未定義動作につながるケースもあります。サイズ取得 (sizeof) の違い書き換え可否の違いを意識する必要があります。

本記事で学んだこと

本記事では、「C言語のchar配列」について、基本から応用まで段階的に解説してきました。最後に、本記事の内容を振り返りつつ、今後の学習の指針をご紹介します。

今後の学習へのアドバイス

  • char配列の役割と宣言方法
    C言語では文字列型が存在しないため、文字列を扱うにはchar型の配列を使用します。
  • 文字列リテラルとヌル文字(\0)の重要性
    文字列を表現するには、末尾にヌル文字が必要不可欠です。
  • 標準ライブラリ関数を用いた文字列操作
    strcpy, strcat, strlen, strcmp などを使えば、効率的に文字列を処理できます。
  • char配列とcharポインタの違い
    配列とポインタは似て非なるものであり、メモリ構造や書き換え可否などに明確な差があります。
  • 関数への配列の渡し方と安全性への配慮
    配列は実際にはポインタとして渡されるため、書き換えやサイズ管理には注意が必要です。
  • 実践例やよくあるエラーを通じた理解の定着
    実際のコードでの応用やエラーの対処方法を通して、より現実的な使い方を学びました。

今後の学習へのアドバイス

char配列の使い方を理解することは、C言語におけるメモリ操作の基礎を理解する第一歩です。ここで学んだ知識は、以下のような次のステップに活かせます。

  • ポインタ演算:char配列とポインタの理解をさらに深める
  • 動的メモリ管理mallocfree を使った高度な文字列処理
  • 構造体との組み合わせ:データ構造を使った実践的なアプリケーション設計

また、他人が書いたコードを読むことで、自分にはなかった視点や書き方を吸収することができます。実際のプロジェクトやOSS(オープンソースソフトウェア)を読む習慣も非常に有効です。

C言語は自由度が高い分、正しく扱わないと危険な側面も持ちますが、基礎を丁寧に積み重ねれば非常に強力な武器になります。char配列の理解はその第一歩。ぜひ、本記事を参考にしながら、着実にスキルを高めていってください。