C语言进阶——数据的存储
Posted wx60f68848f3833
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C语言进阶——数据的存储相关的知识,希望对你有一定的参考价值。
数据的存储
深度剖析数据在内存中的存储
- 数据类型详细介绍
- 整型在内存中的存储:原码、反码、补码
- 大小端字节序介绍及判断
- 浮点型在内存中的存储解析
数据类型详细介绍
-
C语言类型
-
类型的意义
-
类型的基本归类
数据类型在内存中的存储
一个变量的创建是要在内存中开辟空间的,空间的大小根据变量的数据类型决定。
原码、反码、补码
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 14
int 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 FF
return 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个bit
signed 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 11
unsigned char c=-1;
//0000 0000 0000 0000 0000 0000 1111 1111
//255
printf("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); //-10
return 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));//255
return 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.000000
return 0 ;
}
//输出:
//9
//0.000000
//1091567616
//9.000000
//总结:整型和浮点数存储不同
浮点数的存储的详细解读
根据国际标准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)
//0 10000001 011 0000 0000 0000 0000 0000
//0100 0000 1011 0000 0000 0000 0000 0000
//4 0 B 0 0 0 0 0
return 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);//9
printf("*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
//1091567616
printf("*pFloat的值为:%f\\n",*pFloat);//9.000000
return 0 ;
}
以上是关于C语言进阶——数据的存储的主要内容,如果未能解决你的问题,请参考以下文章
我的C语言学习进阶之旅解决 Visual Studio 2019 报错:错误 C4996 ‘fscanf‘: This function or variable may be unsafe.(代码片段
我的C语言学习进阶之旅解决 Visual Studio 2019 报错:错误 C4996 ‘fscanf‘: This function or variable may be unsafe.(代码片段