まず、アドレスを印刷したという事実は、メモリがそのアドレスに割り当てられていることを意味するものではありません。単純に数字を足して、他の数字を生成しました。
第 2 に、2 を加算して得た数値がベース アドレスよりも 2 大きいのではなく 8 である理由は、C で整数をポインターに加算すると、ポイント先の要素に関して算術演算が行われるためです。 、メモリ内のバイト単位ではありません (ポイント先の要素がバイトでない場合)。たとえば、int の配列がありint x[8]
、へのポインタがあるとしますx[3]
。x[5]
そのポインタに 2 を加算すると、 の先頭から 2 バイト先へのポインタではなく、へのポインタが生成されます。x[3]
. C は抽象化であり、C 標準はその抽象化内で何が起こるかを指定していることを覚えておくことが重要です。C の抽象化の内部では、ポインター演算は生のメモリ アドレスではなく、要素の数に対して機能します。C 実装 (C コードをプログラム実行に変換するコンパイラとツール) は、C 標準で指定された抽象化を実装するために必要な生メモリ アドレスに対する操作を実行する必要があります。通常、これはコンパイラが整数をポインタに追加するときに要素のサイズで乗算することを意味します。したがって、2 は 4 で乗算され (anint
が 4 バイトのマシンでは)、結果の 8 がベース アドレスに追加されます。
第三に、この動作に依存することはできません。C 標準では、配列の末尾にある 1 つの架空のオブジェクトを含め、配列内のオブジェクトを指すポインターに対してのみポインター演算を定義しています。さらに、個々のオブジェクトへのポインターは、1 つの要素の配列のように機能します。したがって、p
int を指すポインタがある場合、 p+0
orを計算できますp+1
。これらは、配列内の唯一のオブジェクト ( p+0
) と、配列内の最後の要素の 1 つ先の架空のオブジェクト( ) を指しているためp+1
です。またはを計算することはできません。これらは配列の外にあるためです。これは、ポインタを逆参照すること (計算されたアドレスでメモリを読み書きしようとすること) の問題ではないことに注意してください。p-1
p+2
アドレスは、C 標準で定義されていない動作をもたらします。プログラムがクラッシュしたり、「正しい」結果が得られたり、アカウント内のすべてのファイルが削除されたりする可能性があります。これらの動作はすべて C 標準に準拠しています。 .
範囲外のアドレスを計算するだけで、このような奇妙な動作が発生することはまずありません。ただし、一部のコンピュータ プロセッサには、単純な演算よりも多くの作業を必要とする特殊なアドレス スキームがあるため、標準では許可されています。おそらく、フラット アドレス空間に次いで 2 番目に一般的なアドレス スキームは、ベース アドレスとオフセット スキームです。このような方式では、4 バイト ポインターの上位 16 ビットにベース アドレスが含まれ、下位 16 ビットにオフセットが含まれる場合があります。所定のベース アドレス b とオフセット o の場合、対応する仮想アドレスは 4096*b+o となる場合があります。(このようなスキームは、2 20しかアドレス指定できません。バイト、およびベースとオフセットの多くの異なる値が同じアドレスを参照できます。たとえば、基数 0 とオフセット 4096 は、基数 1 およびオフセット 0 と同じアドレスを参照します。) 基数とオフセットのスキームを使用すると、コンパイラは、オフセットのみに加算し、基数を無視することによって、ポインター演算を実装する場合があります。(このような C 実装は、最大 65536 バイトの配列のみをサポートできます。これは、オフセットだけでアドレス指定できる範囲です。) そのような実装ではp
、0x0000fffc (基数 0、オフセット 65532) のエンコーディングで int へのポインターがある場合、およびint
4 バイトp+2
の場合、値は 0x00000004 であり、8 より大きい値 (0x00010004) ではありません。
これは、ポインター演算がフラット アドレス マシンからは予期しない値を生成する例です。C 標準に従って無効なポインター演算がクラッシュを引き起こす実装を想像するのは困難です。ただし、プロセッサが仮想メモリをサポートするハードウェアを備えていないため、メモリをプロセスによって手動でスワップする必要がある実装を検討してください。このような実装では、ポインターには、ディスクの場所を記述するメモリ内の構造体のアドレスと、メモリ スワッピングの管理に使用されるその他の情報が含まれる場合があります。このような実装では、ポインタ演算を行うにはメモリ内の構造体を読み取る必要があるため、無効なポインタ演算を行うと無効なアドレスが読み取られる可能性があります。