【问题标题】:Using deleted variable - is it depending on compiler? [duplicate]使用已删除的变量 - 是否取决于编译器? [复制]
【发布时间】:2017-12-26 01:51:58
【问题描述】:

以下代码用于从排序列表中删除重复项。它在我的电脑上运行正常。我的问题是关于使用

head = head->下一个;

之后

删除头部;

在下面。是否违法?这些代码在我的编译器上生成正确的结果。它会依赖于编译器吗?还是符合 C++11 标准?

struct ListNode {
    int val;
    ListNode *next;
    explicit ListNode(int x) : val(x), next(nullptr) { }
};


class Solution {
public:
    ListNode* deleteDuplicates(ListNode* head) {
        if (head && (head->next = deleteDuplicates(head->next)) && head->next->val == head->val){
            delete head;
            head = head->next;
        }
        return head;
    }
};


int main() {
    ListNode *h = new ListNode(0);
    auto cur=h;
    cur->next= new ListNode(1);
    cur=cur->next;
    cur->next= new ListNode(2);
    cur=cur->next;
    cur->next= new ListNode(2);
    cur=cur->next;

    Solution sol;
    auto xx=sol.deleteDuplicates(h);
    return 0;
}

【问题讨论】:

  • 您正在输入未定义的行为
  • 完全未定义。内存中那个位置的数据一删除就是垃圾。
  • 但是我的编译器没有警告...
  • 警告您注意所有可能的 UB 不是您的编译器的责任。如果可以这样做,我们几乎不会经常出现运行时错误。
  • @drombe -- 更改编译器选项,例如优化,您可能会看到不同的故事。底线是行为未定义。

标签: c++ c++11


【解决方案1】:

不,这不是非法。当您尝试访问最近为 deleted 且未分配新值的指针时,它是 Undefined Behaviour。请记住,UB 的结果可能一开始看起来是正确的,但是当它们突然“停止工作”时并没有指定(定义)。

编辑:

既然有关于编译器是否应该警告我们UB的问题,让我们看一下这个例子:

int main()
{
    std::array<int, 5> arr = {1, 2, 3, 4, 5};
    int position;
    std::cin >> position;
    std::cout << arr[position];
}

我们只是输入一个数字并输出数组中该位置的值。看起来像一个简单的程序,但是当position &gt; 4position &lt; 0 时会发生什么?然后我们访问了一个无效的索引 (Array access out of bounds),导致未定义的行为。

编译器应该警告我们吗?也许吧,但是想象一下一个简单的代码可以产生多少警告。也许如果我们添加一些检查然后尝试访问数组,警告可能会消失,但请记住,这是一个非常简单的示例。编译器并不总是可以预测何时可能发生 UB,因此它们通常不会警告我们。

【讨论】:

  • 应该有警告吗?
  • 没有。编译器不需要警告未定义的行为,因为导致它的某些(大多数)情况非常难以预测
【解决方案2】:

不,这不是“非法的”。根据 C++ 标准,行为(取消引用 deleted 指针)是未定义的,当行为未定义时,编译器根本不需要发出任何诊断。

当行为未定义时,结果可能会因编译器、编译器标志的选择(例如优化设置)而异,仅在程序的某些输入而不是其他输入时出现,或者随着月相而异(例如,因为代码导致程序访问系统时钟)。

在简单的情况下,编译器可能能够检测到问题并发出警告。大多数现代编译器默认配置为不这样做(遗憾的是,作为程序员游说的历史结果,他们需要生成在没有警告的情况下编译的代码,并且更关心该指标而不是他们的代码是否按要求工作),但可以强制使用编译选项(例如警告标志)这样做。

在复杂的情况下,编译器根本无法检测到未定义行为的实例。

许多未定义的行为来自边缘情况 - 未定义的行为仅发生在非常特定的输入值集上,因此无法检测到,除非编译器评估程序在运行时可能收到的所有可能的输入集。

由于编译器优化,会出现另一类未定义的行为 - 编译器会执行大量代码转换(或代码的中间表示)以优化性能(或大小或其他一些指标)。迄今为止,还没有编译器可以发出类似“由于优化器进行了 100 次不同的代码转换,发现在原始源代码的第 20 行和第 40 行之间的某处可能存在一个或多个未定义行为的实例”之类的警告。”即使有可以做到这一点的编译器,这些消息也不会对程序员特别有帮助。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2023-02-06
    • 1970-01-01
    • 1970-01-01
    • 2021-03-13
    • 2023-03-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多