0

これらのエラーと実行時またはコンパイル後の結果との意味の違いをよりよく理解するために、これをコミュニティ wiki にしています。また、私は Java でのコーディングが長すぎたので、C++ でポインターをよりよく学びたいと考えています。

Edit2:この質問をリファクタリングしています。私が描こうとしている違いは、マネージ コードでは、これらのエラーはすべて例外を介して一様に処理されるということです。ただし、C++ はそれほど単純ではありません。それぞれのケースで、エラー、セグメンテーション違反、回復可能な動作、さらに悪いことに伝播するサイレントエラーが発生する可能性があるかどうかを理解したいと思います。私の新しい具体的な例を見てください (そして、はい、答えは常に「コーディングされているとおりに」であることを知っています。結局のところ、私はプログラマーです。あなたがよく遭遇することの興味深い詳細を知りたいです。)

Edit3:以下では、「クラス」とは、代わりにクラスのインスタンスを意味します。ありがとう

エラー 1: ポインター値が NULL、別名ポインター == 0 です

  • マネージ コード: 実行時に NullPointerException をスローします。
  • C++: ?
  • 例:クラスへのポインタがありますが、0 に初期化されています。関数に渡すとどうなりますか。すなわち。C++ はクラスの表示を一切残しません。これは、パブリックな「プレースホルダー」を連結したものにすぎません。

エラー 2: ポインターは、値が NULL または == 0 であるメモリ内の以前のクラスを指しています

  • マネージ コード: メモリ モデルでは許可されていません。参照されたすべてのオブジェクトはメモリに残ります。例外的なケースはありませんか?
  • C++: ?
  • 例:クラスへのポインタがあり、そのクラスが削除されました。次に、ポインターを引数として関数に渡します。明らかに、発生する問題は、関数がポイント先のクラスをどのように処理するかによって異なります。私の質問は: STL でこれに対するフェイルセーフ処理はありますか? 優れたプロプライエタリ ライブラリ? 平均的なオープン ソース コードは?

エラー 3: ポインタが正しいクラスまたはサブクラスではないクラスを指しています

  • マネージ コード: ClassCastException をスローします。
  • C++: [間違っていれば訂正] コンパイラは、不正なキャストを許可しないことでこれと戦おうとします。ただし、これが実行時に発生する場合は、未定義の動作が想定されます。これが常に爆発するとは限らない同様のクラスオブジェクトのケースはありますか?
  • 例: ポインターが誤って再割り当てされ、その値が別のクラスと完全に等しくなります。この参照されたクラスを渡す関数は、参照するインスタンス変数のオフセットをやみくもに取得すると仮定します。したがって、生のバイナリを間違って解釈します。C++ でこれを防ぐ方法はありませんか? および/または... この能力が永久に悪用される場合はありますか?

エラー 4: ポインターがクラスの中間 (位置合わせされていない) または初期化されていないガベージを指している

  • マネージ コード: メモリ モデルでは許可されていません。
  • C++: ケース 3 に相当しますか?
  • 例: 多くの場合、実際にこれを合法的に使用します。たとえば、STL ベクトルの配列に直接アクセスできます。これは、クラスの中央を指しています。しかし、それは「見逃す」のと同じくらい簡単に思えますか?リンク先のライブラリとは異なるライブラリがロードされた場合など、意図に反してこれが発生する可能性がある一般的な落とし穴はありますか (それを防ぐメカニズムはありますか?)

すべての貢献者に事前に感謝します。

4

8 に答える 8

1

これらのほとんどは、予期しない動作を引き起こします。Steve McConnell の Code Complete 2nd Edition を引用すると、「ポインターの使用は本質的に複雑であり、ポインターを正しく使用するには、コンパイラーのメモリ管理スキームを十分に理解している必要があります」。

于 2009-02-06T01:12:08.247 に答える
1

良い。C++ では、ケース 2 以外でポインターを逆参照すると、未定義の動作が発生するため、何が起こるかわかりません。ただし、ほとんどのオペレーティング システムでは、null ポインターを逆参照すると、セグマンテーション違反が発生します。

比較でポインターを使用するだけで、null ポインターの場合は問題ありませんが、それ以外の場合とケース 2 については正確に定義されていません (未指定)。

ケース 2 は完全に定義されています。ポインターが値 0 の int を指すようにすることができます。C# でさえ、そのようなことが違法である理由がわかりません。おそらく私はあなたのケースを誤解している 2

ケース 3 では、ポインターが既にその間違ったオブジェクトを指しているのか、それともまだそのオブジェクトを指そうとしているのかを区別する必要があります。C++dynamic_castは、指しているオブジェクトの型をチェックし、それが派生していないか、キャストされた型と同じ型でない場合は、null ポインターを返します。しかし、そのチェックを行わない他のキャストがあり、無効なポインターが残ります。

于 2009-02-06T01:35:20.087 に答える
1
  1. ポインター値は NULL、別名ポインター == 0未定義の動作です。コンパイラは、毎回異なることを含め、必要なことは何でも行うことができます。ほとんどの UNIX ベースのシステムでは、これによりセグメンテーション違反が発生します。
  2. 削除されたポインタへのアクセスこれは未定義の動作です。場合によっては、メモリの割り当てと使用の正確なパターンによっては、メモリが他の目的で再利用されていない場合、削除されたポインターを削除されていないかのように使用できる場合があります。これにより、バグの追跡が非常に困難になる可能性があります。もう一度ポインターを削除すると、おそらくメモリ割り当てシステムが破損し、まったく関係のないニュース/削除がクラッシュする可能性があります
  3. ポインターが正しいクラスまたはサブクラスではないクラスを指しているC++ は実行時の型チェックを行いません。メモリ位置をポインタの型として解釈しようとします。正しいタイプのオブジェクトがその場所に作成されていない場合、それは未定義の動作であり、何かが発生する可能性があります (正しく動作しているように見えることを含む)。
  4. ポインタがクラスの中央を指している (位置合わせされていない) または初期化されていないガベージ上記と同じ、未定義の動作。

要約すると、価値のあることを行うこれらのいずれかに依存することはできません。それらが起こらないようにコードを設計することが重要です。コンパイラはできる限り手助けするので、だまそうとすること (キャストなど) には十分注意してください。コンパイラは最終的に復讐を果たします。

于 2009-02-06T01:37:03.033 に答える
0

#1はsegfaultをスローする必要があります。

#2、#3、および #4 は、メソッドが何をしようとしているかに応じて機能する可能性があります。C++ では、クラス コードは 1 回だけ (オブジェクト ポインターが参照するインスタンス データとは別に) 格納されるので、メモリのランダムなチャンクでクラス メソッドを呼び出すことができることを思い出してください。たとえば、次の例では "-1" が出力されます (g++ 4 でテスト済み)。

#include <iostream>

class Foo
{
public:
    int x;
    void foo()
    {
        std::cout << x << std::endl;
    }
};

int main(void)
{
    void* mem = malloc(1024);
    memset(mem, 0xff, 1024);
    Foo* myFoo = (Foo*)mem;
    myFoo->foo();
    return 0;
}
于 2009-02-06T01:21:46.167 に答える
0

Windows では、読み取りアクセス権のない仮想メモリ ページにアクセスしようとすると、エラー 1 によって構造化 (win32) 例外が発生し、アクセス違反が発生します。Unix 由来の OS は、用語は異なりますが、同様のメカニズムを持っています。

これは明確に定義された (通常は望ましくない場合の) 動作であり、構造化された例外ハンドラーによってトラップされる可能性があります。通常、マネージド ランタイムは、基になる OS がこの例外を発生させ、それを処理してマネージド例外に変換することに依存します。これは、フォローする前にすべてのポインター アクセスを null でチェックするよりもはるかに効率的です。

于 2009-02-06T01:23:57.940 に答える
0

これはあなたのエラーの私の改訂版です:

エラー 1: null ポインター/参照

  • マネージド コード: 参照の場合は NullReferenceException をスローし、ポインターの場合は AccessViolationException をスローします (はい! ポインターはマネージド コードに存在します!)。
  • ネイティブ コード: Windows では、これにより "アクセス違反" (多くの場合、AV と呼ばれます) が発生します。Unix では、これは「セグ フォールト」と呼ばれます。Windows では、これは理論的には例外処理を使用してキャッチできます。

エラー 2: 解放されたオブジェクトへのポインター

  • マネージ コード: 通常は未定義ですが、AccessViolationException である可能性が非常に高いです。(これは常に有効なマネージド参照ではなく、実際のポインターの使用法を指すことに注意してください)
  • ネイティブ コード: 通常は未定義ですが、アクセス違反の可能性があります。

エラー 3:

  • マネージ コード: 例外をスローします
  • ネイティブ コード: キャストの種類に応じて、静的キャストの場合はコンパイラ エラー、再解釈キャストの場合は未定義の結果になります。

エラー 4:

  • マネージド コード: 未定義
  • ネイティブ コード: 未定義
于 2009-02-06T01:52:17.387 に答える
0

このちょっとした情報を追加します。ポインターは、あなたが指示したことは何でも実行します。プログラムがカーネルにアクセスできる場合は、カーネルの上書きを含みます。

たとえば、ポイント 3 を取り上げます。これは、カーネルに対する多くの攻撃で使用される手法です。カーネルが存在する場所を見つけ、ポインターを使用して情報を変更します。誰かがこれを試すように提案しているわけではありません。ルートキットやその他のマルウェアの使用を容認しているわけではありません。

于 2009-02-06T01:57:44.753 に答える