【问题标题】:Interpreting unsigned char array as bool array将 unsigned char 数组解释为 bool 数组
【发布时间】:2014-08-14 13:05:08
【问题描述】:

考虑以下代码。没问题还是会导致未定义的行为?

#include <iostream>

int main()
{
    {
        unsigned char binary[] = {0, 5, 10};
        bool* x = reinterpret_cast<bool*>(&binary[0]);

        for (unsigned int i = 0; i < 3; ++i)
        {
            std::cout << (x[i] ? 1 : 0) << " ";
        }
    }

    {
        unsigned char b = 255;
        bool* x = reinterpret_cast<bool*>(&b);
        std::cout << (*x ? 1 : 0) << std::endl;
    }

    return 0;
}

使用 gcc 4.6 到 4.8 编译时的输出是

0 5 10 1

但仅限于优化(-O1 等)。 Clang 结果

0 1 1 1

即使有优化。 现在如果将y[i] ? 1 : 0 更改为y[i] ? 2 : 1 gcc 结果是

1 2 2 1.

有什么想法,或者只是因为演员阵容而导致的未定义行为?

【问题讨论】:

  • 部分问题可以改写为:标准是否保证boolchar 具有相同的大小和对齐方式...我相信是的,但我不确定。

标签: c++ gcc casting boolean


【解决方案1】:

标准不保证boolchar 完全兼容(即不保证它们具有相同的大小或对齐方式),说:

bool 类型的值要么是 true 要么是 false

§3.9.1 [basic.fundamental]

它还说:

以本国际标准描述为“未定义”的方式使用bool 值,例如通过检查未初始化的自动对象的值,可能会导致其表现得既不是true 也不是false .

脚注 47 (N3337)

因此,您处于未定义行为的领域。

请注意,由于该标准并未对bool 做出例外规定,因此适用以下规则:

如果程序尝试通过以下类型之一以外的左值访问对象的存储值,则行为未定义:

——对象的动态类型,

——对象动态类型的 cv 限定版本,

——与对象的动态类型类似(定义见 4.4)的类型,

——对象的动态类型对应的有符号或无符号类型,

— 有符号或无符号类型,对应于对象动态类型的 cv 限定版本,

— 一种聚合或联合类型,在其元素或非静态数据成员(递归地包括子聚合或包含联合的元素或非静态数据成员)中包含上述类型之一,

——对象的动态类型的(可能是 cv 限定的)基类类型,

— char 或 unsigned char 类型。

§3.10 [basic.lval]

在这种情况下,对象的类型是 unsigned char,因此尝试通过 bool 左值(通过取消引用 bool * 获得)访问它们会导致 UB。

【讨论】:

    【解决方案2】:

    这违反了strict aliasing rule,我会引用我的回答here

    严格的别名规则使得访问对象是非法的 通过不同类型的指针,尽管通过 char 访问 * 被允许。允许编译器假设不同类型的指针不指向同一个内存并优化 因此。这也意味着代码调用了未定义的行为和 真的可以做任何事。

    标准草案在3.10左值和右值10段中涵盖了这一点:

    如果程序尝试通过以下方式访问对象的存储值 行为是以下类型之一以外的泛左值 未定义:52

    ——对象的动态类型,

    ——对象动态类型的 cv 限定版本,

    ——与对象的动态类型类似(定义见 4.4)的类型,

    ——对象的动态类型对应的有符号或无符号类型,

    — 有符号或无符号类型,对应于动态类型的 cv 限定版本 对象,

    — 在其元素中包含上述类型之一的聚合或联合类型或非静态类型 数据成员(递归地包括子聚合的元素或非静态数据成员) 或包含联合),

    ——对象的动态类型的(可能是 cv 限定的)基类类型,

    — char 或 unsigned char 类型。

    如果这不是问题,则不清楚,尝试将 bool 解释为 char 甚至是有效的,3.9.1 部分 基本类型 说:

    bool 类型的值要么是真要么是假。[...]

    脚注 47 说:

    以本国际标准描述为“未定义”的方式使用布尔值,例如通过检查 未初始化的自动对象,可能会导致其表现得既不是真也不是假。

    【讨论】:

    • 不写内存有关系吗?
    • 参考标准的§3.10 [basic.lval]。它说“如果程序尝试访问存储的值 [...],则行为未定义”。因此无论你是阅读还是写作都无关紧要,编译器不受标准的约束,可以为所欲为。
    • @tauran 没关系,任何访问都是无效的。
    猜你喜欢
    • 2021-12-06
    • 1970-01-01
    • 2013-05-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-04-19
    • 2020-10-11
    • 2012-11-04
    相关资源
    最近更新 更多