C/C++における配列とポインタの等価性と関数への渡し方

配列とポインタの基本関係

C/C++において、配列名は原則としてその配列の先頭要素を指すポインタとして評価されます。以下の例では、配列名 values が最初の要素のメモリ番地を示していることを示しています。

double values[5] = {10.5, 20.5, 30.5, 40.5, 50.5};
double* ptr = values;

// これらはすべて真となります
if (ptr == &values[0]) {
    // *ptr は values[0] と等価
}

多次元配列の場合、それは「配列を要素とする配列」として扱われます。例えば int grid[3][4] は、4つの整数を持つ配列が3つ並んでいる状態を指します。

int grid[3][4];
int (*rowPtr)[4] = grid; // 4つのintを持つ配列へのポインタ

// ポインタ演算によるアクセス
// grid[i][j] は *(*(grid + i) + j) と等価

注意点として、配列名は定数ポインタのような振る舞いをするため、values++ のように配列名自体の値を変更することはできません。ポインタ変数として宣言されたもののみがインクリメント可能です。

メモリ上の連続性とアクセス方法

多次元配列はメモリ上に一列(行優先)で配置されます。この特性を利用すると、多次元配列をシングルポインタで走査することが可能です。

#include <stdio.h>

int main() {
    int matrix[2][3] = {{1, 2, 3}, {4, 5, 6}};
    int rows = 2, cols = 3;

    // 先頭要素へのポインタを取得
    int *p = &matrix[0][0];

    for (int i = 0; i < rows; i++) {
        for (int j = 0; j < cols; j++) {
            // オフセット計算によるアクセス
            printf("%d ", *(p + i * cols + j));
        }
        printf("\n");
    }
    return 0;
}

関数への配列の受け渡し(1次元)

関数に1次元配列を渡す場合、主に2つのアプローチがあります。

1. ポインタとサイズを渡す

最も一般的な方法です。配列の先頭アドレスと要素数を引数に取ります。

void print_array(const int* arr, int size) {
    for (int i = 0; i < size; i++) {
        printf("%d ", arr[i]);
    }
}

2. 範囲(Begin/End)を渡す

C++の標準ライブラリ(STL)でも多用される、開始ポインタと終了ポインタ(最後の要素の次)を渡す方法です。

void process_range(int* start, int* end) {
    for (int* curr = start; curr < end; curr++) {
        *curr *= 2; 
    }
}

多次元配列を関数で扱う手法

多次元配列を関数に渡す際、列数(2番目以降の次元)が固定されている必要があります。これは、コンパイラがポインタ演算を行う際に1行のサイズを知る必要があるためです。

// 列数が4に固定された関数
void process_matrix(int (*ptr)[4], int rows) {
    int val = ptr[1][2]; // 内部で ptr + (1 * 4) + 2 の計算が行われる
}

動的なサイズの多次元配列を扱う(C言語)

VLA (可変長配列) の利用

C99以降では、引数の順序を工夫することで実行時にサイズが決まる配列を扱えます。

int calculate_sum(int r, int c, int matrix[r][c]) {
    int total = 0;
    for (int i = 0; i < r; i++) {
        for (int j = 0; j < c; j++) {
            total += matrix[i][j];
        }
    }
    return total;
}

ポインタ配列の利用

各要素が行へのポインタとなっている「ポインタの配列」を渡す方法です。

void handle_pointer_array(int* rows_ptrs[], int row_count, int col_count) {
    int val = rows_ptrs[0][0];
}

C++におけるモダンなアプローチ

C++では、生のポインタや配列の代わりに std::vector をネストさせることで、安全かつ柔軟に多次元データを管理できます。

#include <vector>
#include <numeric>

int sum_matrix(const std::vector<std::vector<int>>& matrix) {
    int sum = 0;
    for (const auto& row : matrix) {
        for (int element : row) {
            sum += element;
        }
    }
    return sum;
}

// 使用例
void example() {
    std::vector<std::vector<int>> data = {
        {1, 2},
        {3, 4, 5},
        {6}
    };
    int result = sum_matrix(data);
}

タグ: C C++ pointers arrays MemoryManagement

8月17日 22:37 投稿