最初にメモリ レイアウトを見てみましょう。
char arr[5][7][6];
これarr
は配列の配列の配列です。
次のようにメモリに配置されます。
+--------------+
| | arr[0]
+--------------+
| | arr[1]
+--------------+
| | arr[2]
+--------------+
| | arr[3]
+--------------+
etc.
の配列arr[i]
の配列ですchar
。
のようa[i]
になります
+--------------+ a[i][0]
| |
+--------------+
| | a[i][1]
+--------------+
| | a[i][2]
+--------------+
| | a[i][3]
+--------------+
etc
今arr[i][j]
は の配列ですchars
。
そう
+--------------+
| | arr[i][j][0]
+--------------+
| | arr[i][j][1]
+--------------+
| | arr[i][j][2]
+--------------+
etc.
コンパイラは、最初のブロックの名前arr
とアドレスを使用してシンボル テーブルにエントリを作成し、合計サイズ (5*6*7 = 210 バイト) も追跡します。
これで式
printf("%d\n", (&arr + 1) - &arr);
ポインター演算です。したがって、すべてのシンボルの型にバインドされます。
これを実際に見てみましょう。
(gdb) p &arr
$1 = (char (*)[5][7][6]) 0x7fffffffe160
arr
is ポインターの型が表示されますchar (*)[5][6][7]
つまり、配列の配列の配列へのポインターです。ポインター演算は、実際には、ポインターが指している型に関連しています。そのため、型のサイズが重要です。
(gdb) p sizeof(char [5][7][6])
$2 = 210
したがって、型へのポインターはchar [5][6][7]
次のようにインクリメントまたはデクリメントされます。
&arr+1 => 0x7fffffffe160 +0xd2 => 0x7fffffffe232
と(&arr + 1) - &arr => 0x7fffffffe232 - 0x7fffffffe160=> 0xd2
これで、実際には0xd2が返されます。しかし、ポインター演算の場合、それは1*sizeof(char [5][7][6]) => 1
ポインター演算は、number of sizeof(type)
実際のバイト オフセットではなく を返します。
あなたは正しい結果を得ています
printf("%d\n", (char *)(&arr + 1) - (char *)&arr);
キャストのため、char ポインターにしています。したがって、ポインター演算sizeof(char)
では、1 バイト サイズの単位が使用されます。したがって、出力は 210 になります。
printf("%d\n", (unsigned)(arr + 1) - (unsigned)arr);
ここarr
で、式は型に減衰しchar (*)[7][6]
ます。つまり、2 次元配列へのポインターです。指している型のサイズは 6*7 = 42 です。それが結果として得られるものです。
printf("%d\n", (符号なし)(p + 1) - (符号なし)p);
ここでp+1 - p
は、ポインタ演算になります1 * sizeof(char(*)[5][6][7])
。したがって、ポインター演算では 1 を返す必要があります。ただし、の結果を unsigned にキャストしたため、ポインター演算を放棄し、整数演算を使用します。整数演算を使用しているため、大きな数値のポインター値が処理されるため、実際のバイト オフセットが取得されます。