C言語でポインタを使って2次元配列を関数に渡す方法

2024-07-27

ポインタの基礎

まず、ポインタの基礎について理解しておきましょう。ポインタは、変数のアドレスを格納する変数です。アドレスとは、メモリ上の特定の場所を指す番号です。

ポインタを使って変数の値を参照するには、* 演算子を使用します。例えば、int x = 10; と宣言し、int *ptr = &x; とすると、ptrx のアドレスを格納します。そして、*ptr とすると、x の値である 10 を取得することができます。

2次元配列とポインタ

2次元配列をポインタを使って渡すには、以下の2つの方法があります。

方法1: 各行の先頭要素へのポインタの配列を渡す

この方法では、まず各行の先頭要素へのポインタを格納する配列を準備します。そして、その配列を関数に渡します。

// 2次元配列の宣言
int array[2][5];

// 各行の先頭要素へのポインタの配列
int *pointers[2];

// 各行の先頭要素へのポインタを格納
pointers[0] = array[0];
pointers[1] = array[1];

// 関数への引数としてポインタの配列を渡す
void function(int **pointers) {
  // ...
}

int main() {
  // ...

  function(pointers);

  // ...

  return 0;
}

方法2: 2次元配列全体を指すポインタを渡す

この方法では、2次元配列全体を指すポインタを関数に渡します。

// 2次元配列の宣言
int array[2][5];

// 2次元配列全体を指すポインタ
int (*ptr)[5] = array;

// 関数への引数として2次元配列全体を指すポインタを渡す
void function(int (*ptr)[5]) {
  // ...
}

int main() {
  // ...

  function(ptr);

  // ...

  return 0;
}

それぞれの方法のメリットとデメリット

方法1

  • メリット:

    • 各行へのポインタを個別に操作できるので、柔軟性が高い
    • 配列のサイズを変更する必要がない
    • コードが冗長になる
    • ポインタの管理が複雑になる

方法2

    • 各行へのポインタを個別に操作できない
    • 配列のサイズを変更する必要がある場合、コードを変更する必要がある



#include <stdio.h>

// 2次元配列の宣言
int array[2][5] = {
  {1, 2, 3, 4, 5},
  {6, 7, 8, 9, 10}
};

// 各行の先頭要素へのポインタの配列
int *pointers[2];

// 各行の先頭要素へのポインタを格納
void init_pointers() {
  pointers[0] = array[0];
  pointers[1] = array[1];
}

// 2次元配列全体を指すポインタ
int (*ptr)[5] = array;

// 各要素を出力する関数
void print_array(int (*ptr)[5], int rows, int columns) {
  for (int i = 0; i < rows; i++) {
    for (int j = 0; j < columns; j++) {
      printf("%d ", ptr[i][j]);
    }
    printf("\n");
  }
}

int main() {
  // 各行の先頭要素へのポインタの配列を初期化
  init_pointers();

  // 各要素を出力
  printf("各行の先頭要素へのポインタの配列を使用:\n");
  print_array(pointers, 2, 5);

  // 2次元配列全体を指すポインタを使用
  printf("2次元配列全体を指すポインタを使用:\n");
  print_array(ptr, 2, 5);

  return 0;
}
  1. print_array() 関数は、各行の先頭要素へのポインタの配列を受け取ります。
  2. print_array() 関数は、2次元配列全体を指すポインタを受け取ります。

どちらの方法でも、2次元配列の各要素を出力することができます。

  • ポインタを使う場合は、メモリリークが発生しないように注意する必要があります。



2次元配列を関数に渡す他の方法

2次元配列を1次元配列として渡す

2次元配列を1次元配列として渡す方法は、最も単純な方法です。ただし、この方法では、2次元配列の構造が失われてしまうため、注意が必要です。

// 2次元配列を1次元配列として渡す
void function(int *array, int rows, int columns) {
  // ...
}

int main() {
  // 2次元配列の宣言
  int array[2][5] = {
    {1, 2, 3, 4, 5},
    {6, 7, 8, 9, 10}
  };

  // 2次元配列を1次元配列として関数に渡す
  function(array, 2, 5);

  return 0;
}

構造体を使う

2次元配列を構造体として渡す方法は、2次元配列の構造を保持することができます。

// 2次元配列を表す構造体
typedef struct {
  int rows;
  int columns;
  int **array;
} Array2D;

// 構造体を関数に渡す
void function(Array2D array) {
  // ...
}

int main() {
  // 2次元配列の宣言
  int array[2][5] = {
    {1, 2, 3, 4, 5},
    {6, 7, 8, 9, 10}
  };

  // 構造体変数を宣言
  Array2D array2d;
  array2d.rows = 2;
  array2d.columns = 5;
  array2d.array = array;

  // 構造体を関数に渡す
  function(array2d);

  return 0;
}

可変長配列を使う

C99以降では、可変長配列を使って2次元配列を関数に渡すことができます。

// 可変長配列を使って2次元配列を関数に渡す
void function(int array[][5], int rows) {
  // ...
}

int main() {
  // 2次元配列の宣言
  int array[2][5] = {
    {1, 2, 3, 4, 5},
    {6, 7, 8, 9, 10}
  };

  // 可変長配列を使って関数に渡す
  function(array, 2);

  return 0;
}

どの方法を使うべきか

どの方法を使うべきかは、状況によって異なります。

  • 2次元配列の構造を保持する必要がない場合は、1番目の方法が最も簡単です。
  • 2次元配列の構造を保持する必要がある場合は、2番目の方法または3番目の方法を使う必要があります。
  • 可変長配列を使用できる場合は、3番目の方法が最も簡潔です。

c pointers multidimensional-array



C/C++ プログラミング:マクロにおける `do-while` と `if-else` ステートメントの謎を解き明かす

この解説では、do-while と if-else ステートメントがマクロでどのように使われ、なぜ一見無意味に見えるコードでも意味を持つのか、詳細に説明します。マクロとCプリプロセッサー:コード展開と処理Cプリプロセッサーは、C/C++ ソースコードをコンパイル前に処理するプログラムです。マクロは、プリプロセッサーによって展開されるテキスト置換規則です。マクロ呼び出しは、マクロ定義内のテキストで置き換えられます。...


C言語における配列の初期化の代替方法

C言語において、配列の全要素を同じ値で初期化する方法にはいくつかの手法があります。初期化リストを用いる方法小さな配列の場合、最も単純な方法は初期化リストを使うことです。この方法では、配列 num のすべての要素が値 1 で初期化されます。メモリセット関数 memset を用いる方法...


C++とCにおけるmain()関数の戻り値の具体的な例

C++とCにおいて、main()関数の戻り値は通常、int型です。これは、プログラムの実行が正常に終了した場合は0、エラーが発生した場合は非ゼロの値を返すことを示します。0: プログラムが正常に終了しました。非ゼロの値: プログラムがエラーで終了しました。この値は、エラーの種類や重さを示すことができます。例えば、1は一般的なエラー、2はファイルが見つからないエラー、3はメモリ不足エラーなどを表すことができます。...


C言語での定数文字列/リテラル文字列の連結についてのコード例解説

定数文字列の連結定数文字列を連結するには、単純に文字列を並べて記述します。コンパイラが自動的に連結して一つの文字列として扱います。上記のコードでは、str1とstr2を連結してstr3に代入しています。str3には"Hello world"という文字列が格納されます。...


コードレビューの鬼になる! `a[5] == 5[a]` を見逃さないためのチェックポイント

解説:この式は、配列とポインタの仕組みを理解する上で重要なポイントです。配列とポインタの関係C言語において、配列はポインタの連続体として表現されます。配列名: 配列全体の先頭アドレスを表すポインタa[i]: 配列の i 番目の要素へのポインタ (アドレス計算によって算出)...



c pointers multidimensional array

++i と i++ の違い: C言語におけるインクリメントと for ループ

C言語において、++i と i++ はどちらも変数 i の値を 1 増やすインクリメント演算子ですが、そのタイミングが異なります。++i は、式の評価前に i の値を 1 増やします。つまり、++i 自体の値はインクリメント後の i の値になります。


C言語で配列のサイズを調べる方法:コード例と解説

C言語では、配列の要素数を直接取得する機能はありません。しかし、sizeof 演算子を用いて、配列のサイズ(バイト数)を計算し、要素数を求めることができます。基本的な方法配列の総バイト数を求める:int array[5] = {1, 2, 3, 4, 5}; size_t array_size_bytes = sizeof(array); // 配列全体のバイト数


C/C++ ビット操作入門: 単一ビットの設定、クリア、トグルの代替方法

C++とCでは、ビットレベルでの操作を行うことができます。これは、低レベルなシステムプログラミングや、効率的なデータ処理において重要です。ビット演算子& : AND| : OR~ : NOT<< : 左シフト>> : 右シフトビット位置は、通常0から始まり、右から左にインデックスされます。


C言語のユニットテストにおけるサンプルコード解説

ユニットテストとは、ソフトウェア開発において、プログラムの最小単位である「ユニット」に対して行うテストのことです。C言語では、関数やモジュールがユニットとみなされます。ユニットテストでは、各ユニットが期待通りの動作をするかどうかを検証します。


C++/Cにおける構造体のsizeofとメンバーの和の関係について

日本語解説C++やC言語において、構造体のsizeofは、その構造体内の各メンバーのsizeofの合計と必ずしも一致しません。これは、構造体のメモリレイアウトやパディングによる影響です。メモリアライメント: 多くのプロセッサは、特定のデータ型を特定のアドレス境界に配置することを要求します。例えば、4バイトの整数型は通常4バイト境界に配置されます。