目录
一、数据类型
1.1数据类型介绍
在前面我们已经学过前置类型:
char 字符数据类型
short 短整型
int 整形
long 长整型
long long 更长的整形
float 单精度浮点数
double 双精度浮点数
那么让我们来回忆一下他们所占空间大小:
注:long 可能是4/8字节,因为在C语言中只规定了sizeof(long)>=sizeof(int)
为什么整形会分这么多类型呢?
因为数据存储的大小不一样,为了避免浪费空间,以提高内存利用率。
让我们通过下面图片来体会一下:
从这我们可以看到,每个类型存储数据的最大最小值都不一样,以此来提高内存利用率。
那么类型的意义是什么呢?
1.使用这个类型开辟内存空间(大小决定了使用范围)
2.如何看待内存空间视角
1.2类型基本介绍
整形家族:
char:
unsigned char
signed char
short:
unsigned short[int]
signed short[int]
int :
unsigned int
signed int
long:
unsigned long[int]
signed long[int]
为什么char也算在整形中?
因为字符存储的时候存储的是ASCII码值,是整形。
那么接下来就用char类型来举例子!
有符号位:
当 signed char 的值为11111111时,在加上1变为100000000。因为signed
char为一个字节(8bit)因此发生了截断,所以此时的a在内存空间的二进制存储为00000000.
由此可以画出以下循环图:从0逐次加1到127,如果再加1,就会变成-128,然后逐次加1至-1。
无符号:
当 signed char 的值为11111111时,在加上1变为100000000。因为unsigned
char为一个字节(8bit)因此发生了截断,所以此时的a在内存空间的二进制存储为00000000.
由此可以画出以下循环图:从0逐次加1到127,如果再加1,就会变成128,然后逐次加1至255,然后再次循环。
其他类型的有无符号位同上!
浮点数家族:
float
double
构造类型:
数组类型
结构体类型
枚举类型
联合类型
指针类型:
int *p
空类型:
void表示空类型(无类型)
通常用于函数返回类型,函数参数,指针类型
二、整形在内存中存储方式
2.1原码 反码 补码
在计算机中整数有三种二进制表示方法,即原码,反码,补码。
三种表示方法均有符号位和数值位,符号位0表示正,1表示负。
正数的原码 反码 补码全都一样
负数:
原码:直接将数值按照正负数的形式翻译成二进制就可以得到原码。
反码:除了符号位不变,其他位全部取反。
补码:在反码的基础上+1。
对于整形来说:数据存放内存中其实存放的是补码。
由此可得:计算机中存储的确实是二进制的补码,但是为什么顺序是反的呢?
接下来我们要介绍一个新知识大端小端。
2.2大小端介绍
大端:指数据低位保存在内存的高地址中,数据高位保存在低地址中。
小端:指数据低位保存在内存的低地址中,数据高位存储在高地址中。
那么我们知道大小端之后,怎么来确定当前机器的字节序呢?(字节序:是以字节为单位,讨论存储顺序的)
int check_sys()
{
int a = 1;
return *(char*)&a;
}
int main()
{
int ret = check_sys();
if (ret == 1)
{
printf("小端");
}
else
{
printf("大端");
}
return 0;
}
(char*)&a,表示把int * 转化成char *,这样每次操作就只用一个字节的空间
*(char *)&a的到的值就是存储a空间最低地址所存的数
三、浮点数在内存中的存储
3.1.举例说明
这个段代码会输出什么呢?
会是:9 9.000000 9 9.000000吗?
让我们运行一下
int main()
{
int n = 9;
float *pFloat = (float *)&n;
printf("n的值为:%d\n",n);
printf("*pFloat的值为:%f\n",*pFloat);
*pFloat = 9.0;
printf("num的值为:%d\n",n);
printf("*pFloat的值为:%f\n",*pFloat);
return 0;
}
那为什么只有n和*pFloat的值和我们预想一样,其他值的输出差距如此大?那这是不是说明了浮点数的存储和整数的存储不同呢?那么接下来就让我们来了解一下浮点数在内存中的存储的。
3.2浮点数在内存中的存储规则
3.2.1IEEE 754规定
1.(-1)^S * M * 2^E2.(-1)^S 表示符号位,当 S=0 , V 为正数;当 S=1 , V 为负数。3.M 表示有效数字,大于等于 1 ,小于 2 。4.2^E 表示指数位。
3.2.2 M和E特别规定


1.M:
前面说过, 1≤M<2 ,也就是说,M可以写成 1.xxxxxx 的形式,其中xxxxxx表示小数部分。IEEE 754规定,在计算机内部保存M时,默认这个数的第一位总是1,因此可以被舍去,只保存后面的 xxxxxx部分。比如保存1.01的时 候,只保存01,等到读取的时候,再把第一位的1加上去。这样做的目的,是节省1位有效数字。以32位 浮点数为例,留给M只有23位, 将第一位的1舍去以后,等于可以保存24位有效数字。
2.E
首先,E为一个无符号整数(unsigned int)
这意味着,如果 E 为 8 位,它的取值范围为 0~255 ;如果 E 为 11 位,它的取值范围为 0~2047 。但是,我们知道,科学计数法中的E 是可以出现负数的,所以IEEE 754 规定,存入内存时 E 的真实值必须再加上一个 中间数 。对于 8位 的 E ,这个 中间数 是127 ;对于 11位 的 E ,这个中间数是 1023 。比如, 2^10 的 E 是 10 ,所以保存成 32 位浮点数时,必须保存成 10+127=137 ,即 10001001。
这时,浮点数就采用下面的规则表示,即指数 E 的计算值减去 127 (或 1023 ),得到真实值,再将 有效数字M 前加上第一位的 1 。比如:0.5 ( 1/2 )的二进制形式为 0.1 ,由于规定正数部分必须为 1 ,即将小数点右移 1 位,则为1.0*2^(-1) ,其阶码为 -1+127=126 ,表示为 01111110,而尾数1.0 去掉整数部分为 0 ,补齐 0 到 23 位 00000000000000000000000。则其二进 制表示形式为:0 01111110 00000000000000000000000
这时,浮点数的指数 E 等于 1-127 (或者 1-1023 )即为真实值,有效数字 M 不再加上第一位的 1 ,而是还原为 0.xxxxxx 的小数。这样做是为了表示 ±0 ,以及接近于 0的很小的数字。
这时,如果有效数字 M 全为 0 ,表示 ± 无穷大(正负取决于符号位 s );
那么在了解了浮点数在内存中的存储之后我们再回过头去看举的例子
int main()
{
int n = 9;
//n为整数在内存中存储是:00000000 00000000 00000000 00001001
float* pFloat = (float*)&n;
printf("n的值为:%d\n", n);
//按照整数读取:9
printf("*pFloat的值为:%f\n", *pFloat);
//因为*pFloat是浮点型,pFloat指向的地址存储的是:0 00000000 00000000000000000001001
//S=0 E=00000000 M=00000000000000000001001
//是一个很小的接近于0的正数,所以用十进制小数表示就是0.000000
*pFloat = 9.0;
//9的二进制:1001
//*pFloat是小数,因此按照浮点数在内存的规则:
// S=0, E=3+127,转化为二进制即为:10000010。
//所以为0 10000010 00100000000000000000000
printf("num的值为:%d\n", n);
//因为pFloat指向的是n的地址,n在内存中的存储也为0 10000010 00100000000000000000000
//按照整数的取的方法n为:1091567616
printf("*pFloat的值为:%f\n", *pFloat);
//按照浮点数的读取法:9.000000
return 0;
}