数据类型

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

04-数据类型-小白菜博客
结果为-15,不正确。

以补码方式相加:

十进制数 补码
9 0000 1001
-6 1111 1010

04-数据类型-小白菜博客
最高位的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;
}