はじめに
ポインタ演算は、C 言語における強力な機能で、値を加算または減算することでメモリアドレスを操作することができます。ポインタ演算の最も一般的な用途の 1 つは、配列の走査です。ポインタを使用することで、配列を前方および後方の両方向に効率的に走査することができます。
この実験では、配列の作成と初期化方法、配列要素にアクセスするためのポインタの設定方法、およびポインタ演算を使用した配列の走査方法を学習します。この技術は、C 言語プログラミングの基礎であり、多くの高度なデータ操作演算の基礎を形成しています。
基本的な C プログラムの作成
まず、VSCode エディタで新しい C ファイルを作成しましょう。このファイルには、ポインタを使用した配列の走査のためのメインプログラムが含まれます。
WebIDE で、左側のエクスプローラーパネルを見つけ、
~/projectディレクトリに移動します。projectフォルダを右クリックし、「新しいファイル」を選択します。ファイル名をmain.cとします。次の基本的な C プログラム構造をファイルにコピーします。
#include <stdio.h>
int main() {
printf("Array Traversal using Pointers\n");
return 0;
}
Ctrl+S を押すか、メニューから「ファイル」>「保存」を選択して、ファイルを保存します。
すべてが正しく設定されていることを確認するために、このプログラムをコンパイルして実行しましょう。メニューから「ターミナル」>「新しいターミナル」を選択して WebIDE でターミナルを開き、次のコマンドを実行します。
cd ~/project
gcc main.c -o main
./main
次の出力が表示されるはずです。
Array Traversal using Pointers
これにより、C 開発環境が正常に動作していることが確認できます。次の手順では、このプログラムを配列とポインタを使用するように変更します。
配列とポインタの宣言と初期化
このステップでは、配列とポインタを宣言する方法を学びます。これらは、ポインタを使用した配列の走査における基本的な要素です。
配列とポインタの理解
C 言語における配列は、連続したメモリ領域に格納された同じ型の要素の集合です。たとえば、5 つの要素を持つ整数配列は、メモリ上に 5 つの整数分の領域を連続して確保します。
ポインタは、別の変数のメモリアドレスを格納する変数です。ポインタを使用することで、特定のメモリアドレスに格納された値に間接的にアクセスすることができます。
main.cファイルを変更して、配列とポインタを追加しましょう。
#include <stdio.h>
int main() {
printf("Array Traversal using Pointers\n\n");
// Declare and initialize an array of 5 integers
int arr[5] = {10, 20, 30, 40, 50};
// Declare a pointer of integer type
int *ptr;
// Print the array elements using array notation
printf("Array elements using array notation:\n");
for(int i = 0; i < 5; i++) {
printf("arr[%d] = %d\n", i, arr[i]);
}
return 0;
}
このコードでは、以下のことを行っています。
- 5 つの要素を持つ整数配列
arrを宣言し、10、20、30、40、50 の値で初期化しました。 - 整数型のポインタ
ptrを宣言しました。これは後で配列要素を指すために使用されます。 - 従来の配列表記を使用して配列要素を出力しました。
プログラムをコンパイルして実行し、配列要素を確認しましょう。
gcc main.c -o main
./main
次の出力が表示されるはずです。
Array Traversal using Pointers
Array elements using array notation:
arr[0] = 10
arr[1] = 20
arr[2] = 30
arr[3] = 40
arr[4] = 50
次のステップでは、ポインタを配列に関連付け、ポインタを使用して配列要素にアクセスします。
ポインタと配列のリンクと前方走査
このステップでは、ポインタと配列を関連付け、その後ポインタを使って配列を前方に走査します。
ポインタを配列に関連付ける
C 言語では、インデックスが付いていない配列名は、配列の最初の要素のアドレスを表します。これは、このアドレスを直接ポインタ変数に代入できることを意味します。
main.cファイルを変更して、ポインタを配列に関連付け、配列を走査しましょう。
#include <stdio.h>
int main() {
printf("Array Traversal using Pointers\n\n");
// Declare and initialize an array of 5 integers
int arr[5] = {10, 20, 30, 40, 50};
// Declare a pointer of integer type
int *ptr;
// Assign the address of the first element of the array to the pointer
ptr = arr; // This is equivalent to ptr = &arr[0]
// Print the array elements using array notation
printf("Array elements using array notation:\n");
for(int i = 0; i < 5; i++) {
printf("arr[%d] = %d\n", i, arr[i]);
}
// Print the array elements using pointer notation
printf("\nArray elements using pointer notation (forward traversal):\n");
for(int i = 0; i < 5; i++) {
printf("*(ptr + %d) = %d\n", i, *(ptr + i));
}
return 0;
}
この更新されたコードでは、以下のことを行っています。
- 配列
arrの最初の要素のアドレスをポインタptrに代入しました。 - ポインタ演算を使って配列を走査する新しいループを追加しました。
- 式
*(ptr + i)は、メモリ位置ptr + iにある値にアクセスします。iが 0 のときは配列の最初の要素、iが 1 のときは 2 番目の要素、といった具合です。
プログラムをコンパイルして実行し、結果を確認しましょう。
gcc main.c -o main
./main
次の出力が表示されるはずです。
Array Traversal using Pointers
Array elements using array notation:
arr[0] = 10
arr[1] = 20
arr[2] = 30
arr[3] = 40
arr[4] = 50
Array elements using pointer notation (forward traversal):
*(ptr + 0) = 10
*(ptr + 1) = 20
*(ptr + 2) = 30
*(ptr + 3) = 40
*(ptr + 4) = 50
両方の方法で同じ出力が得られることに注意してください。これは、ポインタ演算を使って、従来の配列インデックスと同じように配列要素にアクセスできることを示しています。
配列走査のためのポインタのインクリメントの実装
前のステップでは、式*(ptr + i)を使って配列要素にアクセスしました。この方法は問題なく動作しますが、C 言語ではポインタを使って配列を走査するより簡潔な方法、つまりインクリメント演算子 (++) が用意されています。
ポインタをインクリメントすると、それが指すデータ型のサイズに基づいて次のメモリ位置に移動します。整数型のポインタをインクリメントすると、メモリ上の次の整数に移動します。
main.cファイルを変更して、ポインタのインクリメントを使って配列を走査しましょう。
#include <stdio.h>
int main() {
printf("Array Traversal using Pointers\n\n");
// Declare and initialize an array of 5 integers
int arr[5] = {10, 20, 30, 40, 50};
// Declare and initialize a pointer to the first element of the array
int *ptr = arr; // Same as ptr = &arr[0]
// Print the array elements using pointer incrementation
printf("Array elements using pointer incrementation:\n");
for(int i = 0; i < 5; i++) {
printf("*ptr = %d\n", *ptr);
ptr++; // Move the pointer to the next element
}
// Reset the pointer to the beginning of the array
ptr = arr;
// Print all elements in a single line using pointer incrementation
printf("\nAll elements in a single line: ");
for(int i = 0; i < 5; i++) {
printf("%d ", *ptr++); // Print and then increment
}
printf("\n");
return 0;
}
この更新されたコードでは、以下のことを行っています。
- ポインタ
ptrを宣言すると同時に直接初期化します。 - 最初のループ内では、
*ptrを使って現在の要素にアクセスし、その後ptr++を使って次の要素に移動します。 - 最初のループの後、
ptrを配列の先頭を指すように再設定します。 - 2 番目のループでは、後置インクリメント演算子
*ptr++を使います。これはまずptrの現在の値を使用し、その後でインクリメントします。
プログラムをコンパイルして実行し、結果を確認しましょう。
gcc main.c -o main
./main
次の出力が表示されるはずです。
Array Traversal using Pointers
Array elements using pointer incrementation:
*ptr = 10
*ptr = 20
*ptr = 30
*ptr = 40
*ptr = 50
All elements in a single line: 10 20 30 40 50
これは、ポインタのインクリメントを使って配列を走査する方法を示しています。重要な点は、ptr++が次の要素に移動する際に自動的にデータ型のサイズを考慮することです。
ポインタのデクリメントを使用した逆方向走査の実装
前のステップでは、配列を前方に走査しました。今度は、ポインタのデクリメントを使って配列を逆方向に走査する方法を学びましょう。
逆方向走査を行うには、以下のことが必要です。
- ポインタを配列の最後の要素を指すように初期化する
- ポインタをデクリメントして配列を逆方向に移動する
main.cファイルを変更して、逆方向走査を実装しましょう。
#include <stdio.h>
int main() {
printf("Array Traversal using Pointers\n\n");
// Declare and initialize an array of 5 integers
int arr[5] = {10, 20, 30, 40, 50};
// Forward traversal using pointer increment
int *ptr = arr;
printf("Forward traversal using pointer increment:\n");
for(int i = 0; i < 5; i++) {
printf("%d ", *ptr);
ptr++;
}
printf("\n\n");
// Backward traversal using pointer decrement
// Point to the last element of the array
ptr = &arr[4]; // or ptr = arr + 4
printf("Backward traversal using pointer decrement:\n");
for(int i = 0; i < 5; i++) {
printf("%d ", *ptr);
ptr--; // Move the pointer to the previous element
}
printf("\n\n");
// Alternative approach: Start from the last element and decrement in the loop condition
printf("Alternative backward traversal approach:\n");
for(ptr = &arr[4]; ptr >= arr; ptr--) {
printf("%d ", *ptr);
}
printf("\n");
return 0;
}
この更新されたコードでは、以下のことを行っています。
- まず、ポインタのインクリメントを使って前方走査を行います。
- 逆方向走査のために、
ptr = &arr[4]を使ってポインタを配列の最後の要素を指すように設定します。 - ループ内では、現在の要素を出力し、その後
ptr--を使ってポインタをデクリメントします。 - デクリメントを
forループの更新文の一部とする代替方法も示しています。
プログラムをコンパイルして実行し、結果を確認しましょう。
gcc main.c -o main
./main
次の出力が表示されるはずです。
Array Traversal using Pointers
Forward traversal using pointer increment:
10 20 30 40 50
Backward traversal using pointer decrement:
50 40 30 20 10
Alternative backward traversal approach:
50 40 30 20 10
これは、ポインタ演算を使って配列を前方と逆方向の両方に走査する方法を示しています。ポインタをインクリメントおよびデクリメントできる機能により、配列を任意の方向に簡単に移動できます。
まとめ
この実験では、C 言語でポインタを使って配列を走査する方法を学びました。以下に、カバーした主要な概念をまとめます。
配列とポインタの基本:
- C 言語の配列は、要素を連続したメモリ位置に格納します。
- ポインタはメモリアドレスを格納し、それらの位置にアクセスするために使用できます。
ポインタと配列の関係:
- 配列名(インデックスなし)は、最初の要素のアドレスを表します。
- このアドレスをポインタに割り当てることで、配列との接続を確立できます。
前方走査の手法:
- ポインタ演算を使用する:
*(ptr + i) - ポインタのインクリメントを使用する:
*ptrの後にptr++ - 参照外しとインクリメントを組み合わせる:
*ptr++
- ポインタ演算を使用する:
逆方向走査の手法:
- ポインタを最後の要素に初期化する:
ptr = &arr[size-1] - ポインタのデクリメントを使用する:
ptr--で逆方向に移動する - ループ条件で、ポインタが配列の先頭に到達したかどうかをチェックできます。
- ポインタを最後の要素に初期化する:
ポインタ演算は C 言語の強力な機能で、効率的なメモリ操作を可能にし、配列や他のデータ構造を扱う際に柔軟性を提供します。この手法は、動的メモリ割り当て、連結リスト、その他の複雑なデータ構造など、より高度なプログラミング概念の基礎を形成します。



