多次元配列はメモリ内でどのようにフォーマットされますか?
-
23-09-2019 - |
質問
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 コンパイラがコンパイル中にそれを分類しますか?
解決
静的 2 次元配列は配列の配列のように見えます。メモリ内に連続して配置されているだけです。配列はポインターと同じものではありませんが、ほとんど同じ意味で使用できるため、混乱を招く場合があります。ただし、コンパイラーは適切に追跡するため、すべてが適切に整列します。あなたが言及したような静的2D配列には注意する必要があります。静的2D配列を関数に渡そうとすると、 int **
パラメータを設定すると、悪いことが起こります。簡単な例を次に示します。
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-次元配列。ただし、この種の面白いビジネスをアプリケーションで利用しても、通常は理解しにくくなるだけです。だから外では気をつけてね。
他のヒント
答えはCが本当にのHAVE の2次元配列しないという考えに基づいています。
:あなたはこれを宣言する場合int someNumbers[4][2];
は、その配列の各要素は(2 someNumbers
sの配列そのものである)型int [2]
である4つの要素の配列であることがint
を求めています。
パズルの他の部分は、アレイが常にメモリ内に連続して配置されることです。あなたが求める場合:
sometype_t array[4];
そして、それは常に次のようになります。
| sometype_t | sometype_t | sometype_t | sometype_t |
(4 sometype_t
は、間にスペースなしで、互いに隣にレイアウトオブジェクト)。だからあなたのsomeNumbers
、アレイのサブアレイで、それは次のようになります:
| int [2] | int [2] | int [2] | int [2] |
また、各int [2]
要素は、次のようになりますことを、配列そのものであります
| int | int |
だから、全体的に、あなたがこれを取得ます:
| int | int | int | int | int | int | int | int |
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};
:両方、しかしコンパイラは重い物を持ち上げるのほとんどを行っている。
静的に割り当てられたアレイの場合、「システム」とは、コンパイラであろう。それは任意のスタック変数の場合と同様に、それはメモリを確保します。
はmallocさアレイの場合、「システム」とは、malloc関数の実装(通常カーネル)であろう。すべてのコンパイラが割り当てられますベースポインタです。
コンパイラは、常に彼らはそれが交換可能な使用状況を把握することができますどこカールを与えた例を除いてであると宣言しているものとしてタイプを処理しようとしています。あなたが関数に[] []を渡した場合、それはそれは**ポインタへのポインタであると想定される静的に割り当てられた平らであることを前提としなければならない理由です。
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;
}