gpt4 book ai didi

c++ - 中毒的空引用和短路表达式评估错误或 UB?

转载 作者:行者123 更新时间:2023-11-30 01:37:48 24 4
gpt4 key购买 nike

我最近在升级到 gcc 7.2 后遇到了一个奇怪的 C++ 崩溃,这可以使用以下简单的完整 c++11 程序来演示:

#include <cassert>
#include <cstdio>

struct MyObject
{
static MyObject & null_obj()
{ return *static_cast<MyObject*> (nullptr); }

operator bool()
{
return value != 0;
}

int value = 0;
};

int foo(MyObject & obj = MyObject::null_obj())
{
if (&obj != &MyObject::null_obj() && obj)
return 1;

return 0;
}

int main(int argc, char * argv[])
{
int result;

if (argc == 1)
{
result = foo();
}
else
{
MyObject obj;
obj.value = 1;

result = foo(obj);
}

printf("%d", result);
}

程序可以使用:g++ -std=c++11 -O2

当不带参数执行时 - 程序采用顶部分支并在使用 gcc6.1 或更高版本构建时崩溃。使用 gcc 5.5 构建时不会崩溃。

添加虚拟参数时 - 程序采用第二个分支并且不会按预期崩溃。在没有编译器优化的情况下构建时,该程序在任何情况下都不会崩溃。

崩溃似乎是在评估 foo() 中的条件时发生的。根据短路评估规则,当表达式的第一部分为假时,不应执行此代码路径中的第二个条件。

当使用较新的编译器时,例如 gcc 6.1 或更高版本,为 foo 生成的汇编程序如下所示:

foo(MyObject&):
mov edx, DWORD PTR [rdi]
xor eax, eax
test edx, edx
setne al
ret

崩溃的罪魁祸首是函数顶部的 mov 指令。gcc 5.5 的汇编程序看起来有点不同:

foo(MyObject&):
test rdi, rdi
je .L3
mov edx, DWORD PTR [rdi]
xor eax, eax
test edx, edx
setne al
ret
.L3:
xor eax, eax
ret

函数顶部的检查会跳过无效读取(如预期的那样)

有人可能会争辩说,以这种方式使用空引用是一种相当狡猾的做法,我很想同意,即使我不知道确切原因。然而,我在使用 boost::throws() 的 boost::error_code 类中遇到了相同的习语:docs , source .

我知道短路评估不适用于重载 || 的类型和 && 运算符,但这里显然不是这种情况。

这是UB吗? (用户错误)、编译器错误或其他原因?

更新:

我最初对 boost::error_code 的引用是针对版本 1.65.1。此实现首先引入到 boost 版本 1.40 中。从那以后,我发现在最新版本的 boost 中,该功能被修改大概是为了避免 UB,但奇怪的是它被允许这么长时间不受挑战。新函数使用非零整数常量:

namespace detail
{
// Misuse of the error_code object is turned into a noisy failure by
// poisoning the reference. This particular implementation doesn't
// produce warnings or errors from popular compilers, is very efficient
// (as determined by inspecting generated code), and does not suffer
// from order of initialization problems. In practice, it also seems
// cause user function error handling implementation errors to be detected
// very early in the development cycle.
inline system::error_code* throws()
{
// See github.com/boostorg/system/pull/12 by visigoth for why the return
// is poisoned with (1) rather than (0). A test, test_throws_usage(), has
// been added to error_code_test.cpp, and as visigoth mentioned it fails
// on clang for release builds with a return of 0 but works fine with (1).
return reinterpret_cast<system::error_code*>(1);
}
}

inline system::error_code& throws() { return *detail::throws(); }

最佳答案

这是未定义的行为:

    { return *static_cast<MyObject*>    (nullptr); }

您不能将 nullptr 转换为引用。它还打破了任何有引用资料的人的所有假设。

注意:未定义的行为意味着任何事情都可能发生(包括崩溃或不崩溃)。

关于c++ - 中毒的空引用和短路表达式评估错误或 UB?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49020065/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com