本文共 5604 字,大约阅读时间需要 18 分钟。
入门学习计算机第十五天—数据的存储
编译器:Microsoft Visual Studio 2010
前言
记录第十五天学习C语言的博客。
深度剖析数据在内存中的存储
数据类型介绍
内置类型:
char 字符数据类型 short 短整型 int 整型 long 长整型 long long 更长的整型 float 单精度浮点数 double 双精度浮点数类型的意义:
1、使用这个类型开辟内存空间的大小。(大小决定了使用范围) 2、如何看待内存空间的视角。 如何理解2,例子:int main(){ int a = 10; float f =10.0; return 0;}
调试状态下,调出内存窗口,取出a的地址
取出f的地址 int类型和float类型的存储值的方式不同。所以就是看待内存空间的视角不同。整型家族:
char unsigned char 无符号的char(范围是0 - 255) signed char 有符号的char(范围是-128 - 127) short unsigned short [int] 无符号的short signed short [int ] 有符号的short int unsigned int 无符号int signed int 有符号int long unsigned long [int] 无符号long signed long [int] 有符号long浮点型家族
float
double
构造类型:
- 数组类型 eg:int [10] char[5]
- 结构体类型 struct
- 枚举类型 enum
- 联合类型 union
指针类型:
int* pi char* pc float* pf void* pv
空类型:
void 表示空类型(无类型) 通常引用于函数的返回类型,函数的参数,指针类型整型在内存中的存储
int a = 20;int b =-10;
a分配了四个字节的空间,那如何存储?
b也分配了四个字节的空间了解以下的概念:
计算机中的有符号数有三种表示方法,即原码,反码,补码。
三种表示方法均有符号位和数值位两部分,符号位都是用0表示“正”,用1表示“负”,而数值位三种表示方法各不相同。 有符号正数,无符号数原码,反码,补码相同。
原码
直接将二进制按照正负数的形式翻译成二进制就可以。反码
将原码的符号为不变,其他位依次按位取反就可以得到。补码
反码+1得到补码。int a = 20;//a是正整数,原反补相同//00000000000000000000000000010100 - 原码//00000000000000000000000000010100 - 反码//00000000000000000000000000010100 - 补码//二进制转化为十六进制,每四个二进制转换位一个十六进制位//0000 0000 0000 0000 0000 0000 0001 0100//0x00000014int b = -10;//10000000000000000000000000001010 - 原码//11111111111111111111111111110101 - 反码//111111111111111111111111 1111 0110 - 补码//0xFFFFFFF6
对于整型来说,数据在内存中其实存放的是补码
为什么呢? 使用补码,可以将符号位和数值统一处理,同时加法和减法也可以统一处理(CPU只有加法器),此外补码与原码相互转换,其运算过程是相同的,不需要额外的硬件电路。eg:好比电脑计算1-1,会先转化为1+(-1)
1的补码:00000000000000000000000000000001 -1的补码:11111111111111111111111111111111 相加:100000000000000000000000000000000,33位了,舍弃最高位。 结果是:00000000000000000000000000000000了解到了a =20 存储在内存中是0x00000014,但是为什么倒着顺序放的?
再了解以下概念:
大小端:大端(存储)模式:是指数据的低位保存在内存的高地址中,而数据的高位,保存在内存的低址中。
小端(存储)模式:是指数据的地位保存在内存的低地址中,而数据的高位,保存在内存的高位中。
通过这样的概念,&a会发现
处于低位的14存储在了低地址处,所以是小端存储模式。设置一个代码,告诉我们当前机器的字节序是什么?
int check_sys(){ int a = 1; return *(char*) &a;//只访问a的第一个地址,如果为1,则说明是小端,如果为0,则是大端。}int main(){ int ret = check_sys(); if(ret == 1) printf("小端"); else printf("大端"); return 0;}
以下代码输出的结果是?
int main(){ char a =-1; signed char b = -1; unsigned char c = -1; printf("a=%d b=%d c=%d\n"); return 0;}
输出的结果是-1,-1,255
为什么?char a =-1;//10000000000000000000000000000001 - 原码//11111111111111111111111111111111 - 补码//因为a只能存储8个字节//11111111 当要输出a为整型时,a要进行整型提升,前面补符号位//11111111111111111111111111111111 所以输出的是-1signed char b =-1;//b与a同理unsigned char c = -1;//11111111111111111111111111111111 - 补码//c也只能存储8个字节//11111111 当要输出c为整型时,c要进行整型提升,但是c是无符号,所以补0//00000000000000000000000011111111 - 无符号数,补码原码相同,所以答案是255。
2、以下代码输出的结果是?
int main(){ char a = -128; printf("%u",a); return 0;}
输出的结果是:
int main(){ char a = -128; //10000000000000000000000010000000 -原码 //11111111111111111111111101111111 -反码 //11111111111111111111111110000000 -补码 //10000000 char类型只能存储2个字节 8个bit //11111111111111111111111110000000 整型提升,有符号数要补符号位 //但是输出的是十进制无符号的整型 printf("%u",a); return 0;}
所以就是上述结果。
!!!!!!要记住
当char a = 128时,char类型是不能存储128,但是可以把128看作127+1,等于-128
int main(){ char a = 128; printf("%u",a); return 0;}
所以输出的结果依然是:
还有一题:int main(){ int i = -20; unsigned int j = 10; printf("%d\n", i+j);//按照补码的形式进行运算,最好转化为有符号整数 return 0;}
//11111111111111111111111111101100 - 20的补码//00000000000000000000000000001010 - 10的原码//11111111111111111111111111110110 - 相加//111111111111111111111111111110101 - 反码//1000000000000000000000000000001010 - 原码
结果就是-10
又来一题
int main(){ unsigned int i; for(i = 9; i>=0; i--) { printf("%u\n",i); } return 0;}
输出的结果是9,8,7,6,5,4,3,2,1,0,死循环。
因为i是unsigned int类型,永远>=0居然还有题目:
int main(){ char a [1000]; int i ; for(i =0; i<1000; i++) { a[i] = -1-i; } printf("%d\n",strlen(a)); return 0;}
输出的结果是255
需要结合上图: 当i =0时,结果是-1, i =1,结果是-2以此类推,到了i=127时,结果是-128, i = 129,结果会变成127 再依次类推到 3,2,1,0。strlen遇到’\0’就会停止,由于是char类型的数组,’\0’的ASCII码值为0,所以到0时,strlen就停止了计数。不算上0,一共是255个元素。555555这个题做不完了:
以下输出的结果是什么:int main(){ unsigned char i =0; for(i = 0; i<= 255; i++) { printf("hello\n"); } return 0;}
输出的结果是死循环的hello,unsigned char的取值范围是0~255,所以i<=255,恒成立。
浮点型在内存中存储
常见的浮点数:
3.1415 1E10
浮点数家族:
float double long double 浮点数表示的范围:float.h定义浮点数存储
int main(){ int n = 9; float* pFloat = (float*)&n; printf("n的值为:%d\n",n); printf("*pFloat的值位:%f\n",*pFloat); *pFloat = 9.0; printf("n的值为:%d\n",n); printf("*pFloat的值为:%f\n",*pFloat); return 0;}
输出的结果
与想象中区别非常大。为什么呢?需要了解浮点数的存储数据的方式。
根据国际标准IEEE(电气和电子工程协会)754,任意一个二进制浮点数V可以表示成下面的形式:
(-1)^S * M * 2^E
(-1)^S表示符号位,当S=0,V为正数;当S=1,V为负数 M表示有效数字,大于等于1,小于2 2^E表示指数位
举例来说十进制9.0,先转化为二进制1001.0 ,再用科学计数法表示为1.001 * 2^3
为正数,所以是(-1)^0 * 1.001 * 2 ^ 3S为0 ,M为1.001 , E为3
综合上面的存入浮点数方法可知,float a =9.0可以表示为: 0100 0001 0001 0000 0000 0000 0000 0000 转换为十六进制 0x41100000 因为当前机器为小端存储模式,低位数字存在低地址,高位数字存在高地址。浮点数取出:
当有一个二进制数:0100 0001 0001 0000 0000 0000 0000 0000
当E不为全0或者全1时:
这时浮点数就采用下面的规则表示:即指数E的计算值减去127(或者1023),的到真实值,再将有效数字M前加上第一位的1。E为全0时:
这时,浮点数的指数E等于1-127(或者1-1023)即为真实值,有效数字M不再加上第一位的1,而是还原为0.xxxxxxxxxx的小数。这样做是为了表示±0,以及接近0的很小的数字。E为全1时:
这时,如果有效数字M全为0,表示±无穷大(正负号取决于符号位S)再次回到浮点数9.0的问题
int main(){ int n = 9; float* pFloat = (float*)&n; printf("n的值为:%d\n",n); printf("*pFloat的值位:%f\n",*pFloat); *pFloat = 9.0; printf("n的值为:%d\n",n); printf("*pFloat的值为:%f\n",*pFloat); return 0;}int a =9,在内存中二进制为 0 00000000 00000000000000000001001 E为全0,真实值为1-127=-126 转换为十进制数就为(-1)^0 * 0.00000000000000000001001 * 2^(-126),这个值无限接近于0,所以输出的时候为0.000000
因为浮点数9.0在内存中二进制为0100 0001 0001 0000 0000 0000 0000 0000,用十进制的方式输出就是
转载地址:https://blog.csdn.net/xiaotangyu7dong/article/details/115973890 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!