整数类型
整数类型又可以分为两大类:有符号整数和无符号整数。默认情况下,C 语言的整数类型都是有符号的;若要声明为无符号整数,则需要加 unsigned
关键字。C 语言的整数类型有以下这些:
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):
cpp15L, 0377L, 0x7fffL
如果要指明整数字面值是 long long 类型,我们需要在后面加 LL (或 ll):
cpp15LL, 0377LL, 0x7fffLL
如果要指明整数常量是无符号的,我们需要在后面加字母 U (或 u):
cpp15U, 0377U, 0x7fffU
L、LL还可以和 U 结合使用,如:
cpp0xffffffffUL, 0x12345678ULL
顺便说一下:字母 L, LL 和 U 的顺序可以颠倒。
空间占用
但是有两条所有编译器都必须遵循的原则:首先 C标准规定了各个整型类型的最小字节长度,其中 short 2字节、int 2字节、long 4字节、long long 8字节。
其次 C标准规定了各个整数类型的字节长度满足下面的关系:
cppshort <= int <= long <= long long
下表是64 位机器上整数类型的常见取值范围:(常见 并不是说一定)
数据类型 | 占用空间 (字节长度) | 最小值 | 最大值 | 取值范围 |
---|---|---|---|---|
short | 2 | -32768 | 32767 | -215 ~ 215-1 |
unsigned short | 2 | 0 | 65535 | 0 ~ 216-1 |
int | 4 | -2147483648 | 2 147 483 647 | -231 ~ 231-1 |
unsigned int | 4 | 0 | 4 294 967 295 | 0 ~ 232-1 |
long | 8 | -9 223 372 036 854 775 808 | 9 223 372 036 854 775 807 | -263 ~ 263-1 |
unsigned long | 8 | 0 | 18 446 744 073 709 551 615 | 0 ~ 263-1 |
long long | 8 | -9 223 372 036 854 775 808 | 9 223 372 036 854 775 80 7 | -263 ~ 263-1 |
unsigned long long | 8 | 0 | 18 446 744 073 709 551 615 | 0 ~ 263-1 |
- 需要注意的是,整型数据在内存中占的字节数与所选择的操作系统有关。虽然 C 语⾔标准中没有明确规定整型数据的长度,但 long 类型整数的长度不能短于 int 类型, short 类型整数的长度不能长于 int 类型。
- 当一个小的数据类型赋值给一个⼤的数据类型,不会出错,因为编译器会⾃动转化。但当一个⼤的类型赋值给一个小的数据类型,那么就可能丢失⾼位。
测试不同的整型数据空间占用情况
#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;
}
程序输出:
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 表明是十六进制形式。
cppunsigned 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):
cppshort n; scanf("%hd", &n); printf("%hd", n);
读写 long 时,在 d, u, o 或者 x 前面加字母 l:
cpplong n; scanf("%ld", &n); printf("%ld", n);
读写 long long 时,在 d, u, o 或者 x 前面加字母 ll:
cpplong long n; scanf("%lld", &n); printf("%lld", n);
定义整型变量
#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;
}
程序输出:
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类型,字母以⼤写输出 |
编写代码测试
#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;
}
程序输出:
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
获取输入的整型数据
#include <stdio.h>
#include <stdlib.h>
int main() {
int a;
printf("请输⼊a的值:");//不要加“\n”
scanf("%d", &a);
printf("a = %d\n", a);//打印a的值
return 0;
}
程序输出:
请输⼊a的值:10↙️
a = 10
数值溢出
当超过一个数据类型能够存放最⼤的范围时,数值会溢出。
有符号位最⾼位溢出的区别:符号位溢出会导致数的正负发生改变,但最⾼位的溢出会导致最⾼位丢失。
测试int类型的最⼤值及其数据溢出的情况
#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;
}
程序输出:
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 进行转换,具体规则可以参考进制章节。
#include <stdio.h>
#include <stdlib.h>
int main() {
unsigned int a = 3236958022;//定义⽆符号整型变量a
printf("%X\n", a); //结果为 C0F00F46
return 0;
}
程序输出:
C0F00F46
当我们写程序要处理一个不可能出现负值的时候,一般用⽆符号数,这样可以增⼤数的表达最⼤值。
有符号整数的编码
。二进制表示 bn-1 … b1b0 的值为 -bn-12n-1 + … b121 + b020。和无符号整数的区别是。Q:如果一个有符号整数的内存表示为 1010_1010 (2) ,那么它的值是多少?
A:如果要将二进制转换为十进制可以使用 8421 进行转换,具体规则可以参考进制章节。
和无符号整数的唯一区别就是最高项的符号的负的
有符号整数采用补码进行编码的原因是:设计 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代表负数。
#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;
}
程序输出:
BF0FF0BA