【问题标题】:Type casting: double to char: multiple questions类型转换:double to char:多个问题
【发布时间】:2020-10-22 05:17:32
【问题描述】:

考虑这段代码:

#include <stdio.h>

int main(void) 
{
    /* TEST 1 */
    double d = 128;
    char ch = (char)d;
    printf("%d\n", ch);

    /* TEST 2 */
    printf("%d\n", (char)128.0);

    /* TEST 3 */
    char ch1 = (char)128.0;
    printf("%d\n", ch1);
    return 0;
}

结果:

        gcc*  clang*  cl*
TEST 1  -128  -128    -128
TEST 2  127   0       -128
TEST 3  127   -2      -128

* latest version

问题:

  1. 为什么不同测试的结果不同(不包括cl)?
  2. 为什么编译器之间的结果不同(不包括TEST 1)?
  3. 如果是 UB/IB,UB/IB 到底在哪里?标准是怎么说的?
  4. [额外问题] 为什么clang 表现出如此不同的行为?这些0-2 来自哪里?

【问题讨论】:

  • 有符号整数溢出是未定义行为。
  • 就 clang 而言,由于它是 UB,clang 可能已经对此进行了优化,以使包含该参数的寄存器/内存位置为调用前的任何值。
  • 有趣的是,gcc 和 clang 都没有对此发出警告,即使是 -Wall -W-Weverything。似乎编译器“知道”正在调用未定义的行为。
  • 似乎是演员消除了警告。 char c = 128.0 确实发出警告。也许,就像在其他情况下一样,演员表被视为“我知道我在做什么”的信号。
  • 嗯,通常当用户“显式运行到 UB”时,这意味着他们对他们正在使用的编译器实际要做什么有一定的了解,并且他们希望它这样做。

标签: c gcc casting clang cl


【解决方案1】:

CHAR_MAX == 127 时,(char)128.0未定义的行为 (UB)。

当实浮点类型的有限值转换为_Bool以外的整数类型时,小数部分被丢弃(即,该值被截断为零)。如果整数部分的值不能用整数类型表示,则行为未定义。 C17dr § 6.3.1.4 1

由于整数溢出,这不是 UB。由于转换规则,它是UB。

【讨论】:

  • 请注意,从整数转换为有符号整数类型时的溢出没有未定义的行为,但会产生实现定义的结果(或引发实现定义的信号)。这没有在这里完成,所以它不是直接相关的,但值得注意区别。
  • @pmor 将浮点值转换为无符号整数类型是明确定义的如果值在范围内。如果不在范围内,则行为未定义。有符号到无符号的转换涉及余数;浮动到无符号没有。 (实现可以做余数;这是未定义行为可以表现出来的无数种可能方式之一。)
  • @KeithThompson Minor:“...如果 截断 值在范围内,则浮点值到无符号整数类型的定义良好。”。
  • 我认为将一些非常大的浮点值(例如 1.23e45)转换为无法保存该值的整数类型对于定义行为而言不够有用。即使对于像(char)55.0 这样的合理转换,需要一些特定的算法也会带来负担。真的,您多久将浮点值转换为char? (请记住,普通char 的签名是实现定义的。)
  • @chux-ReinstateMonica 是的,正确。例如:积分转换永远不会产生未定义的行为(它可以产生实现定义的行为)。来源:stackoverflow.com/a/19274544/9881330.
【解决方案2】:

正如@chux 所说(char)128.0 是一个UB。 gcc 因为示例的琐碎性检测到了这个 UB,而是将 CHAR_MAX 作为最大最接近的有符号数。

但是如果你稍微混淆一下它就不会这样了(转换为int不是UB,gcc不会检测到下一个转换UB)。

int main(void) 
{
    volatile char x = (char)128.0;
    volatile char y = (char)(int)128.0;

    printf("%d %d\n", x, y);
}

和代码(有趣的部分):

        mov     BYTE PTR [rsp+14], 127
        mov     BYTE PTR [rsp+15], -128

https://godbolt.org/z/xG3jUy

顺便说一句,这种 gcc 行为很久以前就被讨论过了,很多人(包括我)都反对它。但 gcc 开发人员决定走这条路。

【讨论】:

  • 感谢“混淆示例”!有趣的。问题:如果 gcc 检测到 [1st conversion] UB,为什么它不会产生任何警告?
  • @pmor 当然,演员会消除警告。提示:避免强制转换。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-11-28
  • 1970-01-01
  • 1970-01-01
  • 2020-07-12
相关资源
最近更新 更多