是否存在取消引用(但不使用)空指针以进行空引用的平台或情况会表现不佳? [英] Is there a platform or situation where dereferencing (but not using) a null pointer to make a null reference will behave badly?

查看:12
本文介绍了是否存在取消引用(但不使用)空指针以进行空引用的平台或情况会表现不佳?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我目前正在使用一个使用类似代码的库

I'm currently using a library that uses code like

T& being_a_bad_boy()
{
    return *reinterpret_cast<T*>(0);
}

在实际上没有 T 的情况下引用 T.这是未定义的行为,特别指出不受标准支持,但这并不是闻所未闻的模式.

to make a reference to a T without there actually being a T. This is undefined behavior, specifically noted to be unsupported by the standard, but it's not an unheard of pattern.

我很好奇是否有任何示例、平台或用法表明在实践中这会导致问题.谁能提供一些?

I am curious if there are any examples or platforms or usages that show that in practice this can cause problems. Can anyone provide some?

推荐答案

传统上,编译器将未定义行为"视为不检查各种类型错误的借口,而只是让它无论如何发生".但是当代编译器开始使用未定义的行为来指导优化.

Classically, compilers treated "undefined behavior" as simply an excuse not to check for various types of errors and merely "let it happen anyway." But contemporary compilers are starting to use undefined behavior to guide optimizations.

考虑这个代码:

int table[5];
bool does_table_contain(int v)
{
    for (int i = 0; i <= 5; i++) {
        if (table[i] == v) return true;
    }
    return false;
}

经典编译器不会注意到您的循环限制写入不正确以及最后一次迭代读取数组的末尾.无论如何,它只会尝试读取数组的末尾,如果超过数组末尾的值恰好匹配,则返回 true.

Classical compilers wouldn't notice that your loop limit was written incorrectly and that the last iteration reads off the end of the array. It would just try to read off the end of the array anyway, and return true if the value one past the end of the array happened to match.

另一方面,后经典编译器可能会执行以下分析:

A post-classical compiler on the other hand might perform the following analysis:

  • 循环的前五次,函数可能返回true.
  • i = 5 时,代码执行未定义的行为.因此,i = 5 的情况可以被视为不可达.
  • 情况i = 6(循环运行到完成)也是不可到达的,因为为了到达那里,你首先必须做i = 5,我们已经显示无法访问.
  • 因此,所有可访问的代码路径都返回 true.
  • The first five times through the loop, the function might return true.
  • When i = 5, the code performs undefined behavior. Therefore, the case i = 5 can be treated as unreachable.
  • The case i = 6 (loop runs to completion) is also unreachable, because in order to get there, you first have to do i = 5, which we have already shown was unreachable.
  • Therefore, all reachable code paths return true.

然后编译器会将这个函数简化为

The compiler would then simplify this function to

bool does_table_contain(int v)
{
    return true;
}

另一种看待这种优化的方式是编译器在心理上展开了循环:

Another way of looking at this optimization is that the compiler mentally unrolled the loop:

bool does_table_contain(int v)
{
    if (table[0] == v) return true;
    if (table[1] == v) return true;
    if (table[2] == v) return true;
    if (table[3] == v) return true;
    if (table[4] == v) return true;
    if (table[5] == v) return true;
    return false;
}

然后它意识到table[5]的求值是未定义的,所以过了那个点的所有东西都是不可达的:

And then it realized that the evaluation of table[5] is undefined, so everything past that point is unreachable:

bool does_table_contain(int v)
{
    if (table[0] == v) return true;
    if (table[1] == v) return true;
    if (table[2] == v) return true;
    if (table[3] == v) return true;
    if (table[4] == v) return true;
    /* unreachable due to undefined behavior */
}

然后观察所有可达的代码路径返回true.

and then observe that all reachable code paths return true.

使用未定义行为来指导优化的编译器会发现,通过 being_a_bad_boy 函数的每个代码路径都会调用未定义行为,因此 being_a_bad_boy 函数可以简化为

A compiler which uses undefined behavior to guide optimizations would see that every code path through the being_a_bad_boy function invokes undefined behavior, and therefore the being_a_bad_boy function can be reduced to

T& being_a_bad_boy()
{
    /* unreachable due to undefined behavior */
}

这个分析然后可以反向传播到being_a_bad_boy的所有调用者:

This analysis can then back-propagate into all callers of being_a_bad_boy:

void playing_with_fire(bool match_lit, T& t)
{
    kindle(match_lit ? being_a_bad_boy() : t);
} 

由于我们知道 being_a_bad_boy 由于未定义的行为而无法访问,因此编译器可以得出结论,match_lit 绝不能为 true,从而导致

Since we know that being_a_bad_boy is unreachable due to undefined behavior, the compiler can conclude that match_lit must never be true, resulting in

void playing_with_fire(bool match_lit, T& t)
{
    kindle(t);
} 

现在无论火柴是否点燃,一切都在燃烧.

And now everything is catching fire regardless of whether the match is lit.

您可能不会在当前一代编译器中看到太多这种未定义行为引导的优化,但就像 Web 浏览器中的硬件加速一样,它开始变得更加主流只是时间问题.

You may not see this type of undefined-behavior-guided optimization in current-generation compilers much, but like hardware acceleration in Web browsers, it's only a matter of time before it starts becoming more mainstream.

这篇关于是否存在取消引用(但不使用)空指针以进行空引用的平台或情况会表现不佳?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆