Skip to content

整数类型

整数类型又可以分为两大类:有符号整数和无符号整数。默认情况下,C 语言的整数类型都是有符号的;若要声明为无符号整数,则需要加 unsigned 关键字。C 语言的整数类型有以下这些:

cpp
short (int)
unsigned short (int)
int
unsigned (int)
long (int)
unsigned long (int)
long long (int)
// (int) 使用小括号包括int 表示int可写可不写

整型对应数学中的整数、没有小数点的数字,和整数一样有正负之分。

整数字面值

C 语言允许使用十进制 (decimal),八进制 (octal) 或者十六进制 (hexadecimal) 来书写整数字面值。

  • 十进制字面值包含数字 0~9,但是不能以 0 开头。如:15, 255, 32767。
  • 八进制字面值包含数字 0~7,而且必须以 0 开头。如:017, 0377, 077777。
  • 十六进制字面值包含数字 0~9 和字母 a~f,而且总以 0x 开头。如:0xf, 0xff, 0x7ff。

十六进制字面值中的字母即可以是大写也可以是小写,如:0xff, 0xfF, 0xFF, 0Xff, 0XfF, 0XFF。

十进制整数字面值的类型通常是 int,但如果该字面值超出了 int 的表示范围,那么它的类型是 int, long 和 long long 中能表示该值的 "最小" 类型。对八进制和十六进制整数字面值来说,可能的类型顺序为:int, unsigned int, long, unsigned long, long long 和 unsigned long long。

  • 如果要指明某个整数字面值为一个 long 类型,我们需要在后面加字母 L (或 l):

    cpp
    15L, 0377L, 0x7fffL
  • 如果要指明整数字面值是 long long 类型,我们需要在后面加 LL (或 ll):

    cpp
    15LL, 0377LL, 0x7fffLL
  • 如果要指明整数常量是无符号的,我们需要在后面加字母 U (或 u):

    cpp
    15U, 0377U, 0x7fffU
  • L、LL还可以和 U 结合使用,如:

    cpp
    0xffffffffUL, 0x12345678ULL

    顺便说一下:字母 L, LL 和 U 的顺序可以颠倒。

空间占用

但是有两条所有编译器都必须遵循的原则:
  • 首先 C标准规定了各个整型类型的最小字节长度,其中 short 2字节、int 2字节、long 4字节、long long 8字节。

  • 其次 C标准规定了各个整数类型的字节长度满足下面的关系:

    cpp
    short <= int <= long <= long long

下表是64 位机器上整数类型的常见取值范围:(常见 并不是说一定)

数据类型占用空间
(字节长度)
最小值最大值取值范围
short2-3276832767-215 ~ 215-1
unsigned short20655350 ~ 216-1
int4-21474836482 147 483 647-231 ~ 231-1
unsigned int404 294 967 2950 ~ 232-1
long8-9 223 372 036 854 775 8089 223 372 036 854 775 807-263 ~ 263-1
unsigned long8018 446 744 073 709 551 6150 ~ 263-1
long long8-9 223 372 036 854 775 8089 223 372 036 854 775 80 7-263 ~ 263-1
unsigned long long8018 446 744 073 709 551 6150 ~ 263-1
  • 需要注意的是,整型数据在内存中占的字节数与所选择的操作系统有关。虽然 C 语⾔标准中没有明确规定整型数据的长度,但 long 类型整数的长度不能短于 int 类型, short 类型整数的长度不能长于 int 类型。
  • 当一个小的数据类型赋值给一个⼤的数据类型,不会出错,因为编译器会⾃动转化。但当一个⼤的类型赋值给一个小的数据类型,那么就可能丢失⾼位。

测试不同的整型数据空间占用情况

c
#include <stdio.h>
#include <stdlib.h>

int main() {
	short a = 10;
	int b = 20;
	long c = 30;
	long long d = 40;
	
	printf("sizeof a = %zu, a = %d\n", sizeof a, a);
	printf("sizeof b = %zu, b = %d\n", sizeof b, b);
	printf("sizeof c = %zu, c = %d\n", sizeof c, c);
	printf("sizeof d = %zu, d = %d\n", sizeof d, d);

	return 0;
}

程序输出:

shell
sizeof a = 2, a = 10
sizeof b = 4, b = 20
sizeof c = 4, c = 30
sizeof d = 8, d = 40

读/写整数

如果使用 scanf 和 printf 函数读写无符号整数、短整数和长整数,那么我们需要一 些新的转换说明符。%d 只适用于读写 int 类型的数据。

  • 读写 unsigned int 时,使用字母 u, o 或 x 替代转换说明符中的 d。其中 u 表明无符 号整数是十进制形式;o 表明无符号整数是八进制形式;x 表明是十六进制形式。

    cpp
    unsigned int n;
    
    scanf("%u", &n); /* reads n in base 10 */
    printf("%u", n); /* writes n in base 10 */
    scanf("%o", &n); /* reads n in base 8 */
    printf("%o", n); /* writes n in base 8 */
    scanf("%x", &n); /* reads n in base 16 */
    printf("%x", n); /* writes n in base 16 */
  • 读写 short 时,在 d, u, o 或着 x 前面加字母 h (short):

    cpp
    short n;
    
    scanf("%hd", &n); 
    printf("%hd", n);
  • 读写 long 时,在 d, u, o 或者 x 前面加字母 l:

    cpp
    long n;
    
    scanf("%ld", &n); 
    printf("%ld", n);
  • 读写 long long 时,在 d, u, o 或者 x 前面加字母 ll:

    cpp
    long long n;
    
    scanf("%lld", &n); 
    printf("%lld", n);

定义整型变量

c
#include <stdio.h>
#include <stdlib.h>

int main() {
    int a = 123;  //定义变量a,以10进制⽅式赋值为123
    int b = 0567; //定义变量b,以8进制⽅式赋值为0567
    int c = 0xabc;//定义变量c,以16进制⽅式赋值为0xabc

    printf("a = %d\n", a);

    printf("8进制:b = %o\n", b);
    printf("10进制:b = %d\n", b);
    printf("16进制:c = %x\n", c);
    printf("16进制:c = %X\n", c);
    printf("10进制:c = %d\n", c);

    unsigned int d = 0xffffffff;//定义⽆符号int变量d,以16进制⽅式赋值
    printf("有符号⽅式打印:d = %d\n", d);
    printf("⽆符号⽅式打印:d = %u\n", d);

    return 0;
}

程序输出:

shell
a = 123
8进制:b = 567
10进制:b = 375
16进制:c = abc
16进制:c = ABC
10进制:c = 2748
有符号⽅式打印:d = -1
⽆符号⽅式打印:d = 4294967295

输出格式

打印格式含义
%hd输出个的10进制short类型
%d输出一个的10进制int类型
%o(字母o)输出8进制的int类型
%ld输出一个的10进制long类型
%lld输出一个的10进制long long类型
%hu输出unsigned short类型
%u输出一个10进制的数,unsigned int类型
%lu输出unsigned long类型
%llu输出unsigned long long类型
%zu匹配sizeof的结果
%x输出16进制的int类型,字母以小写输出
%X输出16进制的int类型,字母以⼤写输出

编写代码测试

c
#include <stdio.h>
#include <stdlib.h>

int main() {
    short a = 10;
    int b = 10;
    long c = 10l;      //或者10L
    long long d = 10ll;//或者10LL

    printf("sizeof(a) = %zu\n", sizeof(a));
    printf("sizeof(b) = %zu\n", sizeof(b));
    printf("sizeof(c) = %zu\n", sizeof(c));
    printf("sizeof(c) = %zu\n", sizeof(d));
    printf("short a = %hd\n", a);
    printf("int b = %d\n", b);
    printf("long c = %ld\n", c);
    printf("long long d = %lld\n", d);

    unsigned short a2 = 20u;
    unsigned int b2 = 20u;
    unsigned long c2 = 20ul;
    unsigned long long d2 = 20ull;

    printf("unsigned short a = %hu\n", a2);
    printf("unsigned int b = %u\n", b2);
    printf("unsigned long c = %lu\n", c2);
    printf("unsigned long long d = %llu\n", d2);

    return 0;
}

程序输出:

shell
sizeof(a) = 2
sizeof(b) = 4
sizeof(c) = 8
sizeof(c) = 8
short a = 10
int b = 10
long c = 10
long long d = 10
unsigned short a = 20
unsigned int b = 20
unsigned long c = 20
unsigned long long d = 20

获取输入的整型数据

c
#include <stdio.h>
#include <stdlib.h>

int main() {

    int a;
    printf("请输⼊a的值:");//不要加“\n”

    scanf("%d", &a);
    printf("a = %d\n", a);//打印a的值
    return 0;
}

程序输出:

shell
请输⼊a的值:10↙️
a = 10

数值溢出

当超过一个数据类型能够存放最⼤的范围时,数值会溢出。

有符号位最⾼位溢出的区别:符号位溢出会导致数的正负发生改变,但最⾼位的溢出会导致最⾼位丢失。

测试int类型的最⼤值及其数据溢出的情况

c
#include <float.h> //浮点数数的最⼤最小值定义在该文件下
#include <limits.h>//整型数的最⼤最小值定义在该文件下
#include <math.h>
#include <stdio.h>
#include <stdlib.h>

int main() {
    int a = INT_MAX;
    int b = pow(2, 31) - 1;
    int c = b + 1;

    printf("a = INT_MAX=[%d]\n", a);
    printf("b = pow(2, 31) - 1=[%d]\n", b);
    printf("c = b + 1=[%d]\n\n", c);

    if (a == b) {
        printf("a==b\n");
    } else {
        printf("a!=b\n");
    }
    
    return 0;
}

程序输出:

shell
a = INT_MAX=[2147483647]
b = pow(2, 31) - 1=[2147483647]
c = b + 1=[-2147483648]

a==b

整数类型编码 (拓展)

无符号整数和有符号整数它们的编码是不一样的。

无符号整数的编码

首先我们来看一下无符号整数的编码。,也就是说我们只 需要将二进制转换成十进制即可。二进制表示 bn-1 … b1b0 的值为 bn-12n-1 + … b121 + b020

Q:如果一个无符号整数的内存表示为 1010_1010 (2) ,那么它的值是多少?

A:如果要将二进制转换为十进制可以使用 8421 进行转换,具体规则可以参考进制章节。

,而就是数的一部分,⽆符号数不可能是负数。

c
#include <stdio.h>
#include <stdlib.h>

int main() {
    unsigned int a = 3236958022;//定义⽆符号整型变量a
    printf("%X\n", a);          //结果为 C0F00F46

    return 0;
}

程序输出:

shell
C0F00F46

当我们写程序要处理一个不可能出现负值的时候,一般用⽆符号数,这样可以增⼤数的表达最⼤值。

有符号整数的编码

。二进制表示 bn-1 … b1b0 的值为 -bn-12n-1 + … b121 + b020。和无符号整数的区别是

Q:如果一个有符号整数的内存表示为 1010_1010 (2) ,那么它的值是多少?

A:如果要将二进制转换为十进制可以使用 8421 进行转换,具体规则可以参考进制章节。

和无符号整数的唯一区别就是最高项的符号的负的

image-20240427152634904

有符号整数采用补码进行编码的原因是:设计 CPU 的时间设计的是利用加法器做减法运算,毕竟 a - b = a + (b)。当然也可以设计一个减法器来做减法运算,但这无疑会增加 CPU 电路设计的复杂度,会带来很多元器件的增加。所以利用规则来实现是一个比较均衡的结果。

补码有一些特性(假设 x 有 n 位):

x + (-x) = 100...0 (2) (有n个0) ---> 0

x + (~x) = 11...1 (2) (有n个1) ---> -1

有符号数是最⾼位为符号位,0代表正数,1代表负数。

c
#include <stdio.h>
#include <stdlib.h>

int main() {
    signed int a = -1089474374;//定义有符号整型变量a
    printf("%X\n", a);         //结果为 BF0FF0BA
    //  B   F    0     F    F   0    B    A
    //1011 1111 0000 1111 1111 0000 1011 1010

    return 0;
}

程序输出:

shell
BF0FF0BA