1.基本数据类型
char //字符型数据类型
short //短整型
int //整型
long //长整型
long long //更长的整型(C99)
float //单精度浮点数
double //双精度浮点数
类型的意义:
1.决定了开辟空间的大小
2.决定了看待内存空间的视角
例:int类型和float类型同样都是向内存申请4个字节的空间,从int的角度认为这四个字节的空间放的是整型,而从float的角度认为这四个字节的空间存放的是浮点数。(具体怎么看待后面会讲)
类型的基本归类:
整型类型
char //字符的本质是ASCII码值,是整型
unsigend char //unsigend为无符号
sigend char //sigend为有符号
short
unsigend short [int]
sigend short [int]
int
unsigend int
sigend int
long
unsigend long [int]
sigend long [int]
浮点数类型
float
double
构造类型(自定义类型):
数组类型 //int arr[10]的类型为去掉数组名arr之后保留的int [10]
结构体类型 struct
枚举类型 enum
联合类型 union
指针类型
int *pi
char *pc
float* pf
void* pv
2.整型在内存中的存储
数值有多种不同的表现形式,如二进制,八进制,十进制,十六进制等。而在内存中,数值的储存是以二进制的形式储存的。
1.原码、反码、补码
计算机中整数有三种2进制表示方式,即原码、反码、补码。
三种表示方式均有符号位(最高位为“0”则为正,为“1”则为负)和数值位两部分,且正数原码、反码、补码均相同。
负数的原码、反码、补码则需要我们去记忆
原码
直接将数值按照正负数的形式翻译成二进制即可得到原码
反码
符号位不改变,数值位取反(若为1则化为0,若为0则化为1)得到反码
补码
反码+1得到补码
值得一提的是,负数的原码和补码互为原补码。将原码取反+1得到补码,而将该补码取反+1又能得到原来的原码。
2.整型数据在内存中的储存方式是以补码进行的
为什么呢?
CPU只有加法器,计算1-1时是按照1+(-1)的形式进行的而非1-1
32位环境下,int类型的
1的补码为00000000000000000000000000000001(32位)
-1的原码为10000000000000000000000000000001(32位)
-1的反码为11111111111111111111111111111110(32位)
-1的补码为11111111111111111111111111111111(32位)
将二者的补码相加为100000000000000000000000000000000(33位)
因为其超出了32位,所以第33位的1丢失,二者相加的结果为
00000000000000000000000000000000(32位)对应的结果为0
其他正数负数相加的结果同理
而且原码补码的转换过程是相同的,不需要增加额外的硬件电路。
3.大小端
以VS2022为例,
a=10的二进制应为00000000000000000000000000001010,鉴于2进制的32位数字太长,我们将其转化为16进制,应为0x00 00 00 0a(0x指的是用16进制表示),再来看看VS2022上内存的结果注:该结果显示的虽然是16进制,这是因为我将列数修改为4列,而不是储存结果为16进制,其在内存中的储存结果仍为2进制
它的结果竟然是0a 00 00 00,和预期结果是相反的,这究竟是怎么一回事呢?
这里我们以0x11 22 33 44作为例子来说明(11,22,33,44各占一个字节),以字节为单位讨论内存的存放方式。
他在内存中的存储方式有两种:
1.低地址 11 22 33 44 高地址(大端字节序存储)
2.低地址 44 33 22 11 低地址(小端字节序存储)
11 22 33 44从低字节到高字节分别是44 33 22 11(理解不了可以想一下一个三位数,百位是不是比十位更高,十位是不是比个位更高,这个也是如此)
若我们把11(高位字节)放在低地址处,把44(低位字节)放在高地址处,这样的存储方式称之为大端字节序存储
若我们把44(低位字节)放在低地址处,把11(高位字节)放在高地址处,这样的存储方式称之为小端字节序存储
总结:
大端储存模式:
指的是把高位数据放在低地址处,把低位数据放在高地址处。
小段储存模式
指的是把低位数据放在低地址处,把高位数据放在高地址处。
设计一串代码区分当前的字节序
#include<stdio.h>
int main()
{
int a = 1;//00 00 00 01,计算机读取数据是从低地址向高地址进行读取的
//小端字节序第一个字节为01,大端字节序第一个地址为00
if (*(char*)&a)//将a的地址强制转换成char*类型,并进行解引用
//转化为char*的原因是int*访问的是地址后四个字节的数据
//而char*访问的是地址第一个字节的数据
printf("小端\n");//若为01则为小端字节序
else
printf("大端\n");//若为00则为大端字节序
return 0;
}
来看看结果
4.整型数据的打印
先来看看用%d打印数据的结果
#include<stdio.h>
int main()
{
char a = -1;//char在不同编译器中可能默认有符号,也可能默认无符号
//我们这里看看VS2022中char默认有无符号
signed char b = -1;
unsigned char c = -1;
printf("a=%d\tb=%d\tc=%d\n", a, b, c);
return 0;
}
来看结果
为什么会出现这样的结果呢?
-1的原码是10000000000000000000000000000001
-1的反码是11111111111111111111111111111110
-1的补码是11111111111111111111111111111111
a是char类型的数据只能存放8个二进制位,发生了截断(只取最后的8个二进制位)
所以a就为11111111,%d是打印有符号的整型,所以发生了整型提升,对于有符号的数来说,整型提升的规则是高位补符号位
变成打印11111111111111111111111111111111(这依然是补码,所以取反+1)
即打印10000000000000000000000000000001即为-1
b同理
unsigned char c是指一个无符号的字符型数据
同上,c存放的补码为11111111,但是c是一个无符号的字符型数据,最高位不为符号位。发生整型提升时,无符号的数据在高位上直接补0。
所以我们用%d打印的数为00000000000000000000000011111111,这还是补码。又因为用%d打印的是有符号的整型,最高位为0,表示它是整数,原反补相同,打印结果为255。
再看看用%u打印
#include<stdio.h>
int main()
{
char a = -128;//char类型可存储-128-127
//原码10000000000000000000000010000000
//反码11111111111111111111111101111111
//补码11111111111111111111111110000000
//发生截断a - 100000000
printf("%u\n", a);
//%u - 打印无符号的整型
//发生整型提升,a最高位为1,高位全补1
//变成11111111111111111111111110000000
//因为打印的是无符号的数,所以直接将它打印
return 0;
}
运行结果
若char a=128(a存不下128)再用%u去打印,结果仍为这个值,大家可以自己思考一下为什么会出现这个结果
再附一张整型提升的图片
从下往上提升
3.浮点数在内存中的存储
1.整型的储存方式和浮点数的储存方式不一样
#include<stdio.h>
int main()
{
int n = 9;
float* pf = (float*)&n;
printf("n的值为%d\n", n);//以int的视角打印n
printf("*pf的值为%f\n", *pf);//以float*的视角打印&n
*pf = 9.0;
printf("n的值为%d\n", n);//以int的视角打印n
printf("*pf的值为%f\n", *pf);//以float*的视角打印&n
return 0;
}
只是把整型改变成了浮点数,打印的结果就完全不一样。从中不难发现.整型的储存方式和浮点数的储存方式是不一样的
2.浮点数的储存
浮点数存的过程
IEEE 754 标准 一.IEEE 754基本存储规则 (-1) ^ S * M * 2 ^ E 其中S用来控制正负,当S为0浮点数表示为正,反之为负。 M则代表大于等于1且小于2的一个有效数字。 2^E表示指数位。
例:
V = 5.0f;-->二进制形式为101.0 --> 1.01 * 2^2
=1.01 * 2^2
=(-1)^0 * 1.01 * 2^2
S=0 M=1.01 E=2
IEEE 754 规定:
对于32位的浮点数(float),最高的1位储存符号位S,接着的8位储存指数E,剩下的23位储存有效数字M
对于64位的浮点数(double),最高的1位储存符号位S,接着的11位储存指数E,剩下的52位储存有效数字M
IEEE 754 对有效数字M和指数E的特别规定
因为1 <= M < 2,所以M可以写成1.XXXXX的形式,其中XXXXX表示小数部分
IEEE 754 规定,保存M的时候,数的第一位总是1,所以将其省略,进而增加浮点数的精确值。
例:32位浮点数,保存1.01时,只保存01部分,在读取的时候再将1加上去,这样能保存24位有效数字
E为一个无符号正数(unsigned int)
如果E为8位,取值范围为0-255;如果E为11位,取值范围为0-2047。但是科学计数法中E是可以出现负数的,为此IEEE 754 规定,存入内存时,E的值需要增加一个中间数,8位的E的中间数为127,11位的E的中间数为1023。比如,一个E为10的数,在保存成32位浮点数时,必须保存成10+127=137,即10001001
浮点数取的过程
指数E从内存中取出有3种情况:
E不全为0或不全为1
这时,指数E的计算值减去中间数,得到真实值,再将有效数字M前加上第一位的1
比如:0.5的二进制形式为0.1,由于规定正数部分必须为1,即将小数点右移一位,则为1.0*2^(-1),指数E为-1+127=126,表示为01111110,而尾数1.0去掉正数部分为0,补齐0到23位
0 01111110 000000000000000000000000
E全为0
这时,浮点数的指数E等于1-127(或者1-1023)即为真实值,有效数字M不再加上第一位的1,而是还原成0.XXXX的小数。这样做是为了表示+-0,以及接近于0的很小的数字
0 00000000 001000000000000000000000
E全为1
这时,有效数字M全为0,表示正负无穷大
0 00000000 000000000000000000000000