Sun Studio 12:C 用户指南

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

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

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

6.4.1 背景

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

在大多数 C 编译器中,使用比较简单的规则“无符号保留”:当无符号类型需要展宽时,将被展宽为无符号类型;当无符号类型与带符号类型混合时,结果为无符号类型。

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

6.4.2 编译行为

只有在转换模式或 ISO 模式(-Xt-Xs)下,ISO C 编译器才使用无符号保留提升;在其他两种模式下,即符合标准模式 (–Xc) 和 ISO 模式 (–Xa),使用值保留提升规则。

6.4.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,则使用带符号比较且小于测试为真;如果结果类型为 unsigned int,则使用无符号比较且小于测试为假。

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


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

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

6.4.4 位字段

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

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

6.4.5 第二个示例:相同的结果

在以下代码中,假定 unsigned shortunsigned char 均比 int 短。


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

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

6.4.6 整型常量

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

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


注 –

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


6.4.7 第三个示例:整型常量

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


int f(void)
{
    int i = 0;

    return i > 0xffff;
}

由于十六进制常量的类型为 int(在二进制补码机器上,值为 – 1)或 unsigned int(值为 65535),因此在 – Xs-Xt 模式下比较为真,在 – Xa– Xc 模式下比较为假。

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


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

-Xa, -Xc modes:
    i > (unsigned int)0xffff
       or
    i > 0xffffU

U 后缀字符是 ISO C 的新增功能,对于较旧的编译器它可能会产生错误消息。