2

スクリプト言語を作成しています。物を割り当てると、物が割り当てられてアドレスが返され、それを使って何でもしてから削除します。私の言語で構造体を作成する(ポインターが有効なデータを指しているかどうかを確認するためのポインターとboolを使用した構造体)などの変数を制御することはできません。これは、RAMで言語が遅くなり、大きくなるためです。

例:(私のスクリプト言語は簡単に理解できます。これを理解できないとは思いませんが、とにかくコメントを入れておきます)

MyStruct = { //Function. For create object with it use 'new' before it.
    TestAliveVar=0
}
Func = { //I'll explain what exactly this function does every place it runs.
    if (!exists(arg0)) //C++: ???
        exit;
    arg0.TestAliveVar=1
    println "Still alive!";
}
var MyVar=new MyStruct(); //Returns address of the new object in the heap
                          //and runs on it the `MyStruct` function.
Func(MyVar);              //Sets his 'TestAliveVar' to 1
                          //and prints 'Still Alive!' with new line
delete(MyVar);            //C++: free(MyVar);
Func(MyVar);              //Does nothing

exists問題は、このコードで見た関数を作成する方法です。ところで、この言語で C++ コードを実行できます。

4

6 に答える 6

8

を使用shared_ptr<>してポインターを保持し、 を使用weak_ptr<>してポインターをオブジェクトのコンシューマーに渡すことができます。オブジェクトをdelete破棄してshared_ptr<>オブジェクトを削除すると、すべてのweak_ptr<>s が期限切れになります。

std::weak_ptr<int> wptr;
assert(wptr.expired());
{
    std::shared_ptr<int> intptr(new int);
    wptr = intptr;
    assert(!wptr.expired());
}
assert(wptr.expired());

したがって、あなたのexistsチェックは、weak_ptr<>有効期限が切れているかどうかを確認することです。

コンストラクトの使用法をもう少し具体的にするには、次のようにします。

Script code                 Hypothetical C++ code that gets executed
----                        ----
var MyVar=new MyStruct();   var_map["MyVar"]
                                = std::shared_ptr<Obj>(new Obj("MyStruct"));
Func(MyVar);                invoke("Func", std::weak_ptr<Obj>(var_map["MyVar"]));
exists(arg0)                !args[0].expired()
delete(MyVar);              var_map.erase("MyVar");

スクリプトがマルチスレッド環境で動作する場合、weak_ptr<>状態はクリティカル セクションです。

于 2012-08-03T00:47:42.577 に答える
5

メモリがもはや生きていないかどうかの検出は、たとえば既知のデッド ポインタのセットを維持することによって行うことができます。作成したポインタはすべてアライブセットに追加され、オブジェクトを削除するとポインタがデッドセットに移動します。

本当にトリッキーな部分は、メモリの再利用です。別のオブジェクトに同じアドレスを再利用したい場合はどうしますか? ポインターは同じに見えるため、ポインターを見ただけではわかりません。したがって、メモリを再利用したくない場合を除き、要件を変更する必要があります。

  1. 考えられる 1 つの方法は、構造体にフィールドを追加することです。あなたがそれを望んでいないと言ったのは知っていますが、多くのコメントがすでにこれを最善の解決策として提案しており、私は同意する傾向があります.
  2. 別の可能な方法は、オブジェクトへのポインターを実際に渡すのではなく、生きているオブジェクトのリストなどにインデックスを渡すように、間接的なレイヤーを追加することです。
  3. また、参照カウントとガベージ コレクションを検討することもできます。そうすれば、オブジェクトは誰も参照しなくなったときにのみ削除されます。非常に多くの作業が必要ですが、スクリプト言語のユーザーとしては、ガベージ コレクションが提供されることを期待しています。
于 2012-08-03T00:43:11.887 に答える
2

これは本当に悪い考えです。ポインターが安全に使用できるかどうかは、ポインターの値だけでなく、ポインターの履歴全体に基づいています。たとえば、メモリを割り当ててから割り当てを解除しても、そのメモリへのポインタは保持しているとします。このポインターは現在無効です。ここで、前のポインタがあるメモリに別の何かが割り当てられます。古いポインターが有効かどうかを検出しようとすると、それが指すメモリが割り当てられているため、有効であるように見えますが、それを使用しようとすると、未定義の動作が発生します。そこから読み取るとゴミになります。それに書き込もうとすると、おそらくヒープが破損します。

プロセスがポインタが指すメモリにアクセスできるかどうかを検出することだけが必要な場合、それは可能ですが、移植性がなく、間違いなく良い考えではありません (非常に遅くなります)。基本的に、読み取りまたは書き込みを試みてから、OS の例外をキャッチするか、その結果を通知する必要があります。私が言ったように、これでさえ本当に悪い考えです。OS がプロセスにアクセスしようとすると、OS がプロセスを強制終了するかどうかだけが表示されます。実際に安全に使用できるかどうかではありません。

これが悪い考えである理由の詳細については、Raymond Chen の次のブログ投稿を参照してください。

IsBadXxxPtr は実際には CrashProgramRandomly と呼ばれるべきです

悪いアイデアの実装を改善しても意味がない

于 2012-08-03T14:36:11.653 に答える
0

できることの 1 つは、すべてのオブジェクトを割り当てることができる独自のアロケーターを使用することです。関数は、アロケーターexistsにクエリを実行して、オブジェクトがまだ割り当てられているかどうかを確認します。Yoは、SMRに似たものを使用して、使用中の参照が他の何かを指していないことを確認できます...

于 2012-08-03T01:34:59.553 に答える
0

最も簡単な解決策は、マップを使用することです。マップは、オブジェクトへのポインター (おそらくvoid *. マップ アイテムの内容は、作成されたオブジェクトのタイプである必要があります。

スクリプトがオブジェクトを作成するたびに、エントリをマップに追加します。スクリプトがオブジェクトを削除するたびに、マップ エントリを削除します。スクリプトがオブジェクトにアクセスするときは、マップ内のポインターを見つけて、オブジェクトが存在することと型が正しいことの両方を確認します。

于 2012-08-03T14:25:21.660 に答える
-1

この有効なチェックは、Windows のみ (VS) でチェックされます。機能は次のとおりです。

#pragma once
//ptrvalid.h
__inline bool isValid(void* ptr) {
    if (((uint)ptr)&7==7)
        return false; //Not valid address at all (Maybe random pointer?)
    char _prefix;
    __try {
        _prefix=*(((char*)ptr)-1); //Get the prefix of this data
    } __except (true) { //Catch all unique exceptions (Windows exceptions) 
        return false; //Can't reach this memory
    }
    switch (_prefix) {
    case 0:    //Running release mode with debugger
    case -128: //Running release mode without debugger
    case -2:   //Running debug mode with debugger
    case -35:  //Running debug mode without debugger
        return false; //Deleted :(
        break;
    }
    return true; //Still alive!
}

使用法:

#include <stdio.h>
#include "ptrvalid.h"

void PrintValid(void* ptr) {
    if (isValid(ptr))
        printf("%d is valid.\n",ptr);
    else
        printf("%d is not valid.\n",ptr);
}

int main() {
    int* my_array=(int*)malloc(4);
    PrintValid(my_array);
    PrintValid((void*)99);
    free(my_array);
    PrintValid(my_array);
    my_array=new int[4];
    PrintValid(my_array);
    delete my_array;
    PrintValid(my_array);
    getchar();
}

出力:

764776 is valid.
99 is not valid.
764776 is not valid.
774648 is valid.
774648 is not valid.

関数の説明: (何をするか)

関数は、実際のチェックの前に、アドレスが有効かどうかをチェックします\開始点をメモリに指定します。その後、彼はこのプロセスがこのメモリのプレフィックスに到達できるかどうかをチェックし (できない場合に例外がキャッチされた場合)、最後のチェックは、どのモードでも削除された場合にこのメモリのプレフィックスをチェックすることです。(Debugging\Without Debug Mode\Release Mode) 関数がこれらすべてのチェックに合格した場合、true を返します。

于 2012-08-03T13:54:06.617 に答える