配列

[C言語] 配列の使い方についてわかりやすく詳しく解説

C言語における配列は、同じデータ型の要素を連続して格納するためのデータ構造です。

配列は宣言時にサイズを指定し、固定長のメモリ領域を確保します。

要素にはインデックスを使用してアクセスし、インデックスは0から始まります。

例えば、int array[5]は5つの整数を格納できる配列を宣言します。

配列はループを用いて効率的に操作することができ、forループを使って全要素にアクセスするのが一般的です。

また、配列は関数に渡す際にはポインタとして扱われるため、注意が必要です。

配列の基本

配列とは何か

配列とは、同じデータ型の複数の要素を一つのまとまりとして扱うためのデータ構造です。

C言語では、配列を使うことで、同じ種類のデータを効率的に管理し、操作することができます。

配列は、連続したメモリ領域に格納され、各要素にはインデックスを使ってアクセスします。

配列の宣言と初期化

配列を使用するためには、まず宣言を行います。

宣言時には、配列のデータ型と要素数を指定します。

以下に、配列の宣言と初期化の例を示します。

#include <stdio.h>
int main() {
    // 整数型の配列を宣言し、初期化
    int numbers[5] = {1, 2, 3, 4, 5};
    // 配列の要素を出力
    for (int i = 0; i < 5; i++) {
        printf("%d ", numbers[i]);
    }
    return 0;
}
1 2 3 4 5

この例では、整数型の配列numbersを宣言し、5つの要素を初期化しています。

forループを使って、各要素を順に出力しています。

配列のメモリ配置

配列は、連続したメモリ領域に格納されます。

各要素は、配列の先頭から順にメモリに配置され、インデックスを使ってアクセスできます。

例えば、int型の配列の場合、各要素は4バイトのメモリを占有します。

以下に、配列のメモリ配置のイメージを示します。

インデックスメモリアドレス
00x10001
10x10042
20x10083
30x100C4
40x10105

この表は、int型の配列numbersがメモリにどのように配置されるかを示しています。

配列の要素へのアクセス

配列の要素にアクセスするには、インデックスを使用します。

インデックスは0から始まり、配列の要素数-1までの範囲で指定します。

以下に、配列の要素にアクセスする例を示します。

#include <stdio.h>
int main() {
    int numbers[5] = {10, 20, 30, 40, 50};
    // 配列の要素にアクセスして出力
    printf("First element: %d\n", numbers[0]);
    printf("Third element: %d\n", numbers[2]);
    printf("Last element: %d\n", numbers[4]);
    return 0;
}
First element: 10
Third element: 30
Last element: 50

この例では、配列numbersの特定の要素にアクセスし、その値を出力しています。

インデックスを使うことで、任意の要素に直接アクセスすることができます。

配列の操作

配列の要素の変更

配列の要素を変更するには、インデックスを指定して新しい値を代入します。

以下に、配列の要素を変更する例を示します。

#include <stdio.h>
int main() {
    int numbers[5] = {10, 20, 30, 40, 50};
    // 配列の要素を変更
    numbers[2] = 100;
    numbers[4] = 200;
    // 変更後の配列を出力
    for (int i = 0; i < 5; i++) {
        printf("%d ", numbers[i]);
    }
    return 0;
}
10 20 100 40 200

この例では、配列numbersの3番目と5番目の要素を新しい値に変更しています。

forループを使って、変更後の配列を出力しています。

配列の長さの取得

C言語では、配列の長さを直接取得する関数はありませんが、配列のサイズを計算することで長さを求めることができます。

以下に、配列の長さを取得する方法を示します。

#include <stdio.h>
int main() {
    int numbers[5] = {10, 20, 30, 40, 50};
    // 配列の長さを計算
    int length = sizeof(numbers) / sizeof(numbers[0]);
    printf("Length of array: %d\n", length);
    return 0;
}
Length of array: 5

この例では、sizeof演算子を使って配列全体のサイズを取得し、1つの要素のサイズで割ることで配列の長さを計算しています。

配列のコピー

配列をコピーするには、forループを使って要素を一つずつコピーします。

以下に、配列のコピーの例を示します。

#include <stdio.h>
int main() {
    int source[5] = {1, 2, 3, 4, 5};
    int destination[5];
    // 配列をコピー
    for (int i = 0; i < 5; i++) {
        destination[i] = source[i];
    }
    // コピー後の配列を出力
    for (int i = 0; i < 5; i++) {
        printf("%d ", destination[i]);
    }
    return 0;
}
1 2 3 4 5

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

forループを使って、コピー後の配列を出力しています。

配列のソート

配列をソートするには、一般的にバブルソートやクイックソートなどのアルゴリズムを使用します。

ここでは、バブルソートを使った配列のソートの例を示します。

#include <stdio.h>
void bubbleSort(int array[], int size) {
    for (int step = 0; step < size - 1; ++step) {
        for (int i = 0; i < size - step - 1; ++i) {
            if (array[i] > array[i + 1]) {
                // 要素を交換
                int temp = array[i];
                array[i] = array[i + 1];
                array[i + 1] = temp;
            }
        }
    }
}
int main() {
    int data[5] = {64, 34, 25, 12, 22};
    // 配列をソート
    bubbleSort(data, 5);
    // ソート後の配列を出力
    for (int i = 0; i < 5; i++) {
        printf("%d ", data[i]);
    }
    return 0;
}
12 22 25 34 64

この例では、bubbleSort関数を使って配列dataを昇順にソートしています。

バブルソートは、隣接する要素を比較し、必要に応じて交換することで配列をソートします。

多次元配列

多次元配列の宣言と初期化

多次元配列は、配列の中に配列を持つ構造で、特に2次元配列は行列のようにデータを扱うことができます。

C言語では、以下のように多次元配列を宣言し、初期化します。

#include <stdio.h>
int main() {
    // 2次元配列の宣言と初期化
    int matrix[3][3] = {
        {1, 2, 3},
        {4, 5, 6},
        {7, 8, 9}
    };
    // 配列の要素を出力
    for (int i = 0; i < 3; i++) {
        for (int j = 0; j < 3; j++) {
            printf("%d ", matrix[i][j]);
        }
        printf("\n");
    }
    return 0;
}
1 2 3
4 5 6
7 8 9

この例では、3×3の2次元配列matrixを宣言し、初期化しています。

forループを使って、各要素を行ごとに出力しています。

多次元配列の要素へのアクセス

多次元配列の要素にアクセスするには、各次元のインデックスを指定します。

以下に、特定の要素にアクセスする例を示します。

#include <stdio.h>
int main() {
    int matrix[2][2] = {
        {10, 20},
        {30, 40}
    };
    // 特定の要素にアクセスして出力
    printf("Element at (0, 1): %d\n", matrix[0][1]);
    printf("Element at (1, 0): %d\n", matrix[1][0]);
    return 0;
}
Element at (0, 1): 20
Element at (1, 0): 30

この例では、2×2の配列matrixの特定の要素にアクセスし、その値を出力しています。

インデックスは行と列の順に指定します。

多次元配列のメモリ配置

多次元配列は、メモリ上では一次元配列として連続的に配置されます。

C言語では、行優先(row-major order)でメモリに配置されます。

以下に、2次元配列のメモリ配置のイメージを示します。

メモリアドレス
000x10001
010x10042
020x10083
100x100C4
110x10105
120x10146
200x10187
210x101C8
220x10209

この表は、3×3の2次元配列がメモリにどのように配置されるかを示しています。

行ごとに連続してメモリに配置されるため、行のインデックスが先に変化します。

配列とポインタ

配列名とポインタの関係

C言語では、配列名は配列の先頭要素へのポインタとして扱われます。

つまり、配列名は配列の最初の要素のアドレスを指しています。

以下に、配列名とポインタの関係を示す例を示します。

#include <stdio.h>
int main() {
    int numbers[3] = {10, 20, 30};
    int *ptr = numbers; // 配列名をポインタに代入
    // ポインタを使って配列の要素にアクセス
    printf("First element: %d\n", *ptr);
    printf("Second element: %d\n", *(ptr + 1));
    printf("Third element: %d\n", *(ptr + 2));
    return 0;
}
First element: 10
Second element: 20
Third element: 30

この例では、配列numbersの名前をポインタptrに代入しています。

ポインタを使って、配列の各要素にアクセスしています。

ポインタを使った配列操作

ポインタを使うことで、配列の要素を操作することができます。

ポインタ演算を利用して、配列の要素を順に処理することが可能です。

以下に、ポインタを使った配列操作の例を示します。

#include <stdio.h>
int main() {
    int numbers[5] = {1, 2, 3, 4, 5};
    int *ptr = numbers;
    // ポインタを使って配列の要素を2倍にする
    for (int i = 0; i < 5; i++) {
        *(ptr + i) *= 2;
    }
    // 変更後の配列を出力
    for (int i = 0; i < 5; i++) {
        printf("%d ", numbers[i]);
    }
    return 0;
}
2 4 6 8 10

この例では、ポインタptrを使って配列numbersの各要素を2倍にしています。

ポインタ演算を使うことで、配列の要素を効率的に操作できます。

配列とポインタの違い

配列とポインタは似ていますが、いくつかの重要な違いがあります。

特徴配列ポインタ
メモリ配置連続したメモリ領域に配置任意のメモリアドレスを指す
サイズ変更固定サイズ動的に変更可能
初期化宣言時に初期化が必要任意のアドレスを代入可能
演算インデックスを使ってアクセスポインタ演算が可能
  • メモリ配置: 配列は宣言時に連続したメモリ領域が確保されますが、ポインタは任意のメモリアドレスを指すことができます。
  • サイズ変更: 配列のサイズは固定ですが、ポインタは動的にメモリを割り当てることができ、サイズを変更できます。
  • 初期化: 配列は宣言時に初期化が必要ですが、ポインタは任意のアドレスを代入することができます。
  • 演算: 配列はインデックスを使ってアクセスしますが、ポインタはポインタ演算を使って要素にアクセスできます。

これらの違いを理解することで、適切に配列とポインタを使い分けることができます。

配列の応用例

配列を使った文字列操作

C言語では、文字列は文字の配列として扱われます。

文字列操作には、文字配列を使うことが一般的です。

以下に、配列を使った文字列操作の例を示します。

#include <stdio.h>
#include <string.h>
int main() {
    char greeting[20] = "Hello, ";
    char name[] = "World!";
    // 文字列を結合
    strcat(greeting, name);
    // 結合後の文字列を出力
    printf("%s\n", greeting);
    return 0;
}
Hello, World!

この例では、strcat関数を使って、greeting配列にname配列の文字列を結合しています。

文字列は文字の配列として扱われ、strcatなどの標準ライブラリ関数を使って操作できます。

配列を使った数値計算

配列を使うことで、複数の数値を一度に処理することができます。

以下に、配列を使った数値計算の例を示します。

#include <stdio.h>
int main() {
    int numbers[] = {10, 20, 30, 40, 50};
    int sum = 0;
    int length = sizeof(numbers) / sizeof(numbers[0]);
    // 配列の要素を合計
    for (int i = 0; i < length; i++) {
        sum += numbers[i];
    }
    // 合計を出力
    printf("Sum: %d\n", sum);
    return 0;
}
Sum: 150

この例では、配列numbersの要素を合計しています。

forループを使って、各要素を順に加算し、合計を計算しています。

配列を使ったデータ構造の実装

配列を使って、スタックやキューなどのデータ構造を実装することができます。

以下に、配列を使ったスタックの簡単な実装例を示します。

#include <stdio.h>
#define MAX 5
int stack[MAX];
int top = -1;
// スタックに要素をプッシュ
void push(int value) {
    if (top < MAX - 1) {
        stack[++top] = value;
    } else {
        printf("Stack overflow\n");
    }
}
// スタックから要素をポップ
int pop() {
    if (top >= 0) {
        return stack[top--];
    } else {
        printf("Stack underflow\n");
        return -1;
    }
}
int main() {
    push(10);
    push(20);
    push(30);
    printf("Popped: %d\n", pop());
    printf("Popped: %d\n", pop());
    return 0;
}
Popped: 30
Popped: 20

この例では、配列stackを使ってスタックを実装しています。

push関数でスタックに要素を追加し、pop関数でスタックから要素を取り出しています。

スタックのサイズはMAXで定義され、スタックのオーバーフローとアンダーフローをチェックしています。

まとめ

配列はC言語における基本的なデータ構造であり、効率的なデータ管理と操作を可能にします。

この記事では、配列の基本的な使い方から応用例までを詳しく解説しました。

配列の特性を理解し、適切に活用することで、より効率的なプログラムを作成することができます。

この記事を参考に、実際のプログラミングで配列を活用してみてください。

関連記事

Back to top button