04-数据类型
数据类型
1.有符号数和无符号数
1.1 有符号数和无符号数
1. 有符号数
有符号数是最高位为符号位,0代表正数,1代表负数。,最高位为符号位,其他位为数据位
- 默认是有符号数
- 要存储一个负数,要定义一个有符号数
char num;
signed char num; // 有符号数
unsgned char num;// 无符号数
#include <stdio.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;
}
2. 无符号整数
无符号数最高位不是符号位,而就是数的一部分,无符号数不可能是负数。
*当我们写程序要处理一个不可能出现负值的时候,一般用无符号数,这样可以增大数的表达最大值。
#include <stdio.h>
int main()
{
unsigned int a = 3236958022; //定义无符号整型变量a
printf("%X\n", a); //结果为 C0F00F46
return 0;
}
1.2 数的范围
有符号数和无符号数能够表示数的个数是一样的,但是表示的数的范围是不一致的
1. char类型
- 有符号char -128 - 127 (-2^7 ~ 2^7-1)
- 无符号char 0 - 255 (0 - 2^8-1)
- 规定:将负0表示成-128
- 有符号和无符号都表示256个数
// 有符号
1,000 0000 - 1,111 1111 -0 - -127
0,000 0000 - 0,111 1111 +0 - +127
// 无符号
0000 0000 - 1111 1111 0 - 255
2. short类型
- signed:-2^15 - 2^15-1
- unsigned:0 - 2^16-1
其他类型可以类似推导得出
数据类型 | 占用空间 | 取值范围 |
---|---|---|
short | 2字节 | -32768 到 32767 (-215 ~ 215-1) |
int | 4字节 | -2147483648 到 2147483647 (-231 ~ 231-1) |
long | 4字节 | -2147483648 到 2147483647 (-231 ~ 231-1) |
unsigned short | 2字节 | 0 到 65535 (0 ~ 216-1) |
unsigned int | 4字节 | 0 到 4294967295 (0 ~ 232-1) |
unsigned long | 4字节 | 0 到 4294967295 (0 ~ 232-1) |
3. 数的范围不能越界
2.计算机内存数值存储方式
2.1 计算机中数值的存储
120对应的二进制,01111010
-23对应二进制,10010111
// 计算机中存储值,负数在计算机中的存储?
// 以char类型存储0 -0 -1 1
-0 : 1000 0000
0 : 0000 0000
1 : 0000 0001
-1 : 1000 0001
// 使用源码操作加减
0000 0001 1
1000 0001 + -1
1000 0010 -2 //结果错误
如果负数在计算机中原码存储,会导致两个问题,负数运算结果不正确,0的状态还有两种
// 如果以反码存储,查看结果
源码 反码
-0 : 1000 0000 1111 1111
0 : 0000 0000 0000 0000
1 : 0000 0001 0000 0001
-1 : 1000 0001 1111 1110
1 0000 0001
-1 1111 1110
-0 1111 1111 // 结果为-0,运算结果正确但是有两种0
以反码存储,负数运算结果正确但是0得状态有两种
// 以补码存储,负数运算结果
-0 0000 0000
0 0000 0000
1 0000 0001
-1 1111 1111
-1 + 1 = 0
// 负数运算结果正确,而且0的状态只有一种
正数和负数在计算机中都是以补码进行存储的
2.2 原码
一个数的原码(原始的二进制码)有如下特点:
- 最高位做为符号位,0表示正,为1表示负
- 其它数值部分就是数值本身绝对值的二进制数
- 负数的原码是在其绝对值的基础上,最高位变为1
下面数值以1字节的大小描述:
十进制数 | 原码 |
---|---|
+15 | 0000 1111 |
-15 | 1000 1111 |
+0 | 0000 0000 |
-0 | 1000 0000 |
原码表示法简单易懂,与带符号数本身转换方便,只要符号还原即可,但当两个正数相减或不同符号数相加时,必须比较两个数哪个绝对值大,才能决定谁减谁,才能确定结果是正还是负,所以原码不便于加减运算。
#include <stdio.h>
int main()
{
char num = 0x81; // 十六进制数--给的是补码--1000 0001
printf("%d\n",num); // %d输出有符号32位整数 -127
printf("%u\n,num"); // %u输出无符号32位整数 11931683
system("pause");
return 0;
unsigned int d = 0xffffffff; 定义无符号int变量d,以十六进制的方式进行赋值
/*
虽然定义的是无符号数,但是打印的是%d需要将其看成有符号数
d = 0xffffffff -- 赋值以16进制赋值,给的是补码
补码: 1111 1111 1111 1111 1111 1111 1111 1111
反码: 1000 0000 0000 0000 0000 0000 0000 0000
原码: 1000 0000 0000 0000 0000 0000 0000 0001 输出-1
*/
printf("有符号方式打印:d = %d\n",d); // -1
printf("无符号方式打印:d = %u\n",d); // 0xffffffff
}
2.3 反码
- 对于正数,反码与原码相同
- 对于负数,符号位不变,其它部分取反(1变0,0变1)
十进制数 | 反码 |
---|---|
+15 | 0000 1111 |
-15 | 1111 0000 |
+0 | 0000 0000 |
-0 | 1111 1111 |
反码运算也不方便,通常用来作为求补码的中间过渡
2.4 补码
在计算机系统,数值一律用补码来存储
补码特点::
- 对于正数,原码、反码、补码相同
- 对于负数,其补码为它的反码加1
- 补码符号位不动,其他位求反,最后整个数加1,得到原码
十进制数 | 补码 |
---|---|
+15 | 0000 1111 |
-15 | 1111 0001 |
+0 | 0000 0000 |
-0 | 0000 0000 |
2.5 补码的意义
示例1:用8位二进制数分别表示+0和-0
十进制数 | 原码 |
---|---|
+0 | 0000 0000 |
-0 | 1000 0000 |
十进制数 | 反码 |
---|---|
+0 | 0000 0000 |
-0 | 1111 1111 |
不管以原码方式存储,还是以反码方式存储,0也有两种表示形式。为什么同样一个0有两种不同的表示方法呢?
但是如果以补码方式存储,补码统一了零的编码:
十进制数 | 补码 |
---|---|
+0 | 0000 0000 |
-0 | 10000 0000由于只用8位描述,最高位1丢弃,变为0000 0000 |
示例2:计算9-6的结果
以原码方式相加:
十进制数 | 原码 |
---|---|
9 | 0000 1001 |
-6 | 1000 0110 |
结果为-15,不正确。
以补码方式相加:
十进制数 | 补码 |
---|---|
9 | 0000 1001 |
-6 | 1111 1010 |
最高位的1溢出,剩余8位二进制表示的是3,正确。
在计算机系统中,数值一律用补码来存储,主要原因是:
- 统一了零的编码
- 将符号位和其它位统一处理
- 将减法运算转变为加法运算
- 两个用补码表示的数相加时,如果最高位(符号位)有进位,则进位被舍弃
#include <stdio.h>
int main(){
// 赋值时(输入),赋的是十进制,给的是原码,如果赋值给的是8进制或者是16进制给的是补码
// 打印时(输出),十进制打印的是原码,如果是十六进制或者8进制打印的是补码
char num = 129;
//129 -- 十进制 -- 原码-反码-补码 1000 0001
// num是有符号数,最高位为1,计算机认为这是一个负数的补码
/*
1000 0001
1111 1110
1111 1111 -127
*/
printf("num = %d\n",num); // -127
}
#include <stdio.h>
int main(){
// 如果赋值给的是8进制或者十六进制给的是补码
// 输出,十进制要的是原码,16进制或8进制要的是补码
char ch = 0x82; // 1000 0010
/*
补码 1000 0010
反码 1111 1101
原码 1111 1110 -126
*/
printf("%d\n",ch);
//2
int num = 0x80 00 00 01;
// 补码: 1000 0000 0000 0000 0000 0000 0000 0001
// 反码: 1111 1111 1111 1111 1111 1111 1111 1110
// 原码: 1111 1111 1111 1111 1111 1111 1111 1111 -7fffffff
printf("%d\n,num");
//3.
char ch = -123; // 原码 1 111 1011
printf("%d\n",ch); // -123
}
2.6 数值溢出
当超过一个数据类型能够存放最大的范围时,数值会溢出。
有符号位最高位溢出的区别:符号位溢出会导致数的正负发生改变,但最高位的溢出会导致最高位丢失。
数据类型 | 占用空间 | 取值范围 |
---|---|---|
char | 1字节 | -128到 127(-27 ~ 27-1) |
unsigned char | 1字节 | 0 到 255(0 ~ 28-1) |
#include <stdio.h>
int main()
{
char ch;
//符号位溢出会导致数的正负发生改变
// 7f -- 补码 -- 0111 1111
// 2 -- 补码 -- 0000 0010
1000 0001
// 反码 1111 1110
// 原码 1111 1111 -127
ch = 0x7f + 2; //127+2
printf("%d\n", ch);
// 0111 1111
//+2后 1000 0001,这是负数补码,其原码为 1111 1111,结果为-127
//最高位的溢出会导致最高位丢失
// 补码 1111 1111
// 0000 0001
unsigned char ch2;
ch2 = 0xff+1; //255+1
printf("%u\n", ch2);
// 1111 1111
//+1后 10000 0000, char只有8位最高位的溢出,结果为0000 0000,十进制为0
ch2 = 0xff + 2; //255+1
printf("%u\n", ch2);
// 1111 1111
//+1后 10000 0001, char只有8位最高位的溢出,结果为0000 0001,十进制为1
return 0;
}
3. 整型
3.1 整型变量的定义与输出
打印格式 | 含义 |
---|---|
%d | 输出一个有符号的10进制int类型 |
%o(字母o) | 输出8进制的int类型 |
%x | 输出16进制的int类型,字母以小写输出 |
%X | 输出16进制的int类型,字母以大写输出 |
%u | 输出一个10进制的无符号数 |
#include <stdio.h>
int main(){
char num = 0x81;
// 补码 -- 1000 0001
// 反码 -- 1111 1110
// 原码 -- 1111 1111
print("%d\n",num); // 打印有符号数 -- 最高位为符号位 -127
print("%u\n"num); // 4294967169(涉及到8位转32位) (输出无符号数)
int num_2 = 0x81;
// 0000 0000 ... 1000 0001 (补码-原码)
printf("%d\n",num_2); // 129
printf("%u\n",num_2); // 129
}
#include <stdio.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); // a = 123
printf("8进制:b = %o\n", b); // 567(8进制)
printf("10进制:b = %d\n", b); // 375 (十进制)
printf("16进制:c = %x\n", c); // abc
printf("16进制:c = %X\n", c); // ABC
printf("10进制:c = %d\n", c); // 2748
unsigned int d = 0xffffffff; //定义无符号int变量d,以16进制方式赋值
// 十六进制给的是补码,所以存储的时候以补码的形式存储,输出使用%d,就是有符号数,补码转原码,符号
// 位不变,输出使用%u,无符号数,高位不是符号位
// 补码: 1111 1111 1111 1111 1111 1111 1111 1111
// 反码: 1000 0000 0000 0000 0000 0000 0000 0000
// 原码: 1000 0000 0000 0000 0000 0000 0000 0001
printf("有符号方式打印:d = %d\n", d); // -1
printf("无符号方式打印:d = %u\n", d); // 0xffffffff
return 0;
}
3.2 整型变量的输入
- 从键盘输入一个数,使用scanf()函数
- scanf()从键盘(标准输入)读取数据,键盘输入之后,内容写到标准输入文件(stdin),当程序运行到scanf()的时候,从标准输入中读取,读取内容取决于前面的格式,\n表示结束
- scanf()没有回车或者换行,会阻塞程序
- 空格也会结束输入
#include <stdio.h>
int main()
{
int sum = 0;
int num = 0;
scanf("%d",&num); // 键盘输入-->标准输入-->num变量
scanf("%d %d",&num,&sum); // 键盘输入两个值,中间用空格123 456分别给两个变量
printf("%d\n",num);
printf("%d\n",sum);
system("pause");
return 0;
}
#include <stdio.h>
int main()
{
int a;
printf("请输入a的值:");
//不要加“\n”
scanf("%d", &a);
printf("a = %d\n", a); //打印a的值
return 0;
}
3.3 整型变量占用的空间
数据类型 | 占用空间 |
---|---|
short(短整型) | 2字节 |
int(整型) | 4字节 |
long(长整形) | Windows为4字节,Linux为4字节(32位),8字节(64位) |
long long(长长整形) | 8字节 |
注意:
- 需要注意的是,整型数据在内存中占的字节数与所选择的操作系统有关。虽然 C 语言标准中没有明确规定整型数据的长度,但 long 类型整数的长度不能短于 int 类型, short 类型整数的长度不能长于 int 类型。
- 当一个小的数据类型赋值给一个大的数据类型,不会出错,因为编译器会自动转化。但当一个大的类型赋值给一个小的数据类型,那么就可能丢失高位。
sizeof关键字 - sizeof不是函数,所以不需要包含任何头文件,它的功能是计算一个数据类型的大小,单位为字节
- sizeof的返回值为size_t
- size_t类型在32位操作系统下是unsigned int,是一个无符号的整数
整型常量 | 所需类型 |
---|---|
10 | 代表int类型 |
10l, 10L | 代表long类型 |
10ll, 10LL | 代表long long类型 |
10u, 10U | 代表unsigned int类型 |
10ul, 10UL | 代表unsigned long类型 |
10ull, 10ULL | 代表unsigned long long类型 |
#include <stdio.h>
int main()
{
char a;
short int b; // 短整型 int可以省略
int c;
long d;
long long e;
// sizeof 用来测数据类型的大小,打印的是字节数
// sizeof(变量或者数据类型)
}
打印格式 | 含义 |
---|---|
%hd | 输出short类型 |
%d | 输出int类型 |
%ld | 输出long类型 |
%lld | 输出long long类型 |
%hu | 输出unsigned short类型 |
%u | 输出unsigned int类型 |
%lu | 输出unsigned long类型 |
%llu | 输出unsigned long long类型 |
#include <stdio.h>
int main()
{
short a = 10;
int b = 10;
long c = 10l; //或者10L
long long d = 10ll; //或者10LL
printf("sizeof(a) = %u\n", sizeof(a));
printf("sizeof(b) = %u\n", sizeof(b));
printf("sizeof(c) = %u\n", sizeof(c));
printf("sizeof(c) = %u\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;
}