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