C运算符结果类型

时间:2014-06-22 14:45:00

标签: c casting

在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。

但我不太确定。

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

2 个答案:

答案 0 :(得分:5)

规则由标准明确定义:

  

6.3.1.8通常的算术转换

     

1许多期望算术类型操作数的运算符会导致转换并产生结果   以类似的方式输入类型。目的是确定操作数的通用实数类型   和结果。对于指定的操作数,将转换每个操作数,而不更改类型   域,对应的实类型是常见的实类型。除非   另外明确说明,常见的真实类型也是相应的实际类型   结果,其类型域是操作数的类型域,如果它们是相同的,   否则复杂。这种模式称为通常的算术转换:

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

但是,您的类型有多大以及签名类型是否使用1s补码,2s补码或符号和幅度表示是实现定义的。

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

答案 1 :(得分:2)

您的示例中没有“强制转换”,只有转换。 强制转换是一种语法结构,如(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架构。实现定义的参数可以影响转换,例如,对于大多数C99编译器,40000的类型可能是int,而对于其他C99编译器,long int可能是int的类型(它始终是列表中的第一个类型{可以代表常量的{1}},long intlong long int