2

我正在创建脚本语言。当我分配东西时,它是分配东西并返回地址,然后我用它做任何事情然后删除它。我无法控制其中的变量,例如在我的 lang 中创建结构(带有指针和 bool 的结构以检查指针是否指向有效数据)等,因为它会使我的 lang 在 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. 一种可能的方法是在结构中添加一个字段。我知道您说过您不希望这样,但许多评论已经表明这是最佳解决方案,我倾向于同意。
  2. 另一种可能的方法是增加一个间接层,这样您就不会真正传递指向对象的指针,而是将索引传递到活动对象列表或其他内容中。
  3. 您还可以考虑引用计数和垃圾收集。这样,对象只会在没有人再引用它们时被删除。相当多的工作,但作为脚本语言的用户,我希望它能够提供垃圾收集。
于 2012-08-03T00:43:11.887 回答
2

This is a really bad idea. Whether a pointer is safe to use is based on more than just the value of the pointer, it's based on the entire history of the pointer. For instance, let's say that you allocate some memory, then deallocate it but you keep the pointer to it. This pointer is now invalid. Now something else gets allocated to the memory where the previous pointer is. Now if you try to detect if the old pointer is valid, it appears that it is, because the memory it points to is allocated, but if you try to use it, you get undefined behavior. If you read from it, you'll get garbage. If you try to write to it you'll probably corrupt the heap.

If all you want to do is detect is whether your process can access the memory that the pointer points to, that is possible but not portable, and definitely not a good idea (it will also be very slow). You basically have try to read from or write to it, and then catch the OS exception or signal that results. As I said, even this is a really bad idea. All it tells you is whether the OS will kill your process if you try to access it; not whether it's actually safe to use.

For more on why this is a bad idea, check out these blog posts from Raymond Chen, who works on some low-level stuff Windows stuff:

IsBadXxxPtr should really be called CrashProgramRandomly

There's no point improving the implementation of a bad idea

于 2012-08-03T14:36:11.653 回答
0

您可以做的一件事是使用自己的分配器,您可以在其中分配所有对象。您的exists函数将简单地查询分配器以查看对象是否仍被分配。哟可以使用类似于 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 回答