【问题标题】:Question on Usual Arithmetic Conversions - GCC Compiler关于通常算术转换的问题 - GCC 编译器
【发布时间】:2019-12-31 09:53:28
【问题描述】:

我正在尝试理解 C 中的隐式数据类型转换。我以为我已经理解了这个主题,但是下面的代码示例仍然让我感到困惑。

具体来说,我之前从 C 标准的草案中阅读了有关常用算术转换和整数提升的信息。

    unsigned short int a = 0;
    printf("\n%lld", (signed int)a - 1);

我正在使用 GCC 进行编译。

unsigned short int 是 2 个字节。 int 是 4 个字节。

当我运行这段代码时,我得到以下结果:4294967295

我期待结果 -1

这是我预期会发生的:

  1. Typecast 优先,- 的 LHS 变为 signed int

  2. - 操作被执行。这里没有发生整数提升或隐式转换,因为 LHS 和 RHS 都已经是signed int。运算结果为 -1,数据类型为 signed int

  3. printf 语句中,值-1 保留在转换为long long int 的过程中,并显示-1 作为结果。

谁能解释一下我的逻辑缺陷在哪里?

【问题讨论】:

  • 编译时使用-Wall-Wextra

标签: c type-conversion undefined-behavior implicit-conversion integer-promotion


【解决方案1】:

在这里你可以认为自己很幸运。 a 是一个 short int ,它经过通常的算术转换为“有符号整数”,即使有强制转换,所以

unsigned short int a = 0;
printf("\n%d", (signed int)a - 1);

unsigned short int a = 0;
printf("\n%d", a - 1);

会有相同的行为,if unsigned short 的所有值都可以在 int 中表示(就像你的情况一样)。转换的结果是int。现在,对于可变参数,应用默认参数提升,并且任何小于int 的整数都将转换为int(如果可表示),否则转换为unsigned int。但是lld 需要一个 8 字节宽的 signed long long int。默认参数提升不会将 int 隐式提升为 long long int

现在是运气部分 - 您确实得到了一个 错误 值。看,由于 行为未定义,您可能已经获得了您期望的值,这一次 - 毕竟它是完全可行的64 位处理器!

【讨论】:

    【解决方案2】:

    在 printf 语句中,值 -1 在转换为 long long int 时保留

    没有发生这样的转换。 printf(函数族)是愚蠢的,需要一个与参数列表类型相对应的格式字符串。

    printf 像普通函数 void f (long long int x) 那样工作,它会强制隐式转换为参数的类型(“按赋值”/“左值转换”)。这将为您提供预期的“符号扩展”。

    值得注意的是,这里还有另一种特殊的隐式转换,称为默认参数提升,它仅适用于可变参数函数和没有原型的函数。

    C17 6.5.2.2/6

    如果表示被调用函数的表达式的类型不包含 原型,整数提升对每个参数执行,参数 将类型 float 提升为 double。这些被称为 default 参数 促销

    C17 6.5.2.2/7 关于可变参数函数:

    函数原型声明器中的省略号符号导致参数类型转换在最后一个声明的参数之后停止。默认参数 提升是在尾随参数上执行的。

    实际上这意味着:

    • 传递给printffloat 在函数调用期间被隐式转换为double
    • 传递给printf 的小整数类型在函数调用期间根据整数提升进行隐式转换,最有可能以int 结尾。
    • 传递给printf 的其他类型在函数调用期间不会被隐式提升。

    然后传递的和可能转换的参数在内部被视为转换说明符指定的类型。如果该类型与实际类型不匹配,则代码具有未定义的行为。

    在您的情况下,您传递了一个int,它不会被隐式提升,但由于printf 将其视为long long,您会得到未定义的行为。

    【讨论】:

      【解决方案3】:

      这是未定义的行为,因为 %lldint 类型的不合适的格式说明符。

      是的,确实(signed int)a - 1 是一个int 类型,其值为-1,但printf 调用是未定义的部分。 C 标准中没有任何内容表明发生了到 long long 的转换。

      【讨论】:

      • 哇,谢谢!是的,如果我将 (signed int)a - 1 类型转换为 signed long long int,那么我会得到预期的结果。
      猜你喜欢
      • 1970-01-01
      • 2012-01-16
      • 1970-01-01
      • 1970-01-01
      • 2022-10-13
      • 1970-01-01
      • 1970-01-01
      • 2011-07-16
      • 2011-05-26
      相关资源
      最近更新 更多