【发布时间】:2017-04-17 02:41:27
【问题描述】:
我有一个非常古老(而且很大)的 Win32 项目,它通过将取消引用的指针转换为指针来使用 NULL 指针进行大量检查。像这样:
int* x = NULL; //somewhere
//... code
if (NULL == &(*(int*)x) //somewhere else
return;
是的,我知道这段代码很愚蠢,需要重构。但由于代码量很大,这是不可能的。现在我需要在 Xcode 中的 MacOS Sierra 下编译这个项目,这会导致很大的问题......事实证明,在发布模式(使用代码优化)中,条件以不正确的行为执行(由于取消引用 NULL,因此称为未定义行为指针)。
根据this document for GCC 有一个选项-fno-delete-null-pointer-checks,但是当启用 O1、O2 或 O3 优化时,它似乎不适用于 LLVM。所以问题是:如何强制 LLVM 8.0 编译器允许此类取消引用?
更新。检查问题的真实工作示例。
//somewhere 1
class carr
{
public:
carr(int length)
{
xarr = new void*[length];
for (int i = 0; i < length; i++)
xarr[i] = NULL;
}
//some other fields and methods
void** xarr;
int& operator[](int i)
{
return *(int*)xarr[i];
}
};
//somewhere 2
carr m(5);
bool something(int i)
{
int* el = &m[i];
if (el == NULL)
return FALSE; //executes in debug mode (no optimization)
//other code
return TRUE; //executes in release mode (optimization enabled)
}
-O0 和 -O1、something keeps the null check 和代码“有效”:
something(int): # @something(int)
pushq %rax
movl %edi, %eax
movl $m, %edi
movl %eax, %esi
callq carr::operator[](int)
movb $1, %al
popq %rcx
retq
但是-O2及以上,the check is optimized out:
something(int): # @something(int)
movb $1, %al
retq
【问题讨论】:
-
Corresponding bug report。这并不乐观:该标志现在确实被忽略了(起初它无法识别)。
-
-fno-delete-null-pointer-checks不应该影响&*(int*)x,它仍然应该被允许为NULL。在gcc.godbolt.org 上使用clang 检查,只需使用bool b(short *p) { return 0 == &*(int*)p; },clang 会生成正确的代码。请发布一个最小的完整程序,您的编译器会生成不正确的代码。 -
@hvd 我已经发布了真实的例子。我不确定这个问题是否与 GCC 有关,我只在 Apple LLVM 8.0 中看到过这个
-
@hvd
&返回的东西不应该为空——它是某物的地址。取消引用空指针会触发 UB,因此bool b(short *p) { return true; }将是根据标准对您的函数进行的有效优化。 -
@Quentin 对于 C,即使
p是NULL,也明确表示允许&*p,对于 C++,意图已被声明为相同,这就是编译器所做的.对于参考来说,这是一个不同的故事,但这里没有参考。请参阅open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#232 编辑:编辑后的问题中现在有 are 引用。这就解释了。
标签: c++ undefined-behavior clang++ dereference null-pointer