C语言进阶——数据的存储

知识的价值不在于占有,而在于使用。这篇文章主要讲述C语言进阶——数据的存储相关的知识,希望能为你提供帮助。
数据的存储深度剖析数据在内存中的存储

  1. 数据类型详细介绍
  2. 整型在内存中的存储:原码、反码、补码
  3. 大小端字节序介绍及判断
  4. 浮点型在内存中的存储解析
数据类型详细介绍
  1. C语言类型
  2. 类型的意义
  3. 类型的基本归类
数据类型在内存中的存储一个变量的创建是要在内存中开辟空间的,空间的大小根据变量的数据类型决定。
原码、反码、补码
int main() { int a=20; //二进制 //0000 0000 0000 0000 0000 0000 0001 0100 原码 //0000 0000 0000 0000 0000 0000 0001 0100 反码 //0000 0000 0000 0000 0000 0000 0001 0100 补码 //十六进制 //0x00 00 00 14int b=-10; //1000 0000 0000 0000 0000 0000 0000 1010 原码 //1111 1111 1111 1111 1111 1111 1111 0101 反码 //1111 1111 1111 1111 1111 1111 1111 0110 补码 //补码的十六进制 //0xFF FF FF F6//整数在内存中是倒着存的 //例如: //a的存储:0x14 00 00 00 //b的存储:0xF6 FF FF FFreturn 0 ; }

梳理
在实际代码运行中,数据并不是按照我们写的数值顺序存储。为什么呢?
大小端介绍1.什么是大端小端
2.为什么有大端和小端:
百度2015年系统工程师笔试题
//小程序 int check_sys(void) { int a=1; char* p=(char*)& a; if(*p==1) { return 1; } else { return 0; } }//优化1 int check_sys(void) { int a=1; char* p=(char*)& a; //返回1,小端 //返回0,大端 return *p; }//优化2 int check_sys(void) { int a=1; return *(char*)& a; }//指针类型的意义: //1.指针类型决定了指针解引用操作符能访问几个字节:char*p;*p访问了一个字节,int*p;*p访问4个字节 //2.指针类型决定了指针+1,-1,加的或减的是几个字节;char*p;p+1,跳过一个字节,int*p;p+1,跳过4个字节int main() { //写一段代码告诉我们当前机器的字节序 //返回1,小端 //返回0,大端 int ret = check_sys(); if(ret==1) { printf("小端\\n"); } else { printf("大端\\n"); }return 0 ; }

练习1
//练习1 //输出什么? #include < stdio.h> int main() { char a=-1; //1000 0000 0000 0000 0000 0000 0000 0001 //1111 1111 1111 1111 1111 1111 1111 1110 //1111 1111 1111 1111 1111 1111 1111 1111 //1111 1111 char类型只能存储1个字节-8个bitsigned char b=-1; //1000 0000 0000 0000 0000 0000 0000 0001 //1111 1111 1111 1111 1111 1111 1111 1110 //1111 1111 1111 1111 1111 1111 1111 1111 //11 11 11 11unsigned char c=-1; //0000 0000 0000 0000 0000 0000 1111 1111 //255printf("a=%d,b=%d,c=%d",a,b,c); return 0 ; }//输出:a=-1,b=-1,c=255

练习2
//练习2 //输出什么 int main() { char a=-128; //1000 0000 0000 0000 0000 0000 1000 0000 原码 //1111 1111 1111 1111 1111 1111 0111 1111 反码 //1111 1111 1111 1111 1111 1111 1000 0000 补码 //1000 0000 //1111 1111 1111 1111 1111 1111 1111 1111 整型提升(整型提升按照符号位提升)printf("%u\\n",a); return 0; }

有符号的char的范围是:-128~127
无符号的char的范围是:0~255
练习3
//按照补码的形式进行运算,最后格式化成为有符号整数 int main() { int i=-20; //1000 0000 0000 0000 0000 0000 0001 0100 原码 //1111 1111 1111 1111 1111 1111 1110 1011 反码 //1111 1111 1111 1111 1111 1111 1110 1100 补码unsigned int j=10; //0000 0000 0000 0000 0000 0000 0000 1010 补码//补码相加 //1111 1111 1111 1111 1111 1111 1110 1100 补码 //0000 0000 0000 0000 0000 0000 0000 1010 补码 //1111 1111 1111 1111 1111 1111 1111 0110 结果 //1111 1111 1111 1111 1111 1111 1111 0101 结果的反码 //1000 0000 0000 0000 0000 0000 0000 1010 结果的原码 //-10 结果的十进数printf("%d\\n",i+j); //-10return 0 ; }//输出:-10

练习4
int main() { unsigned int i; //当i为无符号数时,减到0的时候再往下减的数还是整数,会把符号位的负数标志1加进去,始终是正数 //可是当i减到负数,是一个很大的数时,i!=9,怎么进入循环? for(i=9; i> =0; i++) { printf("%u\\n",i); }return 0 ; }//结果:无限循环

练习5
int main() { char a[1000]; //char的范围:-128~127 int i; for(i=0; i< 1000; i++) { a[i]=-1-i; //-1~128 127~0 } printf("%d",strlen(a)); //255return 0 ; }//输出:255

练习6
unsigned char i=0; int main() { for(i=0; i< =255; i++)//255+1=0 { printf("hello world\\n"); }return 0 ; }//结果:无限打印hello world

浮点型在内存中的存储解析常见的浮点数:
浮点数存储的例子:
int main() { int n=9; float *pFloat=(float *)& n; printf("n的值为:%d\\n",n); //9 printf("*pFloat的值为:%d\\n",*pFloat); //0.000000*pFloat=9.0; printf("num的值为:%d\\n",n); //1091567616 printf("*pFloat的值为:%f\\n",*pFloat); //9.000000return 0 ; }//输出: //9 //0.000000 //1091567616 //9.000000//总结:整型和浮点数存储不同

浮点数的存储的详细解读
【C语言进阶——数据的存储】根据国际标准IEEE(电气和电子工程协会)754,任意一个二进制浮点数V可以表示成下面的形式:
IEEE 754规定:
  • 对于32位的浮点数,最高的1位是符号位S,接着的8位是指数E,剩下的23位为有效数字M
  • 对于64位的浮点数,最高的1位是符号位S,接着的11位是指数E,剩下的52位位有效数字M
IEEE 754对有效数字M和指数E,还有一些特别规定。前面说1≤M< 2,也就是说,M可以写成1.xxxxxx的形式,其中xxxxxx表示小数部分。
IEEE 754规定,在计算机内部保存M时,默认这个数的第一位总是1,因此可以被舍去,只保存后面的xxxxxx部分。比如保存1.01的时候,只保存01,等到读取的时候,再把第一位的1加上去。这样做的目的,是节省1位有效数字。以32位浮点数为例,留给M只有23位,将第一位的1舍去以后,等于可以保存24位有效数字。
至于指数E,情况就比较复杂。
首先,E为一个无符号整数( unsigned int )这意味着,如果E为8位,它的取值范围为0~255; 如果E为11位,它的取值范围为0~2047。但是,我们知道,科学计数法中的E是可以出现负数的,所以IEEE 754规定,存入内存时E的真实值必须再加上一个中间数127/1023,对于8位的E,这个中间数是127; 对于11位的E,这个中间数是1023。比如,2^10的E是10,所以保存成32位浮点数时,必须保存成10+127=137,即10001001。
int main() { float f = 5.5; //5.5 //101.1 //(-1)^0 * 1.011 * 2^2 //S = 0 //M = 1.011 //E = 2 (E=2+127=129) //S(1bit)E(8bit)M(23bit) //010000001 011 0000 0000 0000 0000 0000 //0100 0000 1011 0000 0000 0000 0000 0000 //4 0 B 0 0 0 0 0return 0; }

然后,指数E从内存中取出还可以再分成三种情况∶
E不全为0或不全为1
E全为0
E全为1
int main() { int n=9; //0000 0000 0000 0000 0000 0000 0000 1001 原码/补码float *pFloat=(float *)& n; printf("n的值为:%d\\n",n); //9printf("*pFloat的值为:%f\\n",*pFloat); //0.000000 //0000 0000 0000 0000 0000 0000 0000 1001 //0.000 0000 0000 0000 0000 1001 //(-1)^0 * 0.000 0000 0000 0000 0000 1001 * 2^-126 //结果无限接近于0,且只打印小数点后6位*pFloat=9.0; //1001.0 //1.001 * 2^3 //(-10)^0 * 1.001 *2^3 //0 10000010 001 0000 0000 0000 0000 0000 //printf("num的值为:%d\\n",n); //1091567616 //1001.0 //1.001 * 2^3 //(-10)^0 * 1.001 *2^3 //0 10000010 001 0000 0000 0000 0000 0000 //1091567616printf("*pFloat的值为:%f\\n",*pFloat); //9.000000return 0 ; }


    推荐阅读