【问题标题】:C++ - Bool to int conversion bugC++ - Bool 到 int 的转换错误
【发布时间】:2018-08-20 09:18:59
【问题描述】:

我的代码中有一个看起来像这样的函数:

bool foo(ObjectType& object){

    //code

    const float result = object.float_data + object.bool_data * integer_data;

    //code

}

在寻找错误时,我发现result 有时具有不正确的值,原因是有时bool*integer 被计算为255*integer 而不是1*integer

C++ 说在整数上下文中bool 被转换为零或一,所以我不明白这一点。我在代码的其他部分乘以bool,它工作正常。这也是随机的:有时它转换为1,有时转换为255。调试器还分别显示true255。当这个错误发生时,它总是发生在那个执行中。重新编译代码没有效果,它仍然随机发生。

【问题讨论】:

  • 您能否发布重现确切问题的代码(包括main 函数)?
  • 可以是代码中其他地方UB的体现。
  • @rubenvb 不,程序是 50K+,但我会尝试用更小的程序来重现它。
  • @Someprogrammerdude 我发现 QA 转储对这个问题非常粗鲁。 1. 问题简洁明了(“为什么 C++ 标准保证的这种行为不会发生在我的代码中”)和 2. 提问者显然知道如何调试程序 - 结论就在问题中。如果你的观点是“你能在一个更小的程序中重现这个”,那么就这么说吧。仅仅因为某人没有花费数小时(或没有成功)尝试重现问题而可能有一个简单的答案,就将所有“学习如何提问”链接转储给某人,这是可怕的。
  • @MaxLanghof 我的意图不是粗鲁,只是为了欢迎新用户访问该站点并提供良好的链接以阅读有关如何成为高效成员以及如何编写好问题的信息。最后一点对于新手来说非常重要,因为即使这个问题对于一个全新成员的第一个问题来说不是很糟糕,但它在目前的形式下也是无法回答的。我们真正能做的就是给出可能的解决方法和毫无意义的猜测和推测。我们不能做的是给出一个关于为什么 OP 有这个问题的明确答案。

标签: c++ gcc c++17


【解决方案1】:

根据C++17 7.14 Boolean conversions,将非布尔值强制转换为布尔值是标准转换过程的一部分,这意味着它实际上是在为布尔值分配一些值时完成的:

算术、无范围枚举、指针或指向成员类型的指针的纯右值可以转换为bool 类型的纯右值。零值、空指针值或空成员指针值转换为false;任何其他值都将转换为true。对于直接初始化 (11.6),std::nullptr_t 类型的纯右值可以转换为 bool 类型的纯右值;结果值为false

保证布尔值为零或一,例如,如果您初始化它,或者您以如下方式初始化它它被视为非布尔值:

void someFunction() {
    bool xyzzy;              // Set to some arbitrary value.
    memcpy(&xyzzy, "x", 1);  // Very contrived, wouldn't pass my
                             //    own code review standards :-)
}

所以我要查看的第一件事是确保您正在正确初始化/分配它。

事实上,我现在非常很少在没有明确将其初始化为某物的情况下引入一个变量。即使它后来在使用前进行了更改,我也宁愿依靠编译器来解决这个问题,并在它认为有用时进行优化。


但是您可以通过使用预期的布尔值来解决此问题。换句话说,作为真理的存储库,而不是将它们评估为整体价值。试试这个:

const float result = object.float_data + (object.bool_data ? integer_data : 0);

无论bool_data 是:

  • 正确初始化的布尔值;
  • 一个未正确初始化的布尔值(尽管你的结果会令人怀疑——我的意思是你永远不会得到超过一个integer_data 添加到你的float_result);或
  • 一个整数值被处理为布尔值(尽管这无论如何都是个坏主意)。

【讨论】:

  • 修复它不是问题,我想知道为什么会这样。是编译器错误还是 IDE 或我的代码有问题。
  • 你的 IDE 不产生任何代码,所以它不能对此负责,你的编译器会。
  • @paxdiabolo:这样的代码通常用在无分支编程中,这在GPU代码中很常见。
  • 或者,在此处不使用bool 来修复它。而是使用 0 或 1 的 int 值。
  • @Khouri:那么您遇到的问题与 OP 发现的问题完全相同。您不应该将int 用于基本上是真/假值的东西。如果你这样做了,并且有人将它设置为 2,那么你的计算就会被搞砸。
【解决方案2】:

您应该检查bool 的设置位置。值得注意的是,如果内存不完全是0,C++ 标准保证任何作为bool 值访问的内存都将转换为1 - 它只保证bool 的值true 将转换为1。正如here 所述,bool 的值可能既不是true 也不是false,这是未定义行为 的结果。这可能看起来很明显(毕竟是 UB),但也令人惊讶(显然即使按照标准作者的标准)。

这是一个实际的演示,使用memcpy

#pragma pack(1)
struct ObjectType
{
    float float_data = -3.0f;
    bool bool_data = false;
    int integer_data = 2;
};

volatile unsigned char x = 7;

float test()
{
    // Don't actually do this, it is for demonstration only.
    std::array<unsigned char, sizeof(ObjectType)> data = { 0, 0, 0, 0, /**/ x, /**/ 2, 0, 0, 0 };

    ObjectType obj;
    memcpy(&obj, data.data(), sizeof(obj));

    return foo(obj);
}

http://coliru.stacked-crooked.com/a/0221a82a6d35e18b http://coliru.stacked-crooked.com/a/982ff8e4d7503f08

确实,inspecting the binary 表明foobool 字面解释为整数并将其与gcc 相乘。我会得出结论,gcc 通过只存储10 来实现标准一致性,只要它存储到bool,因此表现好像 true 只转换为1(只要您从不强制 bool 进入不同于 truefalse 的状态)。

【讨论】:

  • 谢谢!出于性能原因,gcc 这样做是有道理的,如果它没有初始化,那么它是 UB 使用该值,AFAIK。
  • 这个答案非常令人困惑/误导。初始化的 bool 始终完全是 truefalse,它被提升为始终完全是 10。它可以未初始化,但不能像...2.
  • @Barry 您是否将我的回答中的memcpy 示例视为未初始化 bool 的情况?这对我来说似乎是用词不当。而且由于memcpy 基本上是唯一的非混叠转换方式,例如一个网络数据包到结构化数据,理论上你可以在不同系统之间发送包含bool状态的数据包时遇到这个确切的问题。
  • @MaxLanghof 您只是在展示与 OP 明显问题无关的未定义行为。是的,生成的程序集只是乘以 7,因为您将无效的对象表示复制到其中。这并没有改变这样一个事实,根据标准,bool 不能是7。你刚刚打破了先决条件。
  • @Barry 重点是练习标准指出的内容here。现在,显然 UB 是 UB,因此该注释在技术上是多余的,但该注释仍然存在这一事实表明,即使是标准作者也认为这是 UB 的一个非常不直观的结果,bool 既不能是 true 也不能是 @ 987654363@。我已经编辑了我的帖子,明确提到需要 UB 才能达到这种状态。
【解决方案3】:

解决了。 bool 值未初始化,因此有时它包含 255(rubish)。我假设在整数上下文中使用 bool 时将其转换为 0 或 1,而不是在从值设置时将其转换为 0 或 1。 (这完全是我的错误,很抱歉浪费你的时间。)

用代码总结一下:

float float_data = 50.0f;
int integer_data = 30;
bool bool_data;

float result;

bool_data = 255;                                //OK
reinterpret_cast<int&>( bool_data) = 250;       //NOT OK

result = float_data + (bool_data ? integer_data : 0);       //OK
result = float_data + bool_data * integer_data;             //NOT OK.
result = float_data + !!bool_data * integer_data;           //NOT OK.
result = float_data + (bool_data == true) * integer_data;   //NOT OK.

“OK”,表示仍然错误,但至少为 0 或 1

【讨论】:

  • 嘿,正如我在回答中提到的那样,您遇到的确切示例是 standard provides...
【解决方案4】:

我认为你应该使用下面的代码然后执行它

bool foo(ObjectType& object){
    //code
    const float result = object.float_data + int(object.bool_data) * int(integer_data);
    //code
}

【讨论】:

  • 我认为这不会有什么不同
  • 这可能会有所作为。但由于 OP 的代码可能因为 UB 而损坏,我们无法确定。
  • b * iint(b) * int(i) 做同样的事情。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-07-19
  • 2023-04-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-11-29
相关资源
最近更新 更多