C 语言整数类型简介

目录汇总:C 语言零基础入门教程

C 语言支持两种根本不同的数值类型:整数类型(也称整型)和浮点类型(也称浮点型)。整数类型的值是整数,而浮点类型的值则可能还有小数部分。整数类型又分为两类:有符号整数和无符号整数。

有符号整数和无符号整数

有符号整数如果为正数或零,那么最左边的位(符号位)为 0;如果是负数,则符号位为 1。因此,最大的 16 位整数的二进制表示是 0111111111111111,对应的数值是 32 767(即 $2^{15}-1$)。最大的 32 位整数的二进制表示是 01111111111111111111111111111111,对应的数值是 2 147 483 647(即 $2^{31}-1$)。不带符号位(最左边的位是数值的一部分)的整数称为无符号整数。最大的 16 位无符号整数是 65 535(即 $2^{16}-1$),而最大的 32 位无符号整数是 4 294 967 295(即 $2^{32}-1$)。

默认情况下,C 语言中的整型变量都是有符号的,也就是说最左位保留为符号位。若要告诉编译器变量没有符号位,需要把它声明成 unsigned 类型。无符号整数主要用于系统编程和底层与机器相关的应用。

C 语言的整数类型有不同的大小。int 类型通常为 32 位,但在老的 CPU 上可能是 16 位。有些程序所需的数很大,无法以 int 类型存储,所以 C 语言还提供了长整型。某些时候,为了节省空间,我们会指示编译器以比正常存储小的空间来存储一些数,这样的数称为短整型

为了使构造的整数类型正好满足需要,可以指明变量是 long 类型或 short 类型、singed 类型或 unsigned 类型,甚至可以把说明符组合起来(如 long unsigned int)。然而,实际上只有下列 6 种组合可以产生不同的类型:

1
2
3
4
5
6
7
8
short int
unsigned short int

int
unsigned int

long int 
unsigned long int

其他组合都是上述某种类型的同义词。(例如,除非额外说明,否则所有整数都是有符号的。因此,long signed intlong int 是一样的类型。)另外,说明符的顺序没什么影响,所以 unsigned short intshort unsigned int 是一样的。

C 语言允许通过省略单词 int 来缩写整数类型的名字。例如,unsigned short int 可以缩写为 unsigned short,而 long int 可以缩写为 long。C 程序员经常会省略 int。一些新出现的基于 C 的语言(包括 Java)甚至不允许程序员使用 short intlong int 这样的名字,而必须写成 shortlong。基于这些原因,本 C 语言零基础入门教程 在单词 int 可有可无的情况下通常将其省略。

6 种整数类型的每一种所表示的取值范围都会根据机器的不同而不同,但是有两条所有编译器都必须遵守的原则。首先,C 标准要求 short intintlong int 中的每一种类型都要覆盖一个确定的最小取值范围。其次,标准要求 int 类型不能比 short int 类型短,long int 类型不能比 int 类型短。但是,short int 类型的取值范围有可能和 int 类型的范围是一样的,int 类型的取值范围也可以和 long int 的一样。

表 1 说明了在 16 位机上整数类型通常的取值范围,注意 short intint 有相同的取值范围。

表 1 16 位机的整数类型

类型 最小值 最大值
short int -32 768 32 767
unsigned short int 0 65 535
int -32 768 32 767
unsigned int 0 65 535
long int -2 147 483 648 2 147 483 647
unsigned long int 0 4 294 967 295

表 2 说明了 32 位机上整数类型通常的取值范围,这里的 intlong int 有着相同的取值范围。

表 2 32 位机的整数类型

类型 最小值 最大值
short int -32 768 32 767
unsigned short int 0 65 535
int -2 147 483 648 2 147 483 647
unsigned int 0 4 294 967 295
long int -2 147 483 648 2 147 483 647
unsigned long int 0 4 294 967 295

最近 64 位的 CPU 逐渐流行起来了。表 3 给出了 64 位机上(尤其是在 UNIX 系统下)整数类型常见的取值范围。

表 3 64 位机的整数类型

类型 最小值 最大值
short int -32 768 32 767
unsigned short int 0 65 535
int -2 147 483 648 2 147 483 647
unsigned int 0 4 294 967 295
long int -9 223 372 036 854 775 808 9 223 372 036 854 775 807
unsigned long int 0 18 446 744 073 709 551 615

再强调一下,表 1、表 2 和表 3 中给出的取值范围不是 C 标准强制的,会随着编译器的不同而不同。对于特定的实现,确定整数类型范围的一种方法是检查 <limits.h> 头。该头是标准库的一部分,其中定义了表示每种整数类型的最大值和最小值的宏。

一、C99 中的整数类型

C99 提供了两个额外的标准整数类型:long long intunsigned long long int。增加这两种整数类型有两个原因,一是为了满足日益增长的对超大型整数的需求,二是为了适应支持 64 位运算的新处理器的能力。这两个 long long 类型要求至少 64 位宽,所以 long long int 类型值的范围通常为 $-2^{63}$(-9 223 372 036 854 775 808)~$2^{63}-1$(9 223 372 036 854 775 807),而 unsigned long long int 类型值的范围通常为 0~$2^{64}-1$(18 446 744 073 709 551 615)。

C99 中把 short intintlong intlong long int 类型以及 signed char 类型(C 语言字符类型简介)称为标准有符号整型,而把 unsigned short intunsigned intunsigned long intunsigned long long int 类型以及 unsigned char 类型(C 语言字符类型简介)和 _Bool 类型(C 语言 if 语句简介)称为标准无符号整型

除了标准的整数类型以外,C99 标准还允许在具体实现时定义扩展的整数类型(包括有符号的和无符号的)。例如,编译器可以提供有符号和无符号的 128 位整数类型。

二、整型常量

现在把注意力转向常量——在程序中以文本形式出现的数,而不是读、写或计算出来的数。C 语言允许用十进制(基数为 10)、八进制(基数为 8)和十六进制(基数为 16)形式书写整型常量。

八进制数和十六进制数

八进制数是用数字 0~7 书写的。八进制数的每一位表示一个 8 的幂(这就如同十进制数的每一位表示 10 的幂一样)。因此,八进制数 237 表示成十进制数就是 $2\times8^2+3\times8^1+7\times8^0=128+24+7=159$。

十六进制数是用数字 0~9 加上字母 A~F 书写的,其中字母 A~F表 示 10~15 的数。十六进制数的每一位表示一个 16 的幂,十六进制数 1AF 的十进制数值是 $1\times16^2+10\times16^1+15\times16^0=256+160+15=431$。

  • 十进制常量包含 0~9 中的数字,但是一定不能以零开头:

    1
    
    15  255  32767
    
  • 八进制常量只包含 0~7 中的数字,而且必须要以零开头:

    1
    
    017  0377  077777
    
  • 十六进制常量包含 0~9 中的数字和 a~f 中的字母,而且总是以 0x 开头:

    1
    
    0xf  0xff  0x7fff
    

    十六进制常量中的字母既可以是大写字母也可以是小写字母:

    1
    
    0xff  0xfF  0xFf  0xFF  0Xff  0XfF  0XFf  0XFF
    

请记住八进制和十六进制只是书写数的方式,它们不会对数的实际存储方式产生影响。(整数都是以二进制形式存储的,跟表示方式无关。)任何时候都可以从一种书写方式切换到另一种书写方式,甚至可以混合使用:10 + 015 + 0x20 的值为 55(十进制)。八进制和十六进制更适用于底层程序的编写。

十进制整型常量的类型通常为 int,但如果常量的值大得无法存储在 int 型中,那就用 long int 类型。如果出现 long int 不够用的罕见情况,编译器会用 unsigned long int 做最后的尝试。确定八进制十六进制常量的规则略有不同:编译器会依次尝试 intunsigned intlong intunsigned long int 类型,直至找到能表示该常量的类型。

要强制编译器把常量作为长整数来处理,只需在后边加上一个字母 L(或 l):

1
15L  0377L  0x7fffL

要指明是无符号常量,可以在常量后边加上字母 U(或 u):

1
15U  0377U  0x7fffU

LU 可以结合使用,以表明常量既是长整型又是无符号的:0xffffffffUL。(字母 LU 的顺序和大小写无所谓。)

三、C99 中的整型常量

在 C99 中,以 LLll(两个字母大小写要一致)结尾的整型常量是 long long int 型的。如果在 LLll 的前面或后面增加字母 U(或 u),则该整型常量为 unsigned long long int 型。

C99 确定整型常量类型的规则与 C89 有些不同。对于没有后缀(UuLlLLll)的十进制常量,其类型是 intlong intlong long int 中能表示该值的“最小”类型。对于八进制或者十六进制常量,可能的类型顺序为 intunsigned intlong intunsigned long intlong long intunsigned long long int。常量后面的任何后缀都会改变可能类型的列表。例如,以 U(或 u)结尾的常量类型一定是 unsigned intunsigned long intunsigned long long int 中的一种,以 L(或 l)结尾的十进制常量类型一定是 long intlong long int 中的一种。如果常量的数值过大,以至于不能用标准的整数类型表示,则可以使用扩展的整数类型。

四、整数溢出

对整数执行算术运算时,其结果有可能因为太大而无法表示。例如,对两个 int 值进行算术运算时,结果必须仍然能用 int 类型来表示;否则(表示结果所需的数位太多)就会发生溢出

整数溢出时的行为要根据操作数是有符号型还是无符号型来确定。有符号整数运算中发生溢出时,程序的行为是未定义的。回顾 C 语言表达式求值 的介绍可知,未定义行为的结果是不确定的。最可能的情况是,仅仅是运算的结果出错了,但程序也有可能崩溃,或出现其他意想不到的状况。

无符号整数运算过程中发生溢出时,结果是有定义的:正确答案对 $2^n$ 取模,其中 n 是用于存储结果的位数。例如,如果对无符号的 16 位数 65 535 加 1,其结果可以保证为 0。

五、读/写整数

假设有一个程序因为其中一个 int 变量发生了“溢出”而无法工作。我们的第一反应是把变量的类型从 int 变为 long int。但仅仅这样做是不够的,我们还必须检查数据类型的改变对程序其他部分的影响,尤其是需要检查该变量是否用在 printf 函数或 scanf 函数的调用中。如果已经用了,则需要改变调用中的格式串,因为 %d 只适用于 int 类型。

读写无符号整数、短整数和长整数需要一些新的转换指定符。

  • 读写无符号整数时,使用字母 uox 代替转换说明中的 d。 如果使用 u 说明符,该数将按十进制读写,o 表示八进制,x 表示十六进制。

    1
    2
    3
    4
    5
    6
    7
    8
    
    unsigned int u;
    
    scanf("%u", &u);      /* reads  u in base 10 */
    printf("%u", u);      /* writes u in base 10 */
    scanf("%o", &u);      /* reads  u in base  8 */
    printf("%o", u);      /* writes u in base  8 */
    scanf("%x", &u);      /* reads  u in base 16 */
    printf("%x", u);      /* writes u in base 16 */
    
  • 读写整数时,在 doux 前面加上字母 h

    1
    2
    3
    4
    
    short s;
    
    scanf("%hd", &s);
    printf("%hd", s);
    
  • 读写整数时,在 doux 前面加上字母 l

    1
    2
    3
    4
    
    long l;
    
    scanf("%ld",  &l);
    printf("%ld",  l);
    
  • 读写长长整数时(仅限 C99),在 doux 前面加上字母 ll

    1
    2
    3
    4
    
    long long ll;
    
    scanf("%lld",  &ll);
    printf("%lld",  ll);
    

程序 数列求和(改进版)

C 语言 while 语句简介 编写了一个程序,对用户输入的整数数列求和。该程序的一个问题就是所求出的和(或其中某个输入数)可能会超出 int 型变量允许的最大值。如果程序运行在整数长度为 16 位的机器上,可能会发生下面的情况:

This program sums a series of integers.
Enter integers (0 to terminate): 10000 20000 30000 0
The sum is: -5536

求和的结果应该为 60 000,但这个值不在 int 型变量表示的范围内,所以出现了溢出。当有符号整数发生溢出时,结果是未定义的,在本例中我们得到了一个毫无意义的结果。为了改进这个程序,可以把变量改换成 long 型。

sum2.c

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
/* Sums a series of numbers (using long variables) */

#include <stdio.h>

int main(void)
{
 long n, sum = 0;

 printf("This program sums a series of integers.\n");
 printf("Enter integers (0 to terminate): ");

 scanf("%ld", &n);
 while (n != 0) {
   sum += n;
   scanf("%ld", &n);
 }
 printf("The sum is: %ld\n", sum);

 return 0;
}

这种改变非常简单:将 nsum 声明为 long 型变量而不是 int 型变量,然后把 scanfprintf 函数中的转换说明由 %d 改为 %ld

请参阅

(完)

comments powered by Disqus