C言語

【C言語】第3章第3回:配列とメモリ:配列の仕組みを理解する

配列のデータはメモリ上に連続して格納されます。この章では、配列のメモリ構造や仕組みを詳しく解説し、メモリ操作の基本を学びます。

1. 配列とメモリの基本構造

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

例:配列のメモリアドレスを表示するプログラム

#include <stdio.h>

int main() {
    int numbers[5] = {10, 20, 30, 40, 50};

    for (int i = 0; i < 5; i++) {
        printf("Address of numbers[%d]: %p\n", i, (void*)&numbers[i]);
    }

    return 0;
}

解説:

  • &numbers[i]:配列の各要素のメモリアドレスを取得。
  • 配列のアドレスは要素ごとに固定の間隔(データ型のサイズ)で増加します。

2. 配列の先頭アドレスとポインタ

配列の名前は、配列の先頭要素のアドレスを指すポインタとして扱われます。

例:配列名とポインタの比較

#include <stdio.h>

int main() {
    int numbers[5] = {10, 20, 30, 40, 50};

    printf("Address of numbers: %p\n", (void*)numbers);
    printf("Address of numbers[0]: %p\n", (void*)&numbers[0]);

    return 0;
}

解説:

  • numbers:配列の先頭アドレスを指します。
  • &numbers[0]:配列の最初の要素のアドレスで、numbersと同じ値を持ちます。

3. 配列とメモリの連続性

配列の要素はメモリ上で連続して格納されるため、次の要素のアドレスは前の要素のアドレスにデータ型のサイズを加えたものになります。

例:要素間のアドレス差を確認するプログラム

#include <stdio.h>

int main() {
    int numbers[5] = {10, 20, 30, 40, 50};

    for (int i = 0; i < 4; i++) {
        printf("Difference between numbers[%d] and numbers[%d]: %ld bytes\n",
               i + 1, i, (char*)&numbers[i + 1] - (char*)&numbers[i]);
    }

    return 0;
}

解説:

  • アドレス差:整数型intの場合、各要素間のアドレス差はsizeof(int)(通常4バイト)。
  • (char*)&numbers[i]:キャストしてポインタ演算をバイト単位で処理。

4. 配列とポインタの関係

配列の要素にはポインタ演算を使ってアクセスすることも可能です。

例:ポインタ演算で配列にアクセス

#include <stdio.h>

int main() {
    int numbers[5] = {10, 20, 30, 40, 50};

    for (int i = 0; i < 5; i++) {
        printf("numbers[%d] = %d (using pointer: %d)\n", i, numbers[i], *(numbers + i));
    }

    return 0;
}

解説:

  • *(numbers + i):ポインタ演算を使って配列の要素にアクセス。
  • numbers[i]と同じ値を取得。

5. 配列を扱う際の注意点

  • インデックス範囲外アクセス:範囲外アクセスは未定義動作の原因となります。
  • ポインタの不適切な操作:誤ったポインタ演算は予期しないエラーを引き起こす可能性があります。

6. 練習問題

以下の課題に挑戦して、配列のメモリ構造を理解しましょう。

  1. 配列の各要素のアドレスを表示し、その間隔を確認するプログラムを作成してください。
  2. ポインタ演算を使って配列のすべての要素を表示するプログラムを作成してください。
  3. 配列の先頭要素と最後の要素のアドレス差を計算するプログラムを作成してください。

7. 練習問題の解答と解説

問1の解答

#include <stdio.h>

int main() {
    int numbers[5] = {10, 20, 30, 40, 50};

    for (int i = 0; i < 5; i++) {
        printf("Address of numbers[%d]: %p\n", i, (void*)&numbers[i]);
    }

    return 0;
}

問2の解答

#include <stdio.h>

int main() {
    int numbers[5] = {10, 20, 30, 40, 50};

    for (int i = 0; i < 5; i++) {
        printf("numbers[%d] = %d\n", i, *(numbers + i));
    }

    return 0;
}

問3の解答

#include <stdio.h>

int main() {
    int numbers[5] = {10, 20, 30, 40, 50};

    long difference = (char*)&numbers[4] - (char*)&numbers[0];
    printf("Difference between first and last elements: %ld bytes\n", difference);

    return 0;
}

8. まとめ

配列のメモリ構造とポインタの関係を理解することで、C言語での効率的なデータ操作が可能になります。今回学んだ内容を活用して、より高度なプログラムに挑戦してみましょう。