【问题标题】:C operators result typeC 运算符结果类型
【发布时间】:2014-06-22 14:45:00
【问题描述】:

在 C11 (n1570) 中,有一些运算符的结果类型已明确说明。例如,&& 的结果具有 int 类型。 但是我没有找到其他运算符的结果类型,比如+。它是否在标准中的某处指定?

我试过这个程序:

unsigned char usc = 254;
unsigned int usi = 4294967293;
signed char sic = 126;
long long unsigned llu = usc*2;
printf("%llu\n",llu);
llu = usi*2;
printf("%llu\n",llu);
llu = usc+usc;
printf("%llu\n",llu);
llu = usi+usi;
printf("%llu\n",llu);
llu = usc+4294967294;
printf("%llu\n",llu);
llu = usc+2147483646;
printf("%llu\n",llu);
llu = sic+4294967294;
printf("%llu\n",llu);
llu = sic+2147483646;
printf("%llu\n",llu);

输出:

508
4294967290
508
4294967290
4294967548
18446744071562068220
4294967420
18446744071562068092

我猜 unsigned char 在这里得到提升,但 unsigned int 没有; char + unsigned int 的结果类型似乎是 unsigned int,char + int 的结果类型似乎是 int。

但我不太确定。

这些铸件是标准的还是实现定义的?

【问题讨论】:

  • 6.3.1.8 为您提供所有答案。
  • @this 第二行已经是实现定义的。 unsigned int 不需要大于 16 位,因此 unsigned int usi = 4294967293; 行为 usi 分配一个值,该值取决于 UINT_MAX 的实现定义定义。
  • @PascalCuoq 当然,我假设 int 今天到处都是 32 位。
  • @this Assume 把你和我搞得一团糟……知道那个吗?有许多现代机器 int 不是 32 位的,尤其是嵌入式机器。 AFAICT,他们仍然大大超过了这种情况。

标签: c casting


【解决方案1】:

规则由标准明确定义:

6.3.1.8 常用算术转换

1 许多期望算术类型操作数的运算符会导致转换并产生结果 以类似的方式键入。目的是确定操作数的通用实数类型 和结果。对于指定的操作数,每个操作数都进行转换,不改变类型 域,到对应的实类型是公共实类型的类型。除非 另有明确说明,公共实类型也是对应的实类型 结果,其类型域是操作数的类型域(如果它们相同), 否则复杂。这种模式称为通常的算术转换:

  • 首先,如果任一操作数对应的实数类型为 long double,则另一 操作数在不改变类型域的情况下被转换为对应的实类型为 long double 的类型。
  • 否则,如果任一操作数对应的实数类型为双精度,则另一 操作数在不改变类型域的情况下被转换为一个类型 对应的实数类型是double。
  • 否则,如果任一操作数对应的实数类型为浮点数,则另一 操作数在不改变类型域的情况下被转换为一个类型 对应的真实类型是float.62)
  • 否则,将在两个操作数上执行整数提升。然后 以下规则适用于提升的操作数:
    • 如果两个操作数的类型相同,则无需进一步转换。
    • 否则,如果两个操作数都具有有符号整数类型或都具有无符号 整数类型,具有较小整数转换等级类型的操作数是 转换为具有更高等级的操作数的类型。
    • 否则,如果具有无符号整数类型的操作数的秩大于或 等于另一个操作数的类型的等级,然后操作数与 有符号整数类型转换为无符号操作数的类型 整数类型。
    • 否则,如果带符号整数类型的操作数的类型可以表示 无符号整数类型的操作数类型的所有值,然后 将无符号整数类型的操作数转换为 带符号整数类型的操作数。
    • 否则,两个操作数都转换为无符号整数类型 对应带符号整数类型的操作数的类型。

不过,您的类型有多大以及有符号类型是否使用 1s-complement、2s-complement 或 sign-and-magnitude 表示是实现定义的。

您正确推断了您的实施会发生什么。

【讨论】:

    【解决方案2】:

    您的示例中没有“转换”,只有转换。 cast 是一种语法结构,如(unsigned long),它会导致转换。转换也会自行发生,就像在您的示例中所做的那样。

    您的示例中发生的转换的名称是“通常的算术转换”。它们在 C11 标准第 6.3.1.8 条中进行了描述,该条太长而无法完整引用(当程序包含 1 + 1.0 时,它还处理从整数类型到浮点类型的转换)。对于整数类型,规则如下:

    如果两个操作数的类型相同,则不再进行转换 需要。

    否则,如果两个操作数都具有有符号整数类型或两者都有 有无符号整数类型,具有较小类型的操作数 整数转换等级转换为操作数的类型 排名更高。

    否则,如果操作数为无符号整数类型 等级大于或等于其他类型的等级 操作数,然后将带符号整数类型的操作数转换为 无符号整数类型的操作数的类型。

    否则,如果类型 有符号整数类型的操作数可以表示所有 具有无符号整数类型的操作数的类型的值,然后 无符号整数类型的操作数转换为 带符号整数类型的操作数。

    否则,两个操作数都是 转换为类型对应的无符号整数类型 带符号整数类型的操作数。

    结果的类型与为操作数决定的通用类型相同。

    Frama-C 的前端,如果您使用的是 Linux,则只需一个“apt-get install”即可,当您命令它打印它具有的抽象语法树时,它使这些转换显式并显示为强制转换建成。在您的示例中,转换是(删除不添加信息的 printf() 调用):

    $ frama-c -print t.c
    ...
      usc = (unsigned char)254;
      usi = 4294967293;
      sic = (signed char)126;
      llu = (unsigned long long)((int)usc * 2);
      llu = (unsigned long long)(usi * (unsigned int)2);
      llu = (unsigned long long)((int)usc + (int)usc);
      llu = (unsigned long long)(usi + usi);
      llu = (unsigned long long)((unsigned int)usc + 4294967294);
      llu = (unsigned long long)((int)usc + 2147483646);
      llu = (unsigned long long)((unsigned int)sic + 4294967294);
      llu = (unsigned long long)((int)sic + 2147483646);
    

    这是针对通用 ILP32 架构的。实现定义的参数可能会影响转换,例如40000 的类型对于大多数 C99 编译器可能是 int,对于其他编译器可能是 long int(它始终是列表中的第一个类型 intlong int , long long int 可以表示常量)。

    【讨论】:

    • 在您的 llu = (unsigned long long)((unsigned int)usc + 4294967294); 示例中。 4294967294 是 long long int 类型,不是 unsigned int,4294967294u 是 unsigned int。 usc 被提升为 long long int。你怎么看?
    • @this 如果您不使用任何特定选项,例如-std=c99,GCC 仍将 C90 规则应用于整数常量。使用这些规则和 32 位 int/unsigned int,4294967294 的类型为 unsigned long(C90 没有 long long,但尝试用于整数常量的类型列表以 unsigned long 结尾。Frama-C 使用这些规则以遵循 GCC 没有选项时所做的事情。(“整数常量的类型是可以表示其值的相应列表中的第一个。无后缀十进制:int、long int、unsigned long int ...”-@987654322 @)
    • 好的,但我们讨论的是标准而非编译器,在 c11 和 32 位 int 下,4294967294 是 long long int,对吧?
    • @this 是的,在 C11 中,如果 intlong 都是 32 位宽,则 4294967294 的类型为 long long
    猜你喜欢
    • 2016-09-21
    • 2011-02-06
    • 2021-11-07
    • 2011-11-06
    • 1970-01-01
    • 2015-08-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多