一、数据
数据是指对客观事件进行记录并可以鉴别的符号,是信息的表现形式和载体。据所指代的并不仅是狭义上的数字,还可以包括符号、文字、语音、图形和视频等。
简单来说就是有用的信息
那么,数据如何存储在内存中?
1. 确认数据的类型 10 3.14 'a' '1' "10" "100" 'a'
2. 根据数据的类型在c语言中确认分配的数据类型 int char float double
3. 确认分配的空间是否满足数据的存放 char:1字节 short 2字节 int 4字节
二、数据类型的尺寸
整型数据尺寸是指某种整型数据所占用内存空间的大小
C语言标准并未规定整型数据的具体大小,只规定了相互之间的 “ 相对大小 ” ,比如:
- short 不可比 int 长
- long 不可比 int 短
- long 型数据长度等于系统字长
系统字长:CPU 一次处理的数据长度,称为字长。比如32位系统、64位系统。
典型尺寸:
- char 占用1个字节
- short 占用2个字节
- int 在16位系统中占用2个字节,在32位和64位系统中一般都占用4个字节
- long 的尺寸等于系统字长
- long long 在32位系统中一般占用4个字节,在64位系统中一般占用8个字节
存在问题:
同样的代码,放在不同的系统中,可能会由于数据尺寸发生变化而无法正常运行。
因此,系统标准整型数据类型,是不可移植的,这个问题在底层代码中尤为突出。
三、数据的进制表示
1. 进制
- 十进制:
- 由十个数码来表示的数。分别是:0、1、2、3、4、5、6、7、8、9
- 遵循的原则是:逢十进一
- 二进制:
- 由两个数码来表示的数。分别是:0、1
- 遵循的原则是:逢二进一
- 八进制:
- 由八个数码来表示的数。分别是:0、1、2、3、4、5、6、7
- 遵循的原则是:逢八进一
- 十六进制:
- 由十六个数码来表示的数。分别是:0、1、2、3、4、5、6、7、8、9、A、B、C、 D、E、F
- 遵循的原则是:逢十六进一
- 表示方法:一个数占四个比特位
补充:
- 字节(Byte):
- 计算机中数据储存的单位,也是数据读取的最小单位(按字寻址的系统除外)
- 位(bit):也叫作“比特”,计算机中数据储存的最小单位,因为在计算机中是以二进制的形式数据储存,所以每个位以“0”或“1”表示
- 字节与位的关系:1Byte=8bit
- 1KB=1024B(2^10)、1MB=1024KB、1GB=1024MB、1TB=1024GB
2. 进制转换方法
- 十进制转二进制
短除法
- 十六进制转二进制:十六进制中的任意字节码都能用四个二进制字节码表示
- 八进制转二进制:八进制中的任意字节码都能用三位个二进制字节码表示
- 二进制转十进制:2的次方形式
011100 -> 0*2^5+1*2^4+1*2^3+1*2^2+0*2^1+0*2^0 =16+8+4=28
- 二进制转十六进制:
00101100 -> 2C
11 0010 1010 ->0011 0010 1010->32A
- 二进制转八进制:
001 101 011 -> 153
四、类型转换
不一致但相互兼容的数据类型,在同一表达式中将会发生类型转换
转换模式:
隐式转换:系统按照隐式规则自动进行的转换
强制转换:用户显式自定义进行的转换
1. 隐式转换
隐式规则:从小类型向大类型转换,目的是保证不丢失表达式中数据的精度
转换按数据长度增加的方向进行,以保证数值不失真,或者精度不降低。例如,int 和 long 参与运算时,先把 int 类型的数据转成 long 类型后再进行运算
char a = 'a';
int b = 12;
float c = 3.14;
float x = a + b - c; // 在该表达式中将发生隐式转换,所有操作数被提升为float
- 字符必须先转换为整数(C语言规定字符类型数据和整型数据之间可以通用) 。
- short型转换为int型(同属于整型)
- float型数据在运算时一律转换为双精度(double)型,以提高运算精度(同属于实型)
- 若两种类型的字节数相同,且一种有符号,一种无符号,则转换成无符号类型
- 若两种类型的字节数不同,转换成字节数高的类型
2. 强制转换
用户强行将某类型的数据转换为另一种类型,此过程可能丢失精度
#include<stdio.h>
int main()
{
int sum = 103; //总数
int count = 7; //数目
double average; //平均数
average = (double) sum / count;
printf("Average is %lf!\n", average);
return 0;
}
运行结果:
sum 和 count 都是 int 类型,如果不进行干预,那么sum / count的运算结果也是 int 类型,小数部分将被丢弃;虽然是 average 是 double 类型,可以接收小数部分,但是心有余力不足,小数部分提前就被“阉割”了,它只能接收到整数部分,这就导致除法运算的结果严重失真。既然 average 是 double 类型,为何不充分利用,尽量提高运算结果的精度呢?为了达到这个目标,我们只要将 sum 或者 count 其中之一转换为 double 类型即可。上面的代码中,我们将 sum 强制转换为 double 类型,这样sum / count的结果也将变成 double 类型,就可以保留小数部分了,average 接收到的值也会更加精确。
不管是隐式转换,还是强制转换,变换的都是操作数在运算过程中的类型,是临时的,操作数本身的类型不会改变,也无法改变。
五、码制
- 正数:原反补一致,均是原码本身,例如10
- 原码:0 000 1010
- 反码:0 000 1010
- 补码:0 000 1010
- 负数:比如-10
- 原码:1 000 1010
- 反码:1 111 0101 (原码除符号位不变,其余位取反)
- 补码:1 111 0110 (补码 = 反码 + 1),这也是计算机中对于数值-10最终的存放形式
- 公式:
- 当 X >= 0, [ X ] 补 = X 零和正数不用变换
- 当 X < 0 , [ X ] 补 = X + 2^n n是补码的位数
- 数值溢出:
- 超过数据所能表达的范围,称为溢出,就像钟表,最大值和最小值是相邻的
#include <stdio.h>
int main()
{
char ch;
//符号位溢出会导致数的正负发生改变
ch = 0x7f + 2; //127+2
printf("%d\n", ch);
// 0111 1111
//+2后 1000 0001,这是负数补码,其原码为 1111 1111,结果为-127
//最高位的溢出会导致最高位丢失
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+2
printf("%u\n", ch2);
// 1111 1111
//+1后 10000 0001, char只有8位最高位的溢出,结果为0000 0001,十进制为1
return 0;
}
运行结果:
表达范围:
在64位机中,int型变量占4字节,就是32位,
- 当表示负数时,最高位为符号位(符号位为1),最小的负数是 1000 0000 0000 0000 0000 0000 0000 0000 而在计算机中是以补码的形式存储的,即-2147483648
- 当表示正数时,最高位为符号位(符号位为0),最大的正数是 0111 1111 1111 1111 1111 1111 1111 1111 即2147483647
Q:
计算机为什么使用补码?
A:
采用补码可以简化计算机硬件电路设计的复杂度
解决了数字 0 在计算机中非唯一编码的问题