当前位置: 首页 > 知识库问答 >
问题:

为什么增强的GCC6优化器会破坏实用的C++代码?

吕英才
2023-03-14

GCC6有一个新的优化器特性:它假设this总是不为空,并基于此进行优化。

值范围传播现在假定C++成员函数的this指针是非空的。这消除了常见的空指针检查,但也破坏了一些不一致的代码基(如Qt-5、Chromium、KDevelop)。作为临时解决办法,可以使用-fno-delete-null-pointer-checks。使用-fsanitize=undefined可以识别错误的代码。

变更文档明确指出这是危险的,因为它破坏了数量惊人的频繁使用的代码。

为什么这个新的假设会破坏实际的C++代码?是否有一些特定的模式,让粗心或不知情的程序员依赖于这种特定的未定义的行为?我无法想象有人会在(this==NULL)的情况下编写,因为这太不自然了。


共有1个答案

拓拔迪
2023-03-14

我想一个需要回答的问题是为什么好心的人会首先开出支票。

最常见的情况可能是,如果您有一个类是自然发生的递归调用的一部分。

如果你有:

struct Node
{
    Node* left;
    Node* right;
};
void traverse_in_order(Node* n) {
    if(!n) return;
    traverse_in_order(n->left);
    process(n);
    traverse_in_order(n->right);
}
void Node::traverse_in_order() {
    // <--- What check should be put here?
    left->traverse_in_order();
    process();
    right->traverse_in_order();
}

在C++早期(标准化之前),强调成员函数是函数的语法糖,其中this参数是隐式的。代码是用C++编写的,转换成等效的C并进行编译。甚至有明确的例子表明,将this与null进行比较是有意义的,原始的Cfront编译器也利用了这一点。因此,从C背景来看,检查的明显选择是:

if(this == nullptr) return;      

注意:Bjarne Stroustrup甚至提到this的规则多年来在这里发生了变化

这在许多编译器上工作了很多年。当标准化发生时,这就改变了。最近,编译器开始利用调用成员函数的优势,其中thisnullptr是未定义的行为,这意味着该条件始终为false,编译器可以自由地忽略它。

这意味着要对此树进行任何遍历,您需要:

>

  • 在调用traverse_in_order之前进行所有检查

    void Node::traverse_in_order() {
        if(left) left->traverse_in_order();
        process();
        if(right) right->traverse_in_order();
    }
    

    这意味着还要在每个调用站点检查是否可以有空根。

    这意味着您编写的是旧的C风格代码(可能是静态方法),并将对象显式地作为参数调用它。例如,您又回到了在调用站点编写node::traverse_in_order(node);而不是node->traverse_in_order();

    我相信以符合标准的方式修复这个特定示例的最简单/最整洁的方法是实际使用一个哨兵节点,而不是nullptr

    // static class, or global variable
    Node sentinel;
    
    void Node::traverse_in_order() {
        if(this == &sentinel) return;
        ...
    }
    

    前两个选项似乎都不吸引人,虽然代码可以逃过这一劫,但它们使用this==nullptr编写了错误的代码,而不是使用正确的修复程序。

    我猜这就是这些代码库中的某些代码库如何演变为具有this==nullptr检查的原因。

  •  类似资料:
    • 问题内容: 在我的本地路由http:// localhost:9000 /#/ deviceDetail /中, 我有一个控制器来管理该视图。在进入该视图之前,我将一些变量设置为( 例如)。 一旦进入该视图,我就可以访问仪表盘属性,但是例如当我用键刷新页面时,该属性仪表盘就会丢失。 我试图将localSave变量保存,但是该方法遇到了循环引用问题。 有什么技巧可以解决吗? 问题答案: Angula

    • 下面是两张PNG图片: 从视觉上看,它们是完全一样的--唯一的区别是一个在某些像素中有半透明的背景(你可以下载图像来检查)。 但是当我在JavaFX节点上使用这些图像作为图像光标时,我得到了以下结果: 在与问题搏斗了一会之后,我发现了解释这种差异--混合模式的算法: > “预期”的方法(例如,您可以在此浏览器中看到)是取每个通道的值之和,并用alpha值加权:。 “JavaFX游标”给出了不同的公

    • 问题内容: 谁能告诉我Java +5中Enhanced for loop和Iterators的优点是什么? 问题答案: Stephen Colebourne(Joda- Time,JSR-310等)对优点和缺点进行了很好的总结,针对每个循环迭代控制建议进行了增强,以在Java 7中进行扩展: 功能概要: 扩展Java 5 for-each循环,以允许访问循环索引(无论是第一次还是最后一次迭代),并

    • 问题内容: 实际上,我已经找到导致问题的原因。我的问题是为什么现在加入到你的断? 原始问题 对我来说,最简单的CSS任务似乎失败了:不保持元素相对于视点的位置。考虑以下样式表: 首次加载页面时,定位正确。但是,视口的任何更改(例如滚动或调整大小)都不会影响元素的位置。可以这么说,它无法使其位置适应新的视口。 足够奇怪的是,该站点显示了如何工作,实际上在我的浏览器中正常工作,没有任何问题! 因此,问

    • 我经常遇到这种情况。乍一看,我认为,“这是糟糕的编码;我正在执行一个方法两次,必然会得到相同的结果。”但想到这里,我不得不怀疑编译器是否像我一样聪明,并能得出相同的结论。 编译器的行为是否取决于 方法的内容?假设它看起来像这样(有点类似于我现在的真实代码): 除非对这些对象来自的任何存储进行处理不当的异步更改,否则如果连续运行两次,肯定会返回相同的内容。但是,如果它看起来像这样(为了论证而无意义的

    • 问题内容: 我是使用python编程的新手,但我尝试使用分隔符并结束打印,但仍给我语法错误。 我正在使用python 2.7。 这是我的代码: 这是错误: 问题答案: 首先,必须是脚本中的第一行代码(除了下面提到的一些例外)。第二,正如其他答案所说,您现在必须用作函数。这就是重点;将 功能 从Python 3带入Python 2.6+。 语句必须位于文件的顶部,因为它们会更改语言的基本内容,因此编