C言語の配列とは?宣言・初期化から要素数、2次元配列、ポインタとの関係まで徹底解説のカバー画像

C言語の配列とは?宣言・初期化から要素数、2次元配列、ポインタとの関係まで徹底解説

公開日:2025/04/08最終更新日:2025/04/08

C言語の配列は複数の文字列を一括で管理できるため、非常に便利な機能です。


しかし、扱い方を誤るとバグの原因となり得るため、慎重に使わなければなりません。


この記事では、C言語の配列を使うメリットや使い方、注意点などを詳しく解説していきます。すでに配列を使っている方や勉強中の方の参考になる内容となっています。


ぜひ最後までご覧いただき、C言語の配列の理解を深めてください。

1.C言語の配列とは?

C言語の配列とは、同じ型のデータを連続して格納できるデータ構造の一つです。主に複数の整数や文字列を一括で管理する際に使われます。


各要素にはインデックス(添字)でアクセスでき、配列名とインデックスを使うことで個別の値を取得・変更できます。例えば以下のように記述します。

int score[5];

int score[5][7];

配列はプログラム内でデータを効率的に扱うための基本的な仕組みであり、データ管理や処理を行ううえで欠かせない存在です。C言語では配列のサイズやメモリ管理を意識する必要があるため、基礎をしっかり理解しておきましょう。

2.C言語の配列を使うメリット

C言語で配列を使うメリットは主に以下の5点です。それぞれを解説していきます。

メリット

内容

複数のデータを一括で管理できる

一つ一つの要素を管理せずに、まとめて管理できる

ループ処理と組み合わせて効率的に操作できる

同じ処理を複数のデータに対して一気に実行できる

メモリ上に連続して配置されるためアクセスが高速

CPUのキャッシュ効率が良く、パフォーマンスが求められる処理に向いている

ポインタと組み合わせて柔軟な設計ができる

アドレス操作を活用して、高度なデータ処理や動的アクセスも実現できる

C言語の基礎であり、他の構造(構造体、関数引数など)への応用が利く

配列の理解は、より複雑な構造や処理の土台になる

3.C言語の配列の使い方

C言語の配列の使い方として、主に6つを解説していきます。正しく配列の使い方を理解するためにも、最後までお読みください。

  • 配列の宣言

  • 配列の初期化

  • 配列への代入方法

  • 全要素の表示

  • 要素数の計算

  • 配列のコピー

配列の宣言

C言語で配列を使用するには、まず配列を宣言する必要があります。配列の宣言は、データ型、配列名、要素数を指定して行います。例えば、整数型の配列を宣言する場合、以下のように記述します。

int numbers[5];

この例では、int型のデータを5つ格納できる配列numbersを宣言しています。配列名は変数名と同様に任意の名前を付けることができ、要素数は配列内に格納するデータの数を指定します。配列の宣言位置は、変数の宣言と同様に、使用する前に行わなければなりません。

配列の初期化

配列を宣言した後、初期値を設定できます。初期化は、宣言と同時に行うことが一般的です。例えば、以下のように記述します。

int numbers[5] = {1, 2, 3, 4, 5};

この例では、numbers配列の各要素に1から5の値を順に代入しています。初期化の際、要素数を省略することも可能です。

int numbers[] = {1, 2, 3, 4, 5};

この場合、コンパイラが初期値の数から要素数を自動的に判断します。

配列への代入方法

配列への代入方法は、主に以下の2種類あります。一つずつ見ていきましょう。


まずは、直接要素を指定して代入方法を解説します。


配列の各要素には、インデックスを使用して直接値を代入できます。インデックスは0から始まり、配列の要素数-1までの範囲で指定します。例えば、以下のように記述します。

numbers[0] = 10;

numbers[1] = 20;

この例では、numbers配列の0番目の要素に10を、1番目の要素に20を代入しています。


次にループ関数を使って代入を解説します。


複数の要素に対して同じ処理を行う場合、ループを使用すると効率的です。例えば、以下のように記述します。

for (int i = 0; i < 5; i++) {

    numbers[i] = i * 10;

}

この例では、numbers配列の各要素に0から40までの値を10刻みで代入しています。

全要素の表示

配列の全要素を表示するには、ループを使用して各要素にアクセスし、printf関数などで出力します。例えば、以下のように記述します。

for (int i = 0; i < 5; i++) {

    printf("numbers[%d] = %d\n", i, numbers[i]);

}

この例では、numbers配列の各要素のインデックスと値を順に表示しています。

要素数の計算

配列の要素数を取得するには、sizeof演算子を使用します。


sizeof演算子は、変数や型のメモリサイズをバイト単位で返し、配列の要素数は、配列全体のサイズを各要素のサイズで割ることで求められます。例えば、参考として以下のコードを見ていきましょう。

int arraySize = sizeof(numbers) / sizeof(numbers[0]);

この例では、numbers配列の要素数を計算し、arraySizeに格納しています。

配列のコピー

配列の内容を別の配列にコピーするには、ループを使用して各要素を個別に代入する方法が一般的です。例えば、以下のように記述します。

int copy[5];

for (int i = 0; i < 5; i++) {

    copy[i] = numbers[i];

}

この例では、numbers配列の各要素をcopy配列にコピーしています。

4.多次元配列の使い方

ここからは多次元配列の使い方を詳しく解説していきます。より多くの要素をまとめて管理できるため、多次元配列を使えると非常に便利です。ぜひ最後までお読みください。

  • 2次元配列の概念

  • 2次元配列の宣言と初期化方法

  • 2次元配列の操作方法

2次元配列の概念

C言語の2次元配列は、「行」と「列」を持つ、配列の中にさらに配列があるような構造です。


Excelの表のように、データを行列で整理・管理したいときに便利です。たとえば、5人分のテストの点数(各教科ごと)を格納するようなケースでは、2次元配列を使えば、データをわかりやすく、かつ効率的に処理できます。


このように、複数の属性を持つデータをまとめて扱う場面で、2次元配列は非常に有効な手段です。

2次元配列の宣言と初期化方法

2次元配列は、以下のように「行数」と「列数」の2つを指定して宣言します。

int scores[3][4];

この例では、3行4列の整数型配列を作成しています。初期化は、以下のようにネストされた波かっこ {} を使って行います。

int scores[2][3] = {

    {80, 70, 90},

    {60, 85, 75}

};

初期値の並びに従って、自動的に行と列に値が割り当てられます。要素数を省略しても、初期値の個数から自動的に判断されるため、柔軟な記述が可能です(例:int scores[][3] = {...})。

2次元配列の操作方法

2次元配列の要素には、インデックスを2つ指定してアクセスします。例えば、scores[1][2]は「2行目・3列目」の要素を意味します。


要素を表示する場合は、ネストされたfor文を使って操作します。

#include <stdio.h>


int main() {

    int scores[2][3] = {

    {80, 70, 90},

    {60, 85, 75}

    };


    for (int i = 0; i < 2; i++) {

        for (int j = 0; j < 3; j++) {

            printf("scores[%d][%d] = %d\n", i, j, scores[i][j]);

        }

    }

    return 0;

}

出力結果は以下のとおりです。

scores[0][0] = 80

scores[0][1] = 70

scores[0][2] = 90

scores[1][0] = 60

scores[1][1] = 85

scores[1][2] = 75

この方法で、行列構造の全要素を簡単に処理できます。C言語では、2次元配列も1次元的なメモリに連続して格納されるため、配列サイズやインデックスの範囲に注意して操作することが、バグ防止やパフォーマンス向上につながります。

5.C言語の配列とポインタの関係

C言語では、配列とポインタは密接な関係があります。配列名は先頭要素のアドレスを指すポインタとして扱われるため、ポインタ演算を使って配列を操作できます。

int arr[5] = {10, 20, 30, 40, 50};

int *ptr = arr;

このように書くことで、ptr[2]や*(ptr + 2)という形式で要素にアクセス可能です。ポインタを使うと、関数に配列を渡すときや、動的に配列を処理する場面で柔軟な設計が可能になります。


一方で、ポインタの誤操作によって配列外のメモリにアクセスしてしまうリスクもあるため、インデックス管理と境界チェックを徹底することが重要です。特に、組み込み系の開発やメモリ最適化が求められる現場では、ポインタを正しく使いこなすスキルが求められます。

6.文字列と配列の関係

C言語の配列と文字列も密接な関係があります。以下2つについて詳しく解説していきます。配列で文字列を使うことも多いため、最後まで読み、理解を深めてください。

  • 概念

  • 操作方法

概念

C言語では、文字列は「文字型(char)の配列」として扱われます。たとえば "Hello" という文字列は、以下のように宣言できます。

char str[] = "Hello";

メモリ上には H, e, l, l, o, \0(NULL文字)が順に格納されます。


このNULL文字('\0')が文字列の終端を示す重要な役割を果たしています。これにより、関数側はどこまでが文字列なのか判断可能です。C言語では文字列を安全に扱うために、この終端を常に意識しなければならないため、注意が必要です。

操作方法

文字列の操作は、配列を直接操作する方法と、標準ライブラリ関数を使う方法があります。たとえば、以下の標準ライブラリ関数があります。

標準ライブラリ関数

内容

strcpy()

文字列のコピー

strcat()

文字列の結合

strlen()

長さの取得

文字列のコピー関数「strcpy()」を使った例は以下のとおりです。

char src[] = "Hello";

char dest[20];

strcpy(dest, src);

このように文字列の処理を簡潔に行える一方で、C言語の標準関数は配列サイズのチェックを行わないため、バッファオーバーフローのリスクがあります。事前に十分な配列サイズを確保しておくことが重要です。

7.C言語で配列を使う際の注意点5つ

C言語の配列は正しく使えれば便利ですが、使い方には注意が必要です。ここでは、配列を使う際の注意点を以下の5つ解説していきます。

  • メモリ管理とスタック・ヒープの違いを理解する

  • 可変長配列(VLA)を使う際の落とし穴

  • 境界チェックを怠るとバグの温床になりやすい

  • 配列の初期化忘れが意図しない動作を招く

  • 配列サイズの管理は定数やマクロで明確にする

メモリ管理とスタック・ヒープの違いを理解する

C言語で配列を使うとき、どこにメモリが確保されるかによって動作や制限が変わってきます。特に大切なのが「スタック」と「ヒープ」という2つのメモリ領域の違いです。

領域名

メリット

デメリット

スタック(固定長配列)

高速なアクセスが可能

・使えるサイズに限りがある

・大きすぎる配列を宣言するとスタックオーバーフローでプログラムが落ちることもある

ヒープ(動的配列)

柔軟にサイズを決められる

・malloc()で確保したら、使い終わったら必ずfree()で手動でメモリを解放する必要がある

・忘れるとメモリリークの原因となる

このように、C言語の配列は、スタックかヒープかによって動作や制限が変わります。処理の目的や配列の大きさに応じて、適切なメモリ領域を選ぶことが、安定したプログラムの鍵です。

可変長配列(VLA)を使う際の落とし穴

C99以降では、可変長配列(VLA)を使って実行時にサイズを決めることができますが、使用には注意が必要です。スタックに確保されるため、サイズが大きすぎるとスタックオーバーフローの原因になります。


なおC99とは、1999年にISOによって制定されたC言語の標準仕様バージョンのことです。C99に対応したバージョンであれば、可変長配列を扱えます。たとえば以下のように記述可能となりました。

int n;

scanf("%d", &n);

int arr[n];  // ← C99以降で使える書き方

また、一部のコンパイラでは非対応なこともあるため、移植性にも配慮が必要です。特に組込み系のプロジェクトでは、スタックサイズが非常に小さい場合が多く、想定外のサイズで配列を確保しようとするとプログラムがクラッシュすることもあります。


加えて、VLAは静的解析ツールでの検出が困難な場合もあるため、デバッグ性や保守性の面でもリスクがあります。長期的な運用を考えるなら、基本的には動的メモリ確保か固定長配列の利用を検討する方が安全です。

境界チェックを怠るとバグの温床になりやすい

C言語の配列では、要素数を超えたアクセスをしてもコンパイルエラーにはなりません。そのため、ループ処理などで境界を超えてアクセスしてしまうと、意図しないメモリ領域にアクセスしてバグやセキュリティホールの原因になります。配列操作では範囲チェックを徹底しましょう。


特に文字列処理では、終端のNULL文字(\0)を忘れてしまうと、予期せぬメモリ読み込みを引き起こすことがあります。


例えば、以下のように\0を忘れたコードを書くと危険です。

#include <stdio.h>


int main() {

    char str[5] = {'H', 'e', 'l', 'l', 'o'};  // ← '\0' がない!

    printf("文字列:%s\n", str);  // 予期しないメモリを読む可能性あり

    return 0;

}

安全にメモリを使うためには、以下のように記述します。

#include <stdio.h>


int main() {

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

    printf("文字列:%s\n", str);  // 正しく "Hello" を出力

    return 0;

}

危険な状態のままコーディングを続けると、セグメンテーションフォールトや最悪の場合、外部からの不正アクセスにつながることもあります。配列の境界を意識した安全なコーディングが、信頼性の高いシステム構築には欠かせないため、境界チェックは行うようにしましょう。

配列の初期化忘れが意図しない動作を招く

配列を宣言しただけでは、その中身は初期化されていないため、不定の値が格納されています。初期化を怠ると、デバッグが難しい不具合につながることがあるため、注意が必要です。


配列を使う前に必ず明示的に初期化することで、予期しない挙動を未然に防ぐことができます。


例えば、以下のコードのようにint型の配列でint arr[5];とだけ宣言した場合、arr[0]には何が入っているかは保証されません。

#include <stdio.h>


int main() {

    int numbers[5];  // 初期化していない!


    printf("配列の中身(初期化なし):\n");

    for (int i = 0; i < 5; i++) {

        printf("numbers[%d] = %d\n", i, numbers[i]);

    }


    return 0;

}

一見正常に動作していても、実はゴミデータによって偶然正しく見えているだけ、ということも珍しくありません。


正常に動作させるためにも、以下のコードのように配列の初期化を行います。

#include <stdio.h>


int main() {

    int numbers[5] = {0};  // すべての要素を0で初期化


    printf("配列の中身(初期化あり):\n");

    for (int i = 0; i < 5; i++) {

        printf("numbers[%d] = %d\n", i, numbers[i]);

    }


    return 0;

}

この例のように配列の初期化はint arr[5] = {0};のように書くか、ループで1つずつ0代入するなど、安全第一の設計を心がけましょう。

配列サイズの管理は定数やマクロで明確にする

配列のサイズをソースコード中に直接書くと、可読性が下がり、修正ミスの原因にもなります。#defineやconstを使ってサイズを明確に定義しておくことで、保守性や再利用性が向上します。特にチーム開発や大規模なコードでは、サイズの一元管理が重要です。


例えば、以下のように配列を使った関数を複数作成する場合、「サイズ5の配列」という情報が複数箇所にベタ書きされていると、サイズ変更時にミスが生じやすくなります。

#include <stdio.h>


int main() {

    int data[5];  // サイズが直接5と書かれている


    for (int i = 0; i < 5; i++) {  // ここでも「5」を繰り返し使用

        data[i] = i * 10;

    }


    for (int i = 0; i < 5; i++) {

        printf("data[%d] = %d\n", i, data[i]);

    }


    return 0;

}

#define BUFFER_SIZE 5 のように一元化しておけば、変更も簡単でエラーを減らせます。

#include <stdio.h>


#define ARRAY_SIZE 5  // マクロで定義(const intでもOK)


int main() {

    int data[ARRAY_SIZE];


    for (int i = 0; i < ARRAY_SIZE; i++) {

        data[i] = i * 10;

    }


    for (int i = 0; i < ARRAY_SIZE; i++) {

        printf("data[%d] = %d\n", i, data[i]);

    }


    return 0;

}

8.C言語の配列でよくある質問

C言語の配列でよくある質問を2つ解説していきます。

  • 配列の追加方法は?

  • 要素数を指定しない場合としなくてもよい場合の違いは?

配列の追加方法は?

C言語の配列は固定長のため、一度宣言したサイズを後から拡張することはできません。たとえば int arr[5]; のように宣言した場合、6個目のデータを追加することはできず、メモリ外アクセスになってしまいます。


追加のような操作をしたい場合は、以下の方法がよく使われます。


新しい大きな配列を用意してコピーする

int old_arr[5] = {1, 2, 3, 4, 5};

int new_arr[6];

for (int i = 0; i < 5; i++) new_arr[i] = old_arr[i];

new_arr[5] = 6;

動的メモリ確保(malloc/realloc)を使う

int arr = malloc(sizeof(int) 5);

arr = realloc(arr, sizeof(int) * 6);

arr[5] = 6;

固定長配列では「追加」ができないことを前提に設計するか、malloc/realloc等を活用して柔軟に対応する必要があります。

要素数を指定しない場合としなくてもよい場合の違いは?

C言語では、配列の宣言時に要素数を指定することも、指定せずに初期値の個数に任せることも可能です。

// 要素数を指定する場合

int arr1[3] = {1, 2, 3};


// 要素数を省略する場合

int arr2[] = {1, 2, 3};

要素を指定する場合と指定しない場合の主なメリットデメリットは以下のとおりです。

ケース

メリット

デメリット

要素を指定する場合

コードの意図が明確になりやすい

サイズ変更時の操作が手間になりやすい

要素数を指定しない場合

サイズが決まっていない場合でも利用できる

再利用や拡張に向かない場合がある

実務では、柔軟性と可読性のバランスを取りつつ、メモリ管理や可変性の必要性に応じて使い分けることが求められます。


関連記事

C言語とは?できることやC++との違い、学習方法など初心者向けにわかりやすく解説


C言語プログラミング能力認定試験とは?合格率や難易度、対策方法を解説

9.まとめ

今回は、C言語の配列について、メリットや使い方、注意点などをお話ししました。


複数の要素をまとめて管理できるため、配列を使えると効率的なコーディングができるようになります。しかし、扱い方に気をつけないと、バグや不正アクセスの原因を作ることにもなりかねるため、注意が必要です。


最後までお読みいただきありがとうございました。

本記事が皆様にとって少しでもお役に立てますと幸いです。


フリーランスボード」は、数多くのフリーランスエージェントが掲載するITフリーランスエンジニア・ITフリーランス向けの案件・求人を一括検索できるサイトです。


開発環境、職種、単価、稼働形態、稼働日数など様々な条件から、あなたに最適なフリーランス案件・求人を簡単に見つけることができます。

単価アップを目指す方や、自分の得意なスキルを活かせる案件に参画したい方は、ぜひ「フリーランスボード」をご利用ください。

無料で登録したらスカウトを待つだけ フリーランスの新しい仕事探しを始めよう

フルリモート案件を 無料登録した方限定で配信中

目次

1.C言語の配列とは?

2.C言語の配列を使うメリット

3.C言語の配列の使い方

配列の宣言

配列の初期化

配列への代入方法

全要素の表示

要素数の計算

配列のコピー

4.多次元配列の使い方

2次元配列の概念

2次元配列の宣言と初期化方法

2次元配列の操作方法

5.C言語の配列とポインタの関係

6.文字列と配列の関係

概念

操作方法

7.C言語で配列を使う際の注意点5つ

メモリ管理とスタック・ヒープの違いを理解する

可変長配列(VLA)を使う際の落とし穴

境界チェックを怠るとバグの温床になりやすい

配列の初期化忘れが意図しない動作を招く

配列サイズの管理は定数やマクロで明確にする

8.C言語の配列でよくある質問

配列の追加方法は?

要素数を指定しない場合としなくてもよい場合の違いは?

9.まとめ