207

C では、次のコードを使用してヒープに 2 次元配列を動的に割り当てることができることを知っています。

int** someNumbers = malloc(arrayRows*sizeof(int*));

for (i = 0; i < arrayRows; i++) {
    someNumbers[i] = malloc(arrayColumns*sizeof(int));
}

明らかに、これは実際には一連の個別の 1 次元整数配列へのポインターの 1 次元配列を作成し、「システム」は私が要求したときに私が何を意味するかを理解できます。

someNumbers[4][2];

しかし、次の行のように、2D 配列を静的に宣言すると...:

int someNumbers[ARRAY_ROWS][ARRAY_COLUMNS];

...同様の構造がスタック上に作成されますか、それとも完全に別の形式ですか? (つまり、ポインタの 1D 配列ですか?そうでない場合、それは何ですか?また、それへの参照はどのように理解されますか?)

また、私が「システム」と言ったとき、それを理解するのに実際に責任があるのは何ですか? カーネル?それとも、C コンパイラはコンパイル中にそれを整理しますか?

4

6 に答える 6

160

静的な 2 次元配列は、配列の配列のように見えます。メモリ内で連続して配置されているだけです。配列はポインターと同じではありませんが、ほとんど同じように使用できることが多いため、混乱することがあります。ただし、コンパイラは適切に追跡するため、すべてが適切に整列されます。あなたが言及したように、静的な 2D 配列には注意する必要がありint **ます。パラメーターを受け取る関数に 1 つを渡そうとすると、悪いことが起こるからです。簡単な例を次に示します。

int array1[3][2] = {{0, 1}, {2, 3}, {4, 5}};

メモリ内では次のようになります。

0 1 2 3 4 5

以下とまったく同じ:

int array2[6] = { 0, 1, 2, 3, 4, 5 };

array1しかし、この関数に渡そうとすると:

void function1(int **a);

警告が表示されます (アプリは配列に正しくアクセスできません)。

warning: passing argument 1 of ‘function1’ from incompatible pointer type

2D 配列は と同じではないためint **です。配列のポインターへの自動減衰は、いわば「1レベルの深さ」にすぎません。関数を次のように宣言する必要があります。

void function2(int a[][2]);

また

void function2(int a[3][2]);

すべてを幸せにするために。

これと同じ概念がn次元配列にも適用されます。ただし、アプリケーションでこの種の面白いビジネスを利用すると、通常、理解が難しくなるだけです。だからそこに注意してください。

于 2010-04-02T04:49:58.120 に答える
88

答えは、C には実際には2D 配列がなく、配列の配列があるという考えに基づいています。これを宣言すると:

int someNumbers[4][2];

someNumbersその配列の各要素が型int [2](それ自体が 2 の配列) である、4 つの要素の配列であることを要求していますint

パズルのもう 1 つの部分は、配列が常にメモリ内で連続して配置されることです。あなたが求める場合:

sometype_t array[4];

それは常に次のようになります。

| sometype_t | sometype_t | sometype_t | sometype_t |

(sometype_t間にスペースを入れずに 4 つのオブジェクトを並べて配置)。したがって、someNumbers配列の配列では、次のようになります。

| int [2]    | int [2]    | int [2]    | int [2]    |

そして、各int [2]要素はそれ自体が配列であり、次のようになります。

| int        | int        |

全体として、次のようになります。

| int | int  | int | int  | int | int  | int | int  |
于 2010-04-02T06:24:30.483 に答える
29
unsigned char MultiArray[5][2]={{0,1},{2,3},{4,5},{6,7},{8,9}};

メモリ内は次のようになります。

unsigned char SingleArray[10]={0,1,2,3,4,5,6,7,8,9};
于 2013-02-01T13:02:18.847 に答える
5

また、あなたへの答えとして:コンパイラーがほとんどの面倒な作業を行っていますが、両方。

静的に割り当てられた配列の場合、「システム」がコンパイラになります。スタック変数の場合と同様に、メモリを予約します。

malloc された配列の場合、"The System" が malloc の実装者 (通常はカーネル) になります。コンパイラが割り当てるのは、ベース ポインターだけです。

コンパイラは、交換可能な使用法を理解できる Carl の例を除いて、宣言されているものとして常に型を処理します。これが、[][] を関数に渡す場合、それが静的に割り当てられたフラットであると想定する必要がある理由です。ここで、** はポインタへのポインタであると想定されます。

于 2010-04-02T05:13:27.790 に答える
1

特定の 2D 配列にアクセスするには、次のコードに示すように、配列宣言のメモリ マップを検討してください。

    0  1
a[0]0  1
a[1]2  3

各要素にアクセスするには、関心のある配列をパラメーターとして関数に渡すだけで十分です。次に、列のオフセットを使用して、各要素に個別にアクセスします。

int a[2][2] ={{0,1},{2,3}};

void f1(int *ptr);

void f1(int *ptr)
{
    int a=0;
    int b=0;
    a=ptr[0];
    b=ptr[1];
    printf("%d\n",a);
    printf("%d\n",b);
}

int main()
{
   f1(a[0]);
   f1(a[1]);
    return 0;
}
于 2016-02-09T12:51:13.210 に答える