【问题标题】:What is the type promotion rule for a parenthesized sub-expression?带括号的子表达式的类型提升规则是什么?
【发布时间】:2021-01-28 01:09:14
【问题描述】:

假设一个整数表达式由多个无符号整数类型uint16_tuint32_t 组成。该表达式包含一个带括号的子表达式,其中所有元素的类型均为uint16_t

在计算子表达式之前,是否应该将括号内的子表达式中的元素提升为uint32_t

例如:

#include <stdint.h>
#include <stdio.h>

int main(void)
{
    uint16_t a16 = 0x2000;
    uint16_t b16 = 0x3000;
    uint32_t c32 = 0x00000001;
    uint32_t d32;

    // Should (a16 * b16) be evaluated to 0x06000000, or to 0x0000?
    // Should d32 be evaluated to 0x06000001, or to 0x00000001?
    d32 = c32 + (a16 * b16);

    printf("d32=0x%08x\n", d32);

    return 0;
}

ideone 在线编译器中尝试此操作建议在乘法之前将 a16b16 提升为 uint32_t。这是 C 标准规定的吗?为什么不在括号内计算为uint16_t

【问题讨论】:

  • 请注意括号是无关紧要的,因为正常的优先规则使它首先对乘法进行分组。
  • 假设您的d32 = b16 * (a16 + a16); 略有不同,括号是不是很明显,它们是为了确保首先执行加法,而不是将总和限制为 16 位?
  • @Barmar - 这个问题比这个特定的乘法示例更普遍。我可以把它变成一个溢出的添加,并用 c32 术语交换位置。
  • @WeatherVane - 不,实际上并不明显。可以认为计算是按顺序进行的,并且在每一步,编译器都会应用必要的提升规则。
  • 我去年修复的最喜欢的错误实际上是bonus = 1/2 * salary;,其中salarydouble 类型。这是了解隐式类型转换一步一步发生的试金石。

标签: c types integer-promotion type-promotion


【解决方案1】:

所有比int窄的类型在乘法之前都被提升为int

因此,如果您有一个 32 位 2 的补码 int,那么结果实际上是两个 int32_t 类型的乘积。

表达式中的括号对这种隐式类型转换或最终结果都没有影响。

【讨论】:

  • 谢谢。因此,您是说所有类型提升都是在评估表达式或其任何部分之前执行的。真的吗?你能指出标准中的位置(或其他一些有名的参考)吗?
  • @ysap:乘法是二元运算符。参数的类型在乘法之前经过隐式转换阶段。加法也是如此。运算符优先级和关联性是硬连线到语言语法中的,因此很难指向除此之外的特定引用。
  • 这个问题与操作数优先级或特定(乘法)运算无关。我将编辑问题以强调这一点。
【解决方案2】:

任何时候小于int 的类型用于表达式时,它首先被提升为intunsigned int,无论表达式的上下文如何。

这在C standard 的第 6.3.1.1p2 节中有详细说明:

以下内容可以在表达式中使用int 或 可以使用unsigned int

  • 具有整数类型(intunsigned int 除外)的对象或表达式,其整数转换等级小于 大于等于intunsigned int的等级。
  • _Boolintsigned intunsigned int 类型的位字段。

如果int 可以表示原始类型的所有值(如 受宽度限制,对于位域),值为 转换为int;否则,它被转换为 unsigned int。这些被称为整数促销。所有 其他类型不因整数提升而改变

因此,在您的情况下,a16b16 在应用于任何运算符之前都将被提升为 int,假设 int 大于 uint16_t

请注意,这是整数提升,与通常的算术转换不同。后者规定了在应用运算符之前如何将两个不同类型的操作数转换为公共类型。

【讨论】:

  • 但是uint16_t 的排名是否高于 16 位 1 的补码 int?不错的答案,但比我的要好。
  • 谢谢。 “假设 int 大于 uint16_t” - 这是这里的关键点。我可以将问题改为使用uint32_tuint64_t,或者假设一台具有16 位ints 的机器。然后会发生什么?
  • @ysap 在这种情况下,没有促销活动。所以给定uint32_t a, b;uint64_t c,在表达式(a + b) + c中,a+b的加法是用uint32_t类型完成的,然后结果在加到c之前转换为uint64_t。跨度>
  • 太好了,我刚刚在 ideone 中确认了这一点。我会更新问题。
  • Re 任何时候小于 int 的类型被用作表达式,它首先被提升为 intunsigned int 而不管表达式的上下文“:括号不会出现这种情况, _Generic,数组下标,原型函数的参数不对应于...,递增和递减运算符(需要左值,因此不能容忍值的转换),一元&amp;sizeof,强制转换,整数类型对指针的加法或减法、&amp;&amp;||、简单赋值和逗号运算符。但任何其他时间,当然。
【解决方案3】:

假设一个整数表达式由多个无符号整数组成 类型 uint16_t 和 uint32_t。该表达式包含一个带括号的 子表达式,其中所有元素都是 uint16_t 类型。

括号内的子表达式中的元素是否应该是 在评估子表达式之前提升为 uint32_t?

没有。主要有两种可能:

  1. uint16_tunsigned int 的类型相同。在这种情况下,在评估具有两个 uint16_t 操作数的操作时不会执行任何提升,并且结果的类型为 uint16_t

  2. intuint16_t 宽,因此可以表示uint16_t 可以表示的所有值。在这种情况下,当一个二元运算符的两个操作数都是uint16_t类型时,都被提升为int(肯定和uint32_t不一样),并且运算结果的类型是int

原则上,C 不排除在实践中你永远不会看到的第三种可能性:

  1. uint16_tunsigned int 的类型不同,但大小相同。在这种情况下,您要么升级到 unsigned int(在这种情况下不能与 uint32_t 相同),要么没有升级。

在任何情况下,符合标准的 C 实现都不会将相关操作数提升为 uint32_t,具有两个 uint16_t 操作数的操作也不会产生 uint32_t 结果。

【讨论】:

  • 谢谢。我认为这同意并扩展了@dbush 的回答。
  • 确实同意。它只关注对标准含义的分析,而不是条款本身。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-09-22
  • 2015-07-22
相关资源
最近更新 更多