【问题标题】:C99 - why are false and true defined as 0 and 1 and not as ((bool)0) and ((bool)1)?C99 - 为什么将 false 和 true 定义为 0 和 1 而不是 ((bool)0) 和 ((bool)1)?
【发布时间】:2011-07-08 16:02:57
【问题描述】:

偶然发现了一个失败的断言,因为它将 false 与函数的返回类型进行比较,因为函数本身返回了一个布尔值,并且断言不仅检查了值,还检查了返回值的类型以匹配那个为假,以保证返回一个布尔值。 现在的问题是,C99 将 bool 定义为 _Bool 并且 _Bool 甚至不一定与 int 大小相同(实际上,根据我的经验,在当今的大多数平台上,它通常与 unsigned char 大小相同),更不用说相同了type (这实际上是不可能的,因为 _Bool 是 C99 中语言的内置类型),但将 false 和 true 定义为 0 和 1 没有任何类型转换,并且没有类型转换的预处理器定义将默认为 int。 如果 C99 将 false 和 true 定义为 ((bool)0) 和 ((bool)1),那么无论 _Bool 是如何定义的,它们都将始终是 bool 类型。 那么是否有充分的理由将它们始终定义为 int,即使 bool 在该平台上不是 int 或者这只是语言中的一个错误,应该用 C1x 修复?

【问题讨论】:

    标签: c language-design c99 boolean


    【解决方案1】:

    falsetrue 分别定义为整数常量 01,因为这正是 C99 标准在 7.16 节中指定的内容:

    其余三个宏适用于 #if 预处理指令。他们 是

    展开成整数常数1,

    展开成整数常量 0,并且

    EDIT :正如下面的 cmets 所示,我似乎稍微误解了这个问题,我应该提供标准这样指定的原因。我能想到的一个原因是 truefalse 应该可以在 #if 预处理指令中使用(正如标准中提到的那样)。

    ((bool) 0)((bool) 1)#if 预处理指令中不起作用的原因是标准不允许这样做。在 6.10.1 部分它说:

    控制条件包含的表达式应为整数常量表达式 除了:它不应包含演员表;

    【讨论】:

    • 不错的答案,但提供实际引用将使其成为一个很好的答案。
    • @Nemo :代码块包含来自 C99 标准的直接引用,但我可以明确指出它,并将其作为引用(现已修复)。
    • 是的,标准规范当然是原因,为什么符合标准的实现以这种方式定义常量,但这并不能回答问题,如果标准有充分的理由让它们扩展为整数而不是布尔值。
    • @Sander:他的意思是您的回答不会对讨论增加任何内容。 OP 知道它们被定义为整数,他在问为什么做出这个决定。
    • @Kaiserludi:明白。看来我误解了这个问题。我现在也明白@Rob 的意思了。我猜不将它们设为_Bool 类型的原因是因为它们应该可以在#if 预处理指令中使用,正如上面引用的那样。
    【解决方案2】:

    除了已经提到的其他原因,因为只要您对它进行几乎任何操作,_Bool 就会变成int

    例如(_Bool)0 & (_Bool)1的类型是什么?你可能认为表达式的类型为_Bool,但实际上§6.5.10 定义了& 的语义:

    ...通常的算术转换是在操作数上执行的...

    “通常的算术转换”在 C 标准中具有非常特殊的含义。它在 §6.3.1.8 中定义,包括以下内容:

    ...整数提升在两个操作数上执行...

    “整数促销”也是一个定义的术语,来自 §6.3.1.1:

    如果一个int可以表示原始类型的所有值,则将该值转换为一个int;否则,将其转换为无符号整数。这些被称为整数提升。48) 所有其他类型都不受整数提升的影响。

    虽然 C 标准中存在比 int 更窄的类型,但在几乎任何表达式中它们都会自动扩展为 int。再加上布尔运算的结果类型为 int,这使得 int 成为这些文字类型的自然选择。

    【讨论】:

    • 为什么要在两个布尔值上使用二进制 AND?
    • @dascandy:如果两个操作数是返回 _Bool 的函数调用,并且如果函数调用具有您不想因短路而丢失的副作用怎么办?跨度>
    • 没想到。谢谢!好理由。我确实认为它有点简洁,但它会很好用。
    • @jamesdlin:如果你关心函数的副作用,你很可能关心两个函数被调用的顺序。其实我曾经尝试过做一些事情就像你建议的那样,当一个特定的编译器决定颠倒调用的顺序时,这让我很伤心......我现在甚至可以说使用具有副作用的多个函数作为算术的操作数几乎总是有害的/位运算符。
    【解决方案3】:

    首先,虽然_Bool 可能不是int,但要求_Bool 可以接受值0 和1,因此将truefalse 扩展为1 和0 就可以了。

    C99 §6.2.5/2:声明为 _Bool 类型的对象大到足以存储值 0 和 1。

    另外,为了向后兼容,truefalseints 是合理的,因为所有逻辑运算符都返回int

    C99 §6.5.3.3/5:逻辑否定运算符! 的结果如果其操作数的值比较不等于 0 则为 0,如果其操作数的值比较相等则为 1为 0。结果的类型为 int。表达式!E 等价于(0==E)

    C99 §6.5.8/6:每个运算符<(小于)、>(大于)、<=(小于或等于)和如果指定的关系为真,>=(大于或等于)应产生 1,如果为假,则应产生 0。90) 结果的类型为 int

    C99 §6.5.9/3==(等于)和!=(不等于)运算符类似于关系运算符,只是它们的优先级较低。91) 如果指定的关系为真,则每个运算符产生 1,如果为假,则产生 0。结果类型为int。对于任何一对操作数,其中一个关系为真。

    C99 §6.5.13/3&& 运算符在其两个操作数比较不等于 0 时应产生 1;否则,它产生 0。结果的类型为 int

    C99 §6.5.14/3|| 运算符在其任一操作数比较不等于 0 时应产生 1;否则,它产生 0。结果的类型为 int

    最后,正如@Sander De Dycker 所提到的,定义truefalse 的标准将像这样扩展(C99 §7.16/3)。

    【讨论】:

    • 但是您通常会检查逻辑运算符而不使用 true 或 false 并编写诸如“if(foo)”和“if(!foo)”之类的代码(因为实现特定于 TRUE-macro 它只能保证为 !=FALSE,但不为 1,并且如果您编写像“if(foo==true)”这样的代码,那么您可能会意外地养成 TRUE 的习惯)。此外,如果在将逻辑运算符结果与 true/false 进行比较时避免隐式转换是一个问题,那么将这些结果保存到 bool 或检查 bool 与 true/false 会遇到同样的问题,并且最好将标准定义为 bool诠释。
    【解决方案4】:

    所有其他答案都试图使用标准来证明为什么标准以某种方式定义事物的原因,我觉得这并不令人满意。该标准不仅定义了类型,还定义了运算符和预处理器,因此如果 C99 引入了布尔类型,为什么不将所有布尔运算符更改为该类型的值并扩展预处理器以支持布尔类型?

    这样做是可能的,但更复杂的是必要的。标准编写者和编译器编写者只需进行最小必要的更改即可将新的布尔类型添加到语言中要容易得多。由于所有布尔运算仍然计算类型为int,所有 C99 之前的编译器都可以更新以支持 C99,而无需更改所有基本运算符的类型计算代码,标准编写者可以更有信心新的布尔特性并没有意外地对以前正常的部分标准引入不一致。他们需要做的就是确保将“通常的算术转换”应用于_Bool,然后保证其他一切都能正常工作。

    这不是技术原因。这是一个实用的。

    【讨论】:

      猜你喜欢
      • 2012-01-05
      • 2017-02-21
      • 2011-12-16
      • 1970-01-01
      • 1970-01-01
      • 2011-02-09
      • 2012-01-07
      • 1970-01-01
      • 2019-02-09
      相关资源
      最近更新 更多