Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/146.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ C++;-检查指针是否指向有效内存(此处不能使用空检查)_C++_Pointers_Null_Interpreter - Fatal编程技术网

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

C++ C++;-检查指针是否指向有效内存(此处不能使用空检查),c++,pointers,null,interpreter,C++,Pointers,Null,Interpreter,我正在创建脚本语言。 当我分配东西时,它就是分配东西并返回地址 然后我用它做任何事情,然后删除它。我无法控制其中的变量 比如在我的lang中创建struct(用指针和bool来检查指针是否指向有效数据的struct)等等,因为这会使我的lang在RAM中变慢、变大 例如:(我的脚本语言很容易理解。我怀疑你不会理解这一点,但无论如何我会在其中添加一些注释) 问题是如何创建您在此代码中看到的函数exists。 顺便说一下,我可以在这个Lang.< /P> < P>中运行C++代码,检测内存是否已经被

我正在创建脚本语言。 当我分配东西时,它就是分配东西并返回地址 然后我用它做任何事情,然后删除它。我无法控制其中的变量 比如在我的lang中创建struct(用指针和bool来检查指针是否指向有效数据的struct)等等,因为这会使我的lang在RAM中变慢、变大

例如:(我的脚本语言很容易理解。我怀疑你不会理解这一点,但无论如何我会在其中添加一些注释)

问题是如何创建您在此代码中看到的函数
exists

顺便说一下,我可以在这个Lang.< /P> < P>中运行C++代码,检测内存是否已经被激活,例如可以通过维护一组已知的死指针来完成。创建的任何指针都会添加到活动集,删除该对象时,会将指针移动到死集

真正棘手的部分是重用内存。当您想为不同的对象重用相同的地址时,您会怎么做?你不能通过看指针来判断,因为指针看起来是一样的。因此,除非您永远不想重用内存,否则您必须更改您的需求

  • 一种可能的方法是在结构中添加字段。我知道你说过你不想那样做,但很多评论都认为这是最好的解决办法,我倾向于同意
  • 另一种可能的方法是添加一层间接寻址,这样您就不会真正传递指向对象的指针,而是索引到一个活动对象列表或其他任何内容中
  • <>你也可以考虑引用计数和垃圾回收。这样,只有当没有人再引用对象时,对象才会被删除。相当多的工作,但作为脚本语言的用户,我希望它能够提供垃圾收集
    您可以使用
    shared_ptr
    握住指针,并使用
    weak_ptr
    将指针传递给对象的使用者。您可以通过销毁
    共享的\u ptr
    对象来
    删除该对象,然后所有
    弱的\u ptr
    都将过期

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

    如果脚本要在多线程环境中运行,那么
    弱ptr
    状态是一个关键部分。

    您可以使用自己的分配器,在该分配器中可以分配所有对象。您的
    exists
    函数只需查询分配器,查看对象是否仍然被分配。您可以使用类似于SMR的方法来确保您的引用在仍在使用时不会指向其他内容…

    此有效检查仅在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();
    }
    
    函数说明:(它的作用)

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

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


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

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

    如果您只想检测您的进程是否可以访问指针指向的内存,那么这是可能的,但不可移植,而且肯定不是一个好主意(它也会非常慢)。基本上,您必须尝试对其进行读取或写入,然后捕获操作系统异常或由此产生的信号。正如我所说的,即使是这也是一个非常糟糕的主意。它告诉你的只是如果你试图访问它,操作系统是否会杀死你的进程;不知道它是否真的安全使用

    关于为什么这是一个坏主意的更多信息,请查看Raymond Chen的博客文章,他从事一些底层Windows的工作:


    很难想象保持“valid”标志比测试指针的有效性慢多少。@Ugo如果我将指针发送到代码中没有结构的任何地方,然后在某个地方删除它,然后我如何检查指针是否仍然有效?@BenVoigt如果我考虑如何在我的语言中实现它,它将适合任何表达式的值内存和更多。顺便说一句,我的lang中的值也很大,我不认为我需要把它变得更大。我的意思是,检查指针是否“有效”会有很多误报,而且速度非常慢。即使你在性能上受到了冲击,想要得到一个结构,这仍然比你所问的设计要快他正在寻找一种解决方案,以防止他的解释器访问指向无效内存的脚本传递的指针……就像弗拉德拉扎连科所说的那样。@弗拉德拉扎连科:我不明白。我看不出我的解决方案与您的声明之间有任何不一致之处。我要指出,
    过期
    具有意义
    #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.