Oracle® Developer Studio 12.5:C 用户指南

退出打印视图

更新时间: 2016 年 7 月
 
 

7.3 提升:无符号保留与值保留

以下信息显示在 1990 ISO C 标准的补充材料 Rationale 一节:"QUIET CHANGE"。依赖于无符号保留算术转换的程序表现各异,可能没有错误消息。此更改被认为是委员会对当前的普遍实践做出的最重大更改。

本节研究此更改如何影响代码。

7.3.1 一些背景历史

在《The C Programming Language》的第一版中,unsigned 准确地指定一种类型,没有 unsigned charunsigned shortunsigned long。但在此后不久,大多数 C 编译器增加了这些类型。一些编译器未实现 unsigned long,而包括了另外两种类型。自然地,当这些新类型与在表达式中其他类型混合时,实现为类型提升选择不同的规则。

在大多数 C 编译器中,使用更简单的规则,即无符号保留。需要展宽无符号类型时,其将展宽为无符号类型;当将无符号类型与有符号类型混合时,结果是有符号类型。

ISO C 指定的另一个规则称为“值保留”, 其中结果类型取决于操作数类型的相对大小。当展宽 unsigned charunsigned short 类型时,如果 int 的长度足以表示较短类型的所有值,则结果类型为 int。否则,结果类型为 unsigned int。对于大多数表达式,值保留规则产生较少意外的算术结果。

7.3.2 编译行为

仅在转换或 ISO 模式下(-Xt-Xs ),ISO C 编译器才会使用无符号的保留提升。指定了 -std=anyvalue 或在其他两种模式下(符合 (–Xc) 和 ISO (–Xa)),将使用值保留提升规则。

7.3.3 示例:强制类型转换的使用

在以下代码中,假定 unsigned charint 窄。

int f(void)
{
    int i = -2;
    unsigned char uc = 1;

   return (i + uc) < 17;
}

该代码导致编译器在您使用 -xtransition 选项时发出以下警告:

line 6: warning: semantics of "<" change in ISO C; use explicit cast

加法运算结果的类型为 int(值保留)或 unsigned int(无符号保留),但二者之间的位模式不会更改。在二进制补码机器上:

    i:       111...110 (-2)
+   uc:      000...001 ( 1)
===================
        111...111 (-1 or UINT_MAX)

这种位表示对应于 -1(对于 int)或 UINT_MAX(对于 unsigned int)。因此,如果结果的类型为 int,将使用有符号比较,并且小于测试为 true。如果结果的类型为 unsigned int,将使用无符号比较,并且小于测试为 false。

强制类型转换的加法用来指定这两种行为之中所期望的行为:

value preserving:
    (i + (int)uc) < 17
unsigned preserving:
    (i + (unsigned int)uc) < 17

由于不同的编译器对相同的代码选择不同的含义,因此该表达式存在歧义。强制类型转换的加法帮助阅读器并消除警告消息。

相同的 位字段值的提升存在同样的情况。在 ISO C 中,如果 intunsigned int 位字段中的位数小于 int 中的位数,则提升的类型为 int;否则提升的类型为 unsigned int。在大多数较旧的 C 编译器中,对于显式无符号位字段,所提升的类型为 unsigned int,在其他情况下为 int

强制类型转换的类似使用可以消除存在歧义的情况。

7.3.4 示例:相同的结果,不发出警告

在以下代码中,假定 unsigned shortunsigned char 都比 int 要窄。

int f(void)
{
    unsigned short us;
    unsigned char uc;
    return uc < us;
}

在此示例中,两个自动变量会同时提升为 intunsigned int,因此比较有时无符号,有时带符号。然而,由于两种选择的结果相同,因此 C 编译器并不向您发出警告。

7.3.5 整数常量

与表达式一样,有些整型常量的类型规则已更改。在 K&R C 中,无后缀二进制常量仅在其值足以表示 int 时才具有类型 int。无后缀八进制或十六进制常量仅在其值足以表示 unsigned int 时才具有类型 int。否则,整型常量的类型为 long。有时,值用结果类型不足以表示。在 1990 ISO/IEC C 标准中,常量类型是以下列表中与值对应的第一个类型:

  • 无后缀十进制:intlongunsigned long

  • 无后缀八进制或十六进制:intunsigned intlongunsigned long

  • U 后缀:unsigned intunsigned long

  • L 后缀:longunsigned long

  • UL 后缀:unsigned long

使用 -xtransition 选项时,对于其行为可能会根据所涉及常量的类型处理规则而更改的任何表达式,ISO C 编译器会向您发出警告。旧整形常量类型处理规则仅在转换模式下使用。ISO 和符合模式使用新规则。


注 -  无后缀十进制常量的类型处理规则已按照 1999 ISO C 标准更改。请参见整型常量

7.3.6 示例:整数常量

在以下代码中,假定 int 为 16 位。

int f(void)
{
    int i = 0;

    return i > 0xffff;
}

由于十六进制常量的类型为 int(在二进制补码计算机上的值为 –1)或 unsigned int(值为 65535),因此在 -Xs-Xt 模式下的比较为 true,在 -Xa-Xc 模式下或在指定了 -std 标志时的比较为 false。

同样,相应的强制类型转换澄清代码并禁止警告:

-Xt, -Xs modes:
    i > (int)0xffff

-Xa, -Xc modes, or when -std flag is specified:
    i > (unsigned int)0xffff
       or
    i > 0xffffU

U 后缀字符是 ISO C 的一项新功能,在旧版编译器中可能会生成错误消息。