9


2

互換性のないポインター型

私は次の署名を持つ関数を持っています:

void box_sort(int**, int, int)

および次のタイプの変数:

int boxes[MAX_BOXES][MAX_DIMENSIONALITY+1]

関数を呼び出しているとき

box_sort(boxes, a, b)

GCCは2つの警告を表示します:

103.c:79: warning: passing argument 1 of ‘box_sort’ from incompatible pointer type (string where i am calling the function)
103.c:42: note: expected ‘int **’ but argument is of type ‘int (*)[11] (string where the function is defined)

質問は_なぜですか? int x [] []とint ** x(および実際にはint * x [])がCで同じ型ではないかどうか?

4 回答


13


数日前にほぼ同じような質問があったことは知っています…​ しかし今それを見つけることができません。

答えは、 int [size] [](最後の注を参照)と `int **`は間違いなく同じ型です。 `int []`と `int *`は、多くの場合、特にこのような場合に交換可能に使用できます。これは、関数に渡すときに配列が最初の要素へのポインターに減衰するためです。 しかし、2次元配列の場合、これらは非常に異なる保存方法です。

2x2配列のメモリ内での表示は次のとおりです。

int a[2][2]:

__a[0][0]__|__a[0][1]__|__a[1][0]__|__a[1][1]__
  (int)       (int)       (int)       (int)

int **a (e.g. dynamically allocated with nested mallocs)

__a__
(int**)
  |
  v
__a[0]__|__a[1]__
  (int*)  (int*)
    |        |
    |        |
    v        ------------------>
__a[0][0]__|__a[0][1]__        __a[1][0]__|__a[1][1]__
  (int)       (int)              (int)       (int)

次のように2番目のものを作成できます。

int **a = malloc(2 * sizeof(int*));
a[0] = malloc(2 * sizeof(int));
a[1] = malloc(2 * sizeof(int));

注:他の人が指摘したように、 `int [] []`は実際の型ではありません。指定できないサイズは1つだけです。 しかし、ここでの質問の核心は、2次元配列とダブルポインターが同じものかどうかです。


1


署名が必要とするため、ポインターの配列を作成したことはありません。

Cで2D配列を行うには2つの方法があります。 ある場合には、あなたは多くのものを持っているだけで、コンパイラは次元が何であるかを知らされます。 行インデックスに列数を掛けて行の先頭を計算し、列インデックスを追加してその行内の要素を見つけます。

もう1つの方法は、コンパイラーが行の先頭を見つけるためにベクターを逆参照するだけのポインターのベクターを使用する方法です。

実際のオブジェクトは第1種の1つですが、関数プロトタイプは第2種を要求しています。

そのため、オブジェクトに一致するようにプロトタイプを変更するか、関数に渡す行ポインターのベクトルを構築する必要があります。


1


Cには `int [] []`のような型はありません。多次元配列の最初の部分のみを指定できません。 したがって、 `int [] [5]`は大丈夫です。

ここに投稿された他の回答に加えて、C99を使用できる場合は、変数配列を使用して目的を達成できます。

void box_sort(int N, int M, int x[M][N]);

これは、MicrosoftのVisual C ++を除くほとんどのプラットフォームで機能します。


0


配列式がほとんどのコンテキストで表示される場合、その型は暗黙的に「TのN要素配列」から「Tへのポインタ」に変換され、その値は配列の最初の要素のアドレスに設定されます。 この規則の例外は、配列式が sizeof`またはaddress-of(& `)演算子のオペランドである場合、または配列式が宣言内の別の配列を初期化するために使用される文字列リテラルである場合です。

コードのコンテキストでこれが意味することは、 box_sort`の呼び出しで、式 boxes`の型が暗黙的に M-element array of N-element array of int`から pointer to N- int`の要素配列、または `int(*)[MAX_DIMENSIONALITY + 1]`であるため、関数は次のようなパラメータタイプを想定する必要があります。

void box_sort(int (*arr)[MAX_DIMENSIONALITY+1], int x, int y)
{
   ...
}

int * a`と int a [] は関数パラメーター宣言で同義であるため、 int(* a)[N] int a [] [N] `と同義です。上記を

void box_sort(int arr[][MAX_DIMENSIONALITY+1], int x, int y)
{
}

私は個人的にポインター表記を好んでいますが、それは何が起こっているかをより正確に反映しているからです。 関数では、通常どおり「arr」に添字を付けることに注意してください。

arr[x][y] = ...;

式 `arr [x]`は `*(arr + x)`と同等であるため、ポインターは暗黙的に間接参照されます。

box_sortを任意のサイズの配列(つまり、2番目の次元が必ずしもMAX_DIMENSIONALITY + 1であるとは限らない配列)で動作させたい場合、1つのアプローチは次のようにすることです:

int boxes[X][Y];
...
box_sort (&boxes[0], X, Y, x, y);
...
void box_sort(int *arr, size_t rows, size_t cols, int x, int y)
{
  ...
  arr[x*cols + y] = ...;
}

基本的に、 `boxes`はintの1次元配列として扱い、オフセットを手動で計算します。