[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バイトのメモリを占有します。
以下に、配列のメモリ配置のイメージを示します。
インデックス | メモリアドレス | 値 |
---|---|---|
0 | 0x1000 | 1 |
1 | 0x1004 | 2 |
2 | 0x1008 | 3 |
3 | 0x100C | 4 |
4 | 0x1010 | 5 |
この表は、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次元配列のメモリ配置のイメージを示します。
行 | 列 | メモリアドレス | 値 |
---|---|---|---|
0 | 0 | 0x1000 | 1 |
0 | 1 | 0x1004 | 2 |
0 | 2 | 0x1008 | 3 |
1 | 0 | 0x100C | 4 |
1 | 1 | 0x1010 | 5 |
1 | 2 | 0x1014 | 6 |
2 | 0 | 0x1018 | 7 |
2 | 1 | 0x101C | 8 |
2 | 2 | 0x1020 | 9 |
この表は、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言語における基本的なデータ構造であり、効率的なデータ管理と操作を可能にします。
この記事では、配列の基本的な使い方から応用例までを詳しく解説しました。
配列の特性を理解し、適切に活用することで、より効率的なプログラムを作成することができます。
この記事を参考に、実際のプログラミングで配列を活用してみてください。