2012-08-03 444 views
1

我正在创建脚本语言。 当我分配的东西,它分配的东西,并返回地址 ,然后我做任何事情,然后删除它。我无法控制其中的变量 就像在我的lang中创建结构(struct用指针和bool来检查指针是否指向有效数据)等等,因为它会让我的lang在RAM中变得越来越慢。C++ - 检查指针是否指向有效内存(不能在这里使用NULL检查)

例如:(我的脚本语言是容易理解的,我怀疑你会不明白这一点,但我会放一些评论中也无妨。)

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++代码。

+7

我没有理解任何事情 – log0 2012-08-03 00:26:33

+4

很难想象如何保持“有效”标志可能比测试指针的有效性要慢。 – 2012-08-03 00:27:41

+0

@Ugo如果我将指针发送到代码中没有struct的地方,并在某处删除它,那我该如何检查指针是否仍然有效? – MessyCode 2012-08-03 00:27:55

回答

-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. 

功能的解释:(什么它呢)

在真正的检查之前检查功能,如果地址有效\开始指向内存。 之后,他检查此进程是否可以达到此内存的前缀(如果捕获异常,如果不能),最后一次检查是检查此内存的前缀是否在任何模式下被删除。 (Debugging \ Without Debug Mode \ Release Mode) 如果函数通过了所有这些检查,则返回true。

+0

down-voter - 为什么? – MessyCode 2012-08-04 18:04:00

+0

您已经重新创建了'IsBadXxxPtr'函数,但强度较低。请参阅[这里](http://blogs.msdn.com/b/oldnewthing/archive/2006/09/27/773741.aspx),为什么这可能是一个坏主意的合理解释。之前的回答,早在这篇文章发表之前,已经引用了这个相同的链接。由于这个原因,我怀疑人们正在贬低它,加上这是一个非常糟糕的主意,他们想阻止其他人使用/考虑。 – 2012-08-05 05:21:17

+0

@CodyGray生成随机指针时抛出的异常。当你只写了'(void *)52256'这是有效地址。但是如果你的地址真的被分配了,它不会抛出异常返回false。 – MessyCode 2012-08-05 06:32:37

5

检测是否存储不再存在可以完成例如通过维护一套已知的死指针。您创建的任何指针都会被添加到存在的集合中,并且当您删除该对象时,您将指针移至死亡集合。

真的很棘手的部分将重用内存。当你想为不同的对象重用相同的地址时,你会做什么?你看不到指针,因为指针看起来是一样的。所以除非你不想重复使用记忆,否则你必须改变你的要求。

  1. 一种可能的方法是在结构中增加一个字段。我知道你说过你不想要这个,但很多评论已经表明这是最好的解决方案,我倾向于赞同。
  2. 另一种可能的方式是添加一个间接层,以便您不会将指针指向对象,而是将其索引到活动对象列表或任何其他内容中。
  3. 您可能还会考虑引用计数和垃圾收集。这样,只有当没有人再引用它时,对象才会被删除。相当多的工作,但作为脚本语言的用户,我期望它提供垃圾收集。
+0

我想你会需要存储指针和内存宽度。因为该范围内的任何内容均有效。例如,如果他的脚本将指针传递给有效数组的中间元素。但是,是的,+1 – 2012-08-03 00:50:09

+0

@ VladLazarenko:只有当脚本可以做指针运算时,您才需要宽度。如果数组访问被包裹在不透明的函数中,然后知道整个数组是否还活着可能就足够了。 – MvG 2012-08-03 00:53:44

+0

我能得到的最接近的是第三。但要实现它,它将需要很多性能。任何'流行'(我有我的表达式计算器堆栈)表达式中需要放置智能指针代码。 – MessyCode 2012-08-03 01:12:31

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

我想(可能是错误的),他正在寻找一种解决方案,以保护他的解释器不会访问指向无效内存的脚本传递的指针...... – 2012-08-03 00:49:02

+0

就像VladLazarenko说的那样。 – MessyCode 2012-08-03 00:50:27

+1

@VladLazarenko:我不明白。我没有看到我的解决方案和您的陈述之间有任何不相容的地方。 – jxh 2012-08-03 00:54:24

0

你可以做的一件事就是使用你自己的分配器,你可以在其中分配所有的对象。你的exists函数只是简单地查询分配器来查看对象是否仍然被分配。哟可以使用类似SMR的东西,以确保您的参考不指向其他东西,而仍然在使用中...

0

最简单的解决方案是使用地图。该地图应该通过指向该对象的指针进行索引,可能为void *。地图项目的内容应该是创建的对象的类型。

任何时候一个脚本创建一个对象,添加一个条目到地图。任何时候脚本删除一个对象,删除地图条目。当脚本访问一个对象时,在映射中找到指针,从而确认它存在并且类型正确。

2

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

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

更多关于为什么这是一个坏主意,看看从雷蒙德陈,谁在一些低层次的东西的Windows东西的作品,这些博客文章:

IsBadXxxPtr should really be called CrashProgramRandomly

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