为什么这种类型的修剪不是未定义的行为?

时间:2018-08-03 10:45:01

标签: c++ undefined-behavior type-punning

这是一个玩具示例,我认为它将调用未定义的行为:

#include <cstdint>
#include <iostream>
#include <vector>

int
main()
{
    std::vector<uint16_t> foo = {0, 0x42F6};
    std::cout << *reinterpret_cast<float*>(foo.data()) << std::endl;
    return 0;
}

我非常确定,取消引用reinterpret_cast的结果将违反严格的别名规则。但是:

$ g++ --version
g++ (Ubuntu 5.4.0-6ubuntu1~16.04.10) 5.4.0 20160609
$ g++ -fstrict-aliasing -Wstrict-aliasing -fsanitize=undefined -std=c++14 -o a a.cpp
$ ./a
123

编译器或UB清理程序没有警告。为什么不呢?

3 个答案:

答案 0 :(得分:6)

  

为什么这种类型的双关不是未定义的行为?

您的前提是错误的。行为未定义。

  

编译器没有警告...为什么不呢?

不需要编译器来警告UB。 有时会在恒星对齐时这样做,但是对于编译器来说,证明UB的存在通常非常昂贵。实际上,如果 是可能的,那么语言规则可能会指定程序格式错误。

  

或UB消毒剂。为什么不呢?

UB消毒剂并不完美。它无法检测到所有UB。考虑提交功能请求以实施此情况的检测-假设尚未请求。

答案 1 :(得分:5)

  

编译器或UB清理程序没有警告。为什么不呢?

这并不意味着您没有未定义的行为。编译器和清理程序可以尽其所能来检测它,但是并不能保证捕获到它的每一次出现。

您可以确定的唯一方法是阅读《标准》并检查您对reinterpret_cast的用法是否定义明确。

答案 2 :(得分:0)

该标准不需要要求实现来定义某些构造的行为这一事实并不意味着旨在适用于各种目的的实现也不应该对其进行定义。许多实现都有各种选择,这些选择会导致编译器一致地对待某些此类构造,以某些方式有时对程序员编写某些类型的代码有用或什至是必需的(尽管标准不需要它) )。

对于您的示例,在某些实现中,使用选项的适当组合进行调用将产生与您所看到的行为完全相同的行为。在许多实现中,所需的选项将包括-fno-strict-aliasing,尽管有些可能还需要其他选项。但是,某些实现,尤其是其中vector<uin16_t>可能未进行32位对齐,并且在加载float而不进行32位对齐时将失败的实现,可能不支持任何选项组合可以定义行为。

但是,即使在不使用(或不支持)这样的选项的情况下,许多实现也会以一种通常(尽管不可靠)与指定和支持此类选项的方式。这可能使得难以确定某个代码段是否可以以无法预测的方式一致地工作,或者有时可以工作,有时甚至失败。