编译器是不是应该正确地将 bool 中的任意非零值解释为 true?

Posted

技术标签:

【中文标题】编译器是不是应该正确地将 bool 中的任意非零值解释为 true?【英文标题】:Should a compiler interpret an arbitrary non-zero value in bool as true correctly?编译器是否应该正确地将 bool 中的任意非零值解释为 true? 【发布时间】:2015-12-30 03:24:31 【问题描述】:

对于真值,布尔值应该转换为 1,否则转换为 0。但是,这并没有说明它们实际上是如何存储在内存中的。如果我将任意非零值存储在布尔值中会发生什么?将这些转换为整数时,标准是否保证正确的行为?

例如,给定以下程序,

#include <string.h>

int main()

  bool b;
  memset( &b, 123, sizeof( b ) );

  return b;

标准是否保证程序会返回 1?

【问题讨论】:

如果你在大端 CPU 上运行它,你可能会返回 0 @mark: memset 将所有字节设置为相同的值(本例中为 123/) “标准是否保证...时行为正确” - 标准总是保证正确行为根据定义 ,因为当它定义行为时,任何兼容的实现都必须提供,而当它没有定义行为时,任何行为都是正确的。您在“应该使用布尔值”中的领先意味着您将 int1 视为“正确”,但这是虚假的,因为 memset 会导致未定义的行为。 FWIW, Footnote 48) of 3.9.2/1 [basic.compound] 说“以本国际标准描述为“未定义”的方式使用bool 值,例如通过检查值一个未初始化的自动对象,可能会导致它的行为就好像它既不是true 也不是false。”,因此推断来自truefalse 的转换应该如何映射到10已经存在缺陷,因为在调用未定义的行为之后您可能还没有开始。 @TonyD 是的,我花了超过 20 分钟的时间来拼凑我在答案中使用的信息。我只是对现有的答案感到恼火。这是一个有趣的问题,其中没有一个答案实际上试图支持他们的立场。 【参考方案1】:

不,在 memset 之后从该 bool 读取是(至少,见下文)未指定的行为,因此无法保证将返回什么值。

可能会证明在特定架构中,bool 的值表示仅包含高位位,在这种情况下,通过在 bool 的字节上广播 123 产生的值会变成false的代表。

C++ 标准没有指定表示值truefalse 的实际位模式是什么。实现可以使用bool 的对象表示中的任何或所有位——它必须至少是一个字节,但可能更长——并且它可以将多个位模式映射到相同的值:

§3.9.1 [basic.fundamental]/1:

…对于窄字符类型,对象表示的所有位都参与值表示。对于无符号窄字符类型,值表示的每个可能的位模式代表一个不同的数字。这些要求不适用于其他类型。

同一部分的第 6 段要求 bool 类型的值是 truefalse,但脚注指出,面对未定义的行为,bool“可能表现得好像它是不真也不假。” (这显然是在未定义行为的范围内;如果程序表现出 UB,则对其执行没有任何要求,即使在 UB 被证明之前也是如此。)

标准中的任何内容都不允许对窄字符数组以外的对象使用低级内存复制操作,除非对象可以简单地复制并且对象表示通过将其复制到缓冲区来保存并稍后恢复通过将其复制回来。覆盖对象表示中任意字节的 C 库函数的任何其他使用都应该由未定义行为的一般定义来定义(“[标准] 省略了任何明确的行为定义”)。但我不得不同意没有明确声明 memset 是 UB,因此我将解决未指定的行为,这似乎很清楚,因为 bool 的表示肯定是未指定的。

【讨论】:

好吧,FWIW,gcc 和 clang 都让它返回 1,在这种情况下我会觉得很奇怪,因为这对我来说似乎是一项额外的工作。 @dragonroot:允许未定义的行为这样做。 :) 似乎是一个很好的答案,但我会更了解为什么这变得未定义。 你能给出一个标准的引用,它是 UB 吗? C++ 标准中没有出现“陷阱表示”一词 @rici 是的,我也没有看到任何确定的东西

以上是关于编译器是不是应该正确地将 bool 中的任意非零值解释为 true?的主要内容,如果未能解决你的问题,请参考以下文章

一种循环冗余校验算法,它对具有特定非零值的尾随字节数不变

TSQL - 列中所有非零值的平均值

为啥 Keras Dropout 中的非零值会发生变化?

为熊猫中的每一列获取非零值

用先前的非零值替换向量中的所有零

MPAndroidChart如何让y轴以非零值开始?