C ++ - 检查指针是否指向有效内存(此处不能使用NULL检查)

时间:2012-08-03 00:22:29

标签: c++ pointers null interpreter

我正在创建脚本语言。 当我分配东西时,它会分配东西并返回地址 然后我用它做任何事情然后删除它。我无法控制其中的变量 比如在我的lang中创建struct(使用指针和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。 顺便说一句,我可以在这个lang中运行C ++代码。

6 个答案:

答案 0 :(得分:8)

您可以使用shared_ptr<>来保持指针,并使用weak_ptr<>将指针传递给对象的使用者。您通过销毁delete对象来shared_ptr<>该对象,然后所有weak_ptr<>都将过期。

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<>状态就是一个关键部分。

答案 1 :(得分:5)

检测存储器是否不再存活可以例如完成通过维护一组已知的死指针。您创建的任何指针都会添加到 alive 集中,当您删除对象时,您将指针移动到 dead 集。

真正棘手的部分是重用内存。如果要为不同的对象重用相同的地址,该怎么办?你不能通过查看指针来判断,因为指针看起来是一样的。因此,除非您不想重复使用内存,否则您必须更改您的要求。

  1. 一种可能的方法是在结构中添加一个字段。我知道你说你不想这样,但很多评论已经表明这是最好的解决方案,我倾向于同意。
  2. 另一种可能的方法是添加间接层,这样你就不会真正传递指向对象的指针,而是将索引转换为生命对象列表或其他任何内容。
  3. 您可能还会考虑引用计数和垃圾回收。这样,当没有人再引用它们时,对象才会被删除。相当多的工作,但作为脚本语言的用户,我希望它提供垃圾收集。

答案 2 :(得分:2)

这是一个非常糟糕的主意。指针是否可以安全使用的基础不仅仅是指针的值,而是基于指针的整个历史记录。例如,假设您分配了一些内存,然后释放它,但保留指针。此指针现在无效。现在,其他东西被分配给前一个指针所在的内存。现在,如果您尝试检测旧指针是否有效,则表明它是,因为它指向的内存已分配,但如果您尝试使用它,则会得到未定义的行为。如果你读它,你会得到垃圾。如果你试着写它,你可能会破坏堆。

如果您要做的就是检测您的进程是否可以访问指针指向的内存,这是可能的但不可移植,并且绝对不是一个好主意(它也会非常慢)。您基本上尝试读取或写入它,然后捕获操作系统异常或发出结果的信号。正如我所说,即使这是一个非常糟糕的主意。它告诉你的是,如果你尝试访问它,操作系统是否会终止你的进程;而不是它是否真的安全使用。

有关为什么这是一个坏主意的更多信息,请查看Raymond Chen撰写的这些博客文章,他们处理Windows的一些低级内容:

IsBadXxxPtr should really be called CrashProgramRandomly

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

答案 3 :(得分:0)

您可以做的一件事是使用您自己的分配器,您可以在其中分配所有对象。您的exists函数只会查询分配器以查看对象是否仍然已分配。你可以使用类似于SMR的东西来确保你的引用在使用时不会指向别的东西......

答案 4 :(得分:0)

最简单的解决方案是使用地图。地图应该由指向对象的指针编制索引,可能是void *。地图项的内容应该是创建的对象的类型。

任何时候脚本创建对象,都要向地图添加一个条目。只要脚本删除对象,就删除映射条目。当脚本访问对象时,在地图中找到指针,从而确认它是否存在以及类型是否正确。

答案 5 :(得分:-2)

这个有效检查仅在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.

功能说明:(它做什么)

在实际检查之前检查功能,如果地址有效\开始指向内存。 之后,他检查此进程是否可以达到此内存的前缀(如果不能,则捕获异常),并且最后一次检查是在任何模式下检查该内存的前缀是否被删除。 (调试\无调试模式\发布模式) 如果函数通过了所有这些检查,则返回true。