当前位置:嗨网首页>书籍在线阅读

11-其他整数类型

  
选择背景色: 黄橙 洋红 淡粉 水蓝 草绿 白色 选择字体: 宋体 黑体 微软雅黑 楷体 选择字体大小: 恢复默认

3.4.2 其他整数类型

初学C语言时, int 类型应该能满足大多数程序的整数类型需求。尽管如此,还应了解一下整型的其他形式。当然,也可以略过本节跳至 3.4.3 节阅读 char 类型的相关内容,以后有需要时再阅读本节。

C语言提供 3 个附属关键字修饰基本整数类型: shortlongunsigned 。应记住以下几点。

  • short int 类型(或者简写为 short )占用的存储空间可能比 int 类型少,常用于较小数值的场合以节省空间。与 int 类似, short 是有符号类型。
  • long intlong 占用的存储空间可能比 int 多,适用于较大数值的场合。与 int 类似, long 是有符号类型。
  • long long intlong longC99 标准加入)占用的存储空间可能比 long 多,适用于更大数值的场合。该类型至少占 64 位。与 int 类似, long long 是有符号类型。
  • unsigned intunsigned 只用于非负值的场合。这种类型与有符号类型表示的范围不同。例如, 16unsigned int 允许的取值范围是 065535 ,而不是 -3276832767 。用于表示正负号的位现在用于表示另一个二进制位,所以无符号整型可以表示更大的数。
  • C90 标准中,添加了 unsigned long intunsigned longunsigned short intunsigned short 类型。 C99 标准又添加了 unsigned long long intunsigned long long
  • 在任何有符号类型前面添加关键字 signed ,可强调使用有符号类型的意图。例如, shortshort intsigned shortsigned short int 都表示同一种类型。

1.声明其他整数类型

其他整数类型的声明方式与 int 类型相同,下面列出了一些例子。不是所有的C编译器都能识别最后 3 条声明,最后一个例子所有的类型是 C99 标准新增的。

long int estine;
long johns;
short int erns;
short ribs;
unsigned int s_count;
unsigned players;
unsigned long headcount;
unsigned short yesvotes;
long long ago;

2.使用多种整数类型的原因

为什么说 short 类型“可能”比 int 类型占用的空间少, long 类型“可能”比 int 类型占用的空间多?因为C语言只规定了 short 占用的存储空间不能多于 intlong 占用的存储空间不能少于 int 。这样规定是为了适应不同的机器。例如,过去的一台运行 Windows 3.x 的机器上, int 类型和 short 类型都占 16 位, long 类型占 32 位。后来, Windows 和苹果系统都使用 16 位存储 short 类型, 32 位存储 int 类型和 long 类型(使用 32 位可以表示的整数数值超过 20 亿)。现在,计算机普遍使用 64 位处理器,为了存储 64 位的整数,才引入了 long long 类型。

现在,个人计算机上最常见的设置是, long long64 位, long32 位, short16 位, int16 位或 32 位(依计算机的自然字长而定)。原则上,这 4 种类型代表 4 种不同的大小,但是在实际使用中,有些类型之间通常有重叠。

C标准对基本数据类型只规定了允许的最小大小。对于 16 位机, shortint 的最小取值范围是 [32768,32767] ;对于 32 位机, long 的最小取值范围是 [2147483648,2147483647] 。对于 unsigned shortunsigned int ,最小取值范围是 [0,65535] ;对于 unsigned long ,最小取值范围是 [0,4294967295]long long 类型是为了支持 64 位的需求,最小取值范围是 [9223372036854775808,9223372036854775807]unsigned long long 的最小取值范围是 [0,18446744073709551615] 。如果要开支票,这个数是一千八百亿亿六千七百四十四万亿零七百三十七亿零九百五十五万一千六百一十五。但是,谁会去数?

int 类型那么多,应该如何选择?首先,考虑 unsigned 类型。这种类型的数常用于计数,因为计数不用负数。而且, unsigned 类型可以表示更大的正数。

如果一个数超出了 int 类型的取值范围,且在 long 类型的取值范围内时,使用 long 类型。然而,对于那些 long 占用的空间比 int 大的系统,使用 long 类型会减慢运算速度。因此,如非必要,请不要使用 long 类型。另外要注意一点:如果在 long 类型和 int 类型占用空间相同的机器上编写代码,当确实需要 32 位的整数时,应使用 long 类型而不是 int 类型,以便把程序移植到 16 位机后仍然可以正常工作。类似地,如果确实需要 64 位的整数,应使用 long long 类型。

如果在 int 设置为 32 位的系统中要使用 16 位的值,应使用 short 类型以节省存储空间。通常,只有当程序使用相对于系统可用内存较大的整型数组时,才需要重点考虑节省空间的问题。使用 short 类型的另一个原因是,计算机中某些组件使用的硬件寄存器是 16 位。

3. long 常量和 long long 常量

通常,程序代码中使用的数字(如, 2345 )都被存储为 int 类型。如果使用 1000000 这样的大数字,超出了 int 类型能表示的范围,编译器会将其视为 long int 类型(假设这种类型可以表示该数字)。如果数字超出 long 可表示的最大值,编译器则将其视为 unsigned long 类型。如果还不够大,编译器则将其视为 long longunsigned long long 类型(前提是编译器能识别这些类型)。

八进制和十六进制常量被视为 int 类型。如果值太大,编译器会尝试使用 unsigned int 。如果还不够大,编译器会依次使用 longunsigned longlong longunsigned long long 类型。

有些情况下,需要编译器以 long 类型存储一个小数字。例如,编程时要显式使用 IBM PC 上的内存地址时。另外,一些C标准函数也要求使用 long 类型的值。要把一个较小的常量作为 long 类型对待,可以在值的末尾加上 l (小写的 L )或 L 后缀。使用 L 后缀更好,因为 l 看上去和数字 1 很像。因此,在 int16 位、 long32 位的系统中,会把 7 作为 16 位存储,把 7L 作为 32 位存储。 lL 后缀也可用于八进制和十六进制整数,如 020L0x10L

类似地,在支持 long long 类型的系统中,也可以使用 llLL 后缀来表示 long long 类型的值,如 3LL 。另外, uU 后缀表示 unsigned long long ,如 5ull10LLU6LLU9Ull

整数溢出

如果整数超出了相应类型的取值范围会怎样?下面分别将有符号类型和无符号类型的整数设置为比最大值略大,看看会发生什么( printf() 函数使用 %u 说明显示 unsigned int 类型的值)。

/* toobig.c-- 超出系统允许的最大int值*/
#include <stdio.h>
int main(void)
{
     int i = 2147483647; 
      unsigned int j = 4294967295;
      printf("%d %d %d\n", i, i+1, i+2);
      printf("%u %u %u\n", j, j+1, j+2);
      return 0;
}

在我们的系统下输出的结果是:

2147483647    -2147483648    -2147483647
4294967295    0    1

可以把无符号整数 j 看作是汽车的里程表。当达到它能表示的最大值时,会重新从起始点开始。整数 i 也是类似的情况。它们主要的区别是,在超过最大值时, unsigned int 类型的变量 j0 开始;而 int 类型的变量 i 则从− 2147483648 开始。注意,当 i 超出(溢出)其相应类型所能表示的最大值时,系统并未通知用户。因此,在编程时必须自己注意这类问题。

溢出行为是未定义的行为,C标准并未定义有符号类型的溢出规则。以上描述的溢出行为比较有代表性,但是也可能会出现其他情况。

4.打印 shortlonglong longunsigned 类型

打印 unsigned int 类型的值,使用 %u 转换说明;打印 long 类型的值,使用 %ld 转换说明。如果系统中 intlong 的大小相同,使用 %d 就行。但是,这样的程序被移植到其他系统( intlong 类型的大小不同)中会无法正常工作。在 xo 前面可以使用 l 前缀, %lx 表示以十六进制格式打印 long 类型整数, %lo 表示以八进制格式打印 long 类型整数。注意,虽然C允许使用大写或小写的常量后缀,但是在转换说明中只能用小写。

C语言有多种 printf() 格式。对于 short 类型,可以使用 h 前缀。 %hd 表示以十进制显示 short 类型的整数, %ho 表示以八进制显示 short 类型的整数。 hl 前缀都可以和 u 一起使用,用于表示无符号类型。例如, %lu 表示打印 unsigned long 类型的值。程序清单 3.4 演示了一些例子。对于支持 long long 类型的系统, %lld%llu 分别表示有符号和无符号类型。第 4 章将详细介绍转换说明。

程序清单3.4  print2.c 程序

/* print2.c--更多printf()的特性 */
#include <stdio.h>
int main(void)
{
     unsigned int un = 3000000000; /* int为32位和short为16位的系统 */
     short end = 200;
     long big = 65537;
     long long verybig = 12345678908642;
     printf("un = %u and not %d\n", un, un);
     printf("end = %hd and %d\n", end, end);
     printf("big = %ld and not %hd\n", big, big);
     printf("verybig= %lld and not %ld\n", verybig, verybig);
     return 0;
}

在特定的系统中输出如下(输出的结果可能不同):

un = 3000000000 and not -1294967296
end = 200 and 200
big = 65537 and not 1
verybig= 12345678908642 and not 1942899938

该例表明,使用错误的转换说明会得到意想不到的结果。第 1 行输出,对于无符号变量 un ,使用 %d 会生成负值!其原因是,无符号值 3000000000 和有符号值− 129496296 在系统内存中的内部表示完全相同(详见第 15 章)。因此,如果告诉 printf() 该数是无符号数,它打印一个值;如果告诉它该数是有符号数,它将打印另一个值。在待打印的值大于有符号值的最大值时,会发生这种情况。对于较小的正数(如 96 ),有符号和无符号类型的存储、显示都相同。

2 行输出,对于 short 类型的变量 end ,在 printf() 中无论指定以 short 类型( %hd )还是 int 类型( %d )打印,打印出来的值都相同。这是因为在给函数传递参数时,C编译器把 short 类型的值自动转换成 int 类型的值。你可能会提出疑问:为什么要进行转换? h 修饰符有什么用?第 1 个问题的答案是, int 类型被认为是计算机处理整数类型时最高效的类型。因此,在 shortint 类型的大小不同的计算机中,用 int 类型的参数传递速度更快。第 2 个问题的答案是,使用 h 修饰符可以显示较大整数被截断成 short 类型值的情况。第 3 行输出就演示了这种情况。把 65537 以二进制格式写成一个 32 位数是 00000000000000010000000000000001 。使用 %hdprintf() 只会查看后 16 位,所以显示的值是 1 。与此类似,输出的最后一行先显示了 verybig 的完整值,然后由于使用了 %ldprintf() 只显示了存储在后 32 位的值。

本章前面介绍过,程序员必须确保转换说明的数量和待打印值的数量相同。以上内容也提醒读者,程序员还必须根据待打印值的类型使用正确的转换说明。

提示 匹配printf()说明符的类型 在使用printf()函数时,切记检查每个待打印值都有对应的转换说明,还要检查转换说明的类型是否与待打印值的类型相匹配。