宣言:
int b[2] = {1, 2};
int
値1
,を持つ2 つの配列を作成します2
。
int のシステム サイズが 4 バイトであると仮定すると、配列b[]
は次のようにメモリに格納する必要があります。
first ele +----------+
(b + 0) ---►| 1 | 0xbf5c787c <----- &b , (c + 0)
next ele +----------+
(b + 1) ---►| 2 | 0xbf5c7880 <------------- (c + 1)
+----------+
(b + 2) ---►| ? | 0xbf5c7884 <----- (&b + 1) next array
+----------+
---►| ? | 0xbf5c7888
+----------+
---►| ? | 0xbf5c788c <----- (&b + 2) next array
+----------+
---►| ? | 0xbf5c7890
+----------+
? means garbage value
b[] array in memory from 0xbf5c787c to 0xbf5c7880
each cell is four bytes
上の図では、値を持つメモリセル?
はガベージ値を意味し、割り当てられていません (メモリからのメモリ0xbf5c7884
は配列に割り当てられていません)。値は1
、2
メモリ内のアドレス0xbf5c787c
および0xbf5c7880
に格納され、配列 に割り当てられb[]
ます。
値を出力する代わりに、(c + i)
andを使用してコードでアクセスするメモリのアドレスを出力します。(&b + i)
このために、次のプログラムを検討 してください。
#include<stdio.h>
int main(){
int b[2] = {1, 2};
int i = 0;
int *c = &b; //Give warning: "assignment from incompatible pointer type"
printf("\n C address: "); // outputs correct values
for (i = 0; i < 2; i++) {
printf("%p ", (void*)(c + i));
}
printf("\n B address: "); // outputs incorrect values/ and behaving differently
for (i = 0; i < 2; i++) {
printf("%p ", (void*)(&b + i)); // Undefined behavior
}
return 1;
}
出力:
C address: 0xbf5c787c 0xbf5c7880
B address: 0xbf5c787c 0xbf5c7884
このコードが機能していることを確認してください @ Codepade
通知、(c + i)
セルの正しいアドレスを value1
で2
出力するため、最初のコードの出力は正しいです。配列に割り当てられていない(配列の外側にある)(&b + i)
アドレス値を出力しますが、このメモリにアクセスすると、実行時に未定義の動作(予測不可能)が発生します。b[]
b[]
実際には と の間に違いがb
あり&b
ます。
b
は配列であり、その型はint[2]
であり、ほとんどの式で最初の要素のアドレスとしてb
崩壊しint*
ます (読み取り:配列名が最初の要素へのポインタに崩壊しないいくつかの例外? )。そして、配列内b + 1
の次の要素を指します (図に注意してください)。int
&b
は完全な配列のアドレスであり、その型はint(*)[2]
で あり、プログラムで割り当てられていない(&b + 1)
型の次の配列を指しint[2]
ます (図のどこが指しているかに注意してください(&b + 1)
)。
とのその他の興味深い違いを知るb
に&b
は: What does sizeof(&array)
return?
最初のコード スナイプでは、 を実行すると、配列のアドレスが(この例では )c = &b
に割り当てられます。GCC コンパイラを使用すると、このステートメントは「互換性のないポインター型からの代入」という警告を出します。 は へのポインタであるため
、アドレス に格納されている整数を出力します。 値が配列の 2 番目の要素 (この例では)を指しているため 、正しく 出力されます。int*
0xbf5c787c
c
int
*(c + i)
(c + i)
i = 1
(c + 1)
0xbf5c7880
*(c + 1)
2
最初のコードでの割り当てについては、以下int *c = &b;
の @AndreyT の回答を読むことを強くお勧めします。ポインターを使用して配列要素にアクセスする正しく簡単な方法は次のとおりです。
int b[2] = {1, 2};
int *c = b; // removed &, `c` is pointer to int
int i;
for (i = 0; i < 2; i++){
printf("%d ", *(c + i));
// printf("%d ", c[i]); // is also correct statement
}
2番目のコードでは、割り当てられたメモリの外側を指すように追加i
し、printfステートメントでは逆参照演算子を使用してメモリにアクセスすると、無効なメモリアクセスが発生し、実行時のこのコードの動作はUndefinedです。これが、2 番目のコードが異なる実行で異なる動作をする理由です。 &b
*
コードは構文的に正しいためコンパイルされますが、実行時に未割り当てメモリへのアクセスが OS カーネルによって検出される可能性があります。これにより、OS カーネルが、例外の原因となったプロセスにシグナル コア ダンプを送信する可能性があります (興味深い点: OS がプロセスによるメモリ権限違反を検出すると、有効なメモリへの無効なアクセスは SIGSEGV を返し、無効なアドレスへのアクセスは次を返します)。 : SIGBUS)。場合によっては、プログラムが失敗せずに実行され、ガベージ結果が生成される場合があります。
2番目のコードに関して、「配列へのポインター」を使用して配列を印刷する正しい方法は次のとおりです。
#include<stdio.h>
int main(){
int b[2] = {1, 2};
int i;
int (*c)[2] = &b; // `c` is pointer to int array of size 2
for(i = 0; i < 2; i++){
printf(" b[%d] = (*c)[%d] = %d\n", i, i, (*c)[i]); // notice (*c)[i]
}
return 1;
}
出力:
b[0] = (*c)[0] = 1
b[1] = (*c)[1] = 2
@codepadeをチェックしてください。*c
演算子の優先順位が逆参照演算子[]
よりも高いため、括弧で囲む必要があることに注意して*
ください (一方、int へのポインターを使用する場合は、上記のコードのように括弧は必要ありません)。