【问题标题】:C++ casting bool to int - standardC++ 将 bool 转换为 int - 标准
【发布时间】:2016-11-29 16:20:28
【问题描述】:

我很感兴趣,无论标准在将 bool 类型转换为 integer 类型后对它的可能值进行了说明。

例如以下代码:

#include <iostream>
using namespace std;

int main() {
    bool someValue=false;
    *((int*)(&someValue)) = 50;

    cout << someValue << endl;

    return 0;
}

即使它被强制存储值 50 也会打印 1。标准是否指定了任何关于它的内容?或者编译器是否为 bool 类型生成一些方法:

operator int(){
    return myValue !=0 ? 1 : 0;
}

还有为什么会像下面这样投射:

reinterpret_cast<int>(someValue) = 50;

错误禁止

错误:从类型“bool”到类型“int”的无效转换

(以上我使用 GCC 5.1 编译器。)

【问题讨论】:

  • AFAIK 您的代码显示 UB,因为它违反了严格的别名规则。
  • 在写入someValue 时,您正在跨越bool 的内存要求
  • 你为什么要尝试做这些事情?
  • 此代码不是bool 转换为int。那将是(int)someValue
  • 所以改变你的标题。

标签: c++ casting boolean


【解决方案1】:

你使用它的方式展示了 UB,因为你写在 bool 变量的边界之外并且你违反了严格的别名规则。

但是,如果您有一个 bool 并希望将其用作 int(这通常发生在您希望根据某些条件索引到数组中时),标准要求 true bool 转换为 @987654322 @ 和false bool 无论如何都会转换成0(显然不包括UB)。

例如,这保证输出52,只要should_add == true

int main(){
    int arr[] = {0, 10};
    bool should_add = 123;
    int result = 42 + arr[should_add];
    std::cout << result << '\n';
}

【讨论】:

    【解决方案2】:

    *((int*)(&amp;someValue)) = 50; 这一行至少是非标准的。该实现可以为 bool (例如 1 或 2 个字节)使用比 int (例如 4 个字节)更小的等级。在这种情况下,您可能会写过去的变量可能会擦除其他变量。

    无论如何,正如您在评论中所说,由于严格的别名规则,几乎所有通过强制转换指针的访问都可以被编译器视为未定义行为。唯一几乎合法的(对于严格的别名规则)是:

    *((char *) &someValue) = 50; 
    

    在小端系统上,并且

    *(((char *) &someValue) + sizeof(bool) - 1) = 50; 
    

    在一个大端(字节访问仍然没有被禁止)。

    无论如何,由于标准没有指定 bool 的表示形式,因此直接在 bool 中写入某些内容可能会导致 true 或 false,具体取决于实现。例如,一个实现可以只考虑最低级别的位(如果 val&1 为 1,则为 true,否则为 0),另一个实现可以考虑所有位(对于任何非 0 值都为 true,仅 0 为 false)。标准所说的唯一内容是 0 的 转换 会导致错误,而非 0 会导致正确。


    但是标准规定是从 bool 到 int 的转换:

    4.5 整体促销 [conv.prom]

    ...
    bool 类型的纯右值可以转换为 int 类型的纯右值,false 变为 0 和 true 合而为一。

    所以这完全解释了显示布尔值只能给出 0 或 1 - 即使前面的操作调用了 UB,任何事情都可能在这里发生,包括这个显示

    You invoked Undefined Behaviour - shame on you
    

    【讨论】:

    • 对不起,与字节序相关的东西没有意义。任何一个操作在任何系统上都是有效的,并且都结束了布尔对象的生命周期。而如果`sizeof(bool>2), you can also write a char`到中间的字节。
    • 另外,由于编写char 会结束bool 的生命周期,因此您不能说它是truefalse。该内存现在拥有char,因此其值为CHAR_MIN...CHAR_MAX。尝试将其读取为 bool 再次是严格别名规则的未定义行为。
    • 反之亦然,特别允许memcpy 之类的东西起作用。 IOW,您可以将 bool 复制到 bool,使用 char[sizeof(bool)] 作为中间表示。
    • @MSalters:我认为可以通过将其地址别名为char * 来从任何对象读取表示的字节,并希望它也可以用于更改对象的字节.当然结果取决于实现的类型的表示,但我希望它至少是合法的并且不会自动结束对象生命周期
    • 嗯,第一部分是真的。毕竟这是memcpy 的后半部分。主要问题是如何描述 POD 对象的生命周期。使用 memcpy,目标的旧 POD 对象在其内存被memcpy 覆盖时会死掉。在那个对象死了之后,它的类型就变得毫无意义了。但是,仍然可能存在引用该存储位置的表达式,并且 那些 也有一个类型。例如。 *pMyBool。现在,如果你 memcpy 一个 bool 而不是另一个,像 *pMyBool 这样的表达式仍然有效 - 那里有一个 bool,现在仍然存在,它恰好是另一个实例。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-01-14
    • 1970-01-01
    • 2017-02-24
    • 1970-01-01
    • 2015-12-01
    • 2017-07-17
    • 2023-03-27
    相关资源
    最近更新 更多