有没有一个平台或情况下,解引用(但不使用)一个空指针,使一个null引用会表现不好? [英] Is there a platform or situation where dereferencing (but not using) a null pointer to make a null reference will behave badly?

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

问题描述

我目前正在使用一个使用

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

来引用一个T,未定义的行为,特别注意到不被标准支持,但它不是一个未知的模式。



我很好奇,如果有任何示例或平台或用法, 在实践中,这可能会导致问题。任何人都可以提供一些?

解决方案

经典地,编译器将未定义的行为视为不检查各种类型的错误而只是让它发生。但当代编译器已开始使用未定义的行为来指导优化



请考虑这个代码:

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

经典编译器不会注意到您的循环限制写入不正确,最后一次迭代读取数组的结尾。它只是尝试读取数组的结尾,如果数组的末尾的值发生匹配,则返回 true



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




  • 前五次通过循环,函数可能返回 true

  • i = 5 ,代码执行未定义的行为。因此, i = 5 可被视为不可达。

  • 案例 i = 6 (循环运行到完成)也是不可达的,因为为了到达那里,你首先必须做 i = 5 ,我们已经显示

  • 因此,所有可到达的代码路径都返回 true



然后编译器将此函数简化为

  bool does_table_contain(int v)
{
return true;
}

另一种查看这种优化的方法是编译器精心地展开循环:

  bool does_table_contain(int v)
{
if(table [0] == v)return真正;
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] 未定义,因此超过该点的一切都是无法访问的:

  bool does_table_contain 
{
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;
/ *由于未定义的行为无法到达* /
}

所有可达的代码路径返回 true



使用未定义的行为来指导优化的编译器将看到每个代码路径通过 being_a_bad_boy 函数调用未定义的行为,因此 being_a_bad_boy 函数可以减少为

  T& were_a_bad_boy()
{
/ *由于未定义的行为无法访问* /
}

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

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

因为我们知道 being_a_bad_boy 由于未定义的行为而无法访问,则编译器可以得出结论: match_lit 不能为 true p>

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

现在无论比赛是否发亮, p>

在当前代编译器中可能看不到这种类型的未定义行为引导优化,但是像Web浏览器中的硬件加速,它只是时间问题变得更主流。


I'm currently using a library that uses code like

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

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.

Consider this code:

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

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:

  • 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;
}

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 */
}

and then observe that all reachable code paths return true.

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 */
}

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);
} 

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.

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.

这篇关于有没有一个平台或情况下,解引用(但不使用)一个空指针,使一个null引用会表现不好?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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