-1

以下は簡単なコード スニペットです。

int main()
{
int *p;
p=(int*)malloc(sizeof(int));//allocate m/y 4 1 int 
printf("P=%p\tQ=%p",p,p+2);
}

1 回のサンプル実行で、次のような出力が得られました。

P=0x8210008 Q=0x8210010

P の開始アドレスは-P=0x8210008、次のバイトは 0x8210009、次のバイトは 0x821000A、次のバイトは 0x821000B です。したがって、int の 4 バイトはそこで終了します。malloc を使用して追加のメモリを割り当てていません。次に、p+2 は、P(0x8210008) の 8 バイト後にある 0x8210010 にどのようにつながるのでしょうか。

4

4 に答える 4

5

ポインターからの整数要素オフセットとして扱っているためです。単一の整数に配列を割り当てました。求めるときp+2は と同じ&p[2]です。最初から 2 バイトが必要な場合は、最初にキャストする必要がありますchar*

char *highWordAddr = (char*)p + 2;
于 2012-07-31T04:00:18.240 に答える
4

C では、好きなポインター演算を喜んで実行できます。p+2他のアドレスのように見えるからといって、それが有効であるとは限りません。実際、この場合はそうではありません。

割り当てられた境界の外に出ないように、ポインター演算が表示されるときはいつでも十分に注意してください。

于 2012-07-31T03:59:29.197 に答える
4

まず、アドレスを印刷したという事実は、メモリがそのアドレスに割り当てられていることを意味するものではありません。単純に数字を足して、他の数字を生成しました。

第 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 つの要素の配列のように機能します。したがって、pint を指すポインタがある場合、 p+0orを計算できますp+1。これらは、配列内の唯一のオブジェクト ( p+0) と、配列内の最後の要素の 1 つ先の架空のオブジェクト( ) を指しているためp+1です。またはを計算することはできません。これらは配列の外にあるためです。これは、ポインタを逆参照すること (計算されたアドレスでメモリを読み書きしようとすること) の問題ではないことに注意してください。p-1p+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 へのポインターがある場合、およびint4 バイトp+2の場合、値は 0x00000004 であり、8 より大きい値 (0x00010004) ではありません。

これは、ポインター演算がフラット アドレス マシンからは予期しない値を生成する例です。C 標準に従って無効なポインター演算がクラッシュを引き起こす実装を想像するのは困難です。ただし、プロセッサが仮想メモリをサポートするハードウェアを備えていないため、メモリをプロセスによって手動でスワップする必要がある実装を検討してください。このような実装では、ポインターには、ディスクの場所を記述するメモリ内の構造体のアドレスと、メモリ スワッピングの管理に使用されるその他の情報が含まれる場合があります。このような実装では、ポインタ演算を行うにはメモリ内の構造体を読み取る必要があるため、無効なポインタ演算を行うと無効なアドレスが読み取られる可能性があります。

于 2012-07-31T08:29:42.017 に答える
1

これはポインタ演算と呼ばれます。http://www.learncpp.com/cpp-tutorial/68-pointers-arrays-and-pointer-arithmetic/

于 2012-07-31T03:59:13.170 に答える