5

これは演習のコードです:

#include <iostream>
using namespace std;

int main() {
    int n = 13;
    int* ip = new int(n + 3);
    int* ip2 = ip;
    cout << *ip << endl;
    delete ip;
    cout << *ip2 << endl;
    cout << ip << tab << ip2 << endl;
}

ヒープ上の int に割り当てられた領域が削除されると、ポインタを逆参照すると何らかのメモリ エラーが発生するのではないかと考えました。代わりに、0 を返します。

どうしてこれなの?

4

4 に答える 4

14

無効なポインターを逆参照すると、仕様ごとに未定義の結果が発生します。失敗する保証はありません。

通常 (CPU/OS/コンパイラ/... 依存)、コンパイラはまったく気にしません。現在そのメモリアドレスにあるものを提供するだけです。たとえば、x86 アーキテクチャでは、アドレスがプロセスにマップされていないメモリ ページにある場合 (またはプロセスにアクセスする権限がない場合) にのみエラーが表示されるため、CPU によって例外がスローされます。 (保護違反)OSが適切に処理します(そしておそらく、プロセスを失敗させます)。アドレスにアクセスすると常にアクセス違反が発生するようにするために、トリックが使用されることがあり0ます。OS は、ページ テーブル内のアドレス空間の最初のページの読み取り/書き込みビットを 0 に設定し、そのページへのアクセスが常に例外を生成するようにします。 .

于 2009-05-29T20:22:33.347 に答える
4

この動作は定義されていないため、何が起こるかは実装とシステムに依存します。

于 2009-05-29T20:23:29.723 に答える
3

ポインタを逆参照するipと、その時点でそのメモリ位置にあったものが返されます。

0を返すのipは、整数としてキャストされたときに4バイトがたまたまエンコードされるためです。

削除された後のポインターの間接参照は予測できません。ゼロの可能性があります。そのメモリが別の場所に再割り当てされている場合は、別の可能性があります。

プログラムを実行するときに0になるのは幸運です。

于 2009-05-29T20:25:12.920 に答える
0

Mehrdadsの回答に加えて、glibcを使用したgccでは、メモリヒープを表すデータ構造が返されたメモリのブロックに格納され、メモリを節約します(つまり、侵入リスト)。そのため、メモリ ブロックが解放されると、フリー リストに追加されます。フリーに続いて書き込まれる 0 は、これがフリー ブロック リストの最後の要素であることを示していると思います (解放されたメモリの最初のポインター サイズのワードには、リストnextポインターが含まれます)。

このブロックを再び逆参照する前に、より多くのメモリを割り当てて解放すると、空きリストの最後に新しい項目が追加されたときに値が変更されます。これは、ライブラリ実装の決定が「未定義」動作中に何が起こるかに影響を与える 1 つの方法です。この場合、glibc の開発者は、この動作が未定義であるという事実を利用して、メモリ アロケータのスペース効率を高めています。

プログラムを valgrind で実行すると、これらのエラーが検出されます。いずれにせよ、未定義の動作は常に避けてください。さまざまなプラットフォームで異なる可能性が高く、同じプラットフォームでも異なるビルドである可能性が非常に高いためです (例: デバッグとリリース)。

于 2009-05-30T19:43:03.997 に答える