以下使用 seg-V 崩溃:
// my code
int* ipt;
int bool set = false;
void Set(int* i) {
ASSERT(i);
ipt = i;
set = true;
}
int Get() {
return set ? *ipt : 0;
}
// code that I don't control.
struct S { int I, int J; }
int main() {
S* ip = NULL;
// code that, as a bug, forgets to set ip...
Set(&ip->J);
// gobs of code
return Get();
}
这是因为虽然 i
不是 NULL
,但它仍然无效。如果调用代码从 NULL
指针获取数组索引操作的地址,也会发生同样的问题。
一个解决方案是修剪低位:
void Set(int* i) {
ASSERT((reinterpret_cast<size_t>(i))>>10);
ipt = i;
set = true;
}
但是我应该/可以去掉多少位?
编辑,我不担心未定义的行为,因为无论如何我都会中止(但比 seg-v 更干净)。
FWIW:这是一个半假设的情况。导致我想到这个的错误在我发布之前已经修复,但我以前遇到过它并且正在考虑将来如何使用它。
为了论证可以假设的事情:
- 如果调用 Set 时使用了 seg-v 的东西,那是一个错误
- Set 可能会被代码调用,这不是我要修复的工作。 (例如,我提交了一个错误)
- Set 可能会被我试图修复的代码调用。 (例如,我在调试工作中添加完整性检查。)
- 以不提供有关调用 Set 位置的信息的方式调用我。 (即允许 Get 到 seg-v 并不是调试任何东西的有效方法。)
- 代码不需要可移植或捕获 100% 的错误指针。它只需要经常在我当前的系统上运行,足以让我找到哪里出了问题。
最佳答案
除 NULL 外,没有可移植的方法来测试任何无效指针。在你对它做任何事情之前,评估 &ip[3]
会给出未定义的行为;唯一的解决方案是在对指针进行任何运算之前 测试 NULL。
如果你不需要可移植性,也不需要保证你捕获所有的错误,那么在大多数主流平台上你可以检查地址是否在内存的第一页内;通常将 NULL 定义为地址零,并保留第一页以捕获大多数空指针取消引用。在 POSIX 平台上,这看起来像
static size_t page_size = sysconf(_SC_PAGESIZE);
assert(reinterpret_cast<intptr_t>(i) >= page_size);
但这不是一个完整的解决方案。唯一真正的解决方案是首先修复任何滥用空指针的问题。
关于c++ - 检查 NULL 时要忽略多少位?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/3720184/