【提升C语言基础知识】数据在内存中的存储

1.整数在内存中的存储

1.1 原码,反码,补码

整数(占4个字节,即32个bit)的二进制表示方法有3种,即原码,反码,补码,三种表示方法均有符号位数值位两部分

原码:直接将数值按照正负数的形式翻译成二进制得到的就是原码

反码:将原码符号位最高位,0为正,1为负不变,其余位按位取反得到反码

补码反码+1得到补码 (补码得到原码也可以使用 取反,+1 的操作)

正整数的原,反,补码都相同,如下

int a = 10;
//00000000000000000000000000001010 - 原码
//00000000000000000000000000001010 - 反码
//00000000000000000000000000001010 - 补码

负整数的原,反,补码各不相同,如下

int b = -10;
//10000000000000000000000000001010 - 原码
//11111111111111111111111111110101 - 反码
//11111111111111111111111111110110 - 补码

对于整型来说:数据存放内存中其实存放的是二进制的补码

1.2 大小端问题

我们设置一个整型的n的地址为 0x11223344 

#include <stdio.h>
int main()
{
	int n = 0x11223344;//两个16进制位表示8个2进制位
	return 0;
}

 当调试查看内存的存放的时候,发现地址存放顺序是反的,为 44 33 22 11,我们需要注意:

1.数据在内存中储存的是二进制的补码

2.在调试窗口观察时,为方便显示,显示的是16进制

3.存放顺序是倒着的

1.2.1大小端字节序存储(以字节讨论)

我们上面的地址 0x11223344  可以看成 0x 11 22 33 44 ,在10进制中比如123这个数 ,3是个位,2是十位,1是百位,右边是低位,左边是高位,可以理解为右边是小端,左边是大端,在16进制中也一样,44是低位,11是高位

大端字节序存储:把一个数据的低位字节的内容储存到高地址处,把高位字节的内容储存到低地址处

小端字节序存储:把一个数据的低位字节的内容储存到低地址处,把高位字节的内容储存到高地址处

所以当前存储方式为 小端字节序存储

1.3 相关练习

练习1

设计一个程序判断当前机器的字节序

参考答案

#include <stdio.h>
int main()
{
	int a = 1;
	if (*(char*)&a == 1)  // &a :01 00 00 00
		printf("小端"); 
	else                  // &a :00 00 00 01
		printf("大端"); 
	return 0;
}
练习2

下面代码的输出结果是什么?

#include <stdio.h>
int main()
{
	char a = -1;
	signed char b = -1;
	unsigned char c = -1;
	printf("a=%d, b=%d, c=%d", a, b, c);
}

答案

a=-1, b=-1, c=255

解析

char类型占1个字节,就是8个bit位,取的是后8位;

signed char是有符号char,最高位是符号位,其余7位是数值位,正号补码范围是00000000(0)~01111111(127),负号补码范围是10000000(-128)~11111111(-1),为了不浪费数据,补码为10000000的默认为是-128,所以 signed char的取值范围是-128~127; unsigned char是无符号char,所有位都是数值位,从00000000(0)~11111111(255),所以unsigned char的取值范围是0~255;

用%d打印,类型需要提升为整型,signd补齐高位时看符号位,符号位是1补1,是0补0,unsignd全补0

练习3

下面程序结果是什么?

#include <stdio.h>
int main()
{
	char a[1000];
	int i;
	for (i = 0; i < 1000; i++)
	{
		a[i] = -1 - i;
	}
	printf("%d", strlen(a));
}

答案

255

解析

strlen计算的是‘\0’之前的元素个数,'\0'的ASCII码值是0;

2. 浮点数在内存中的存储

任意一个二进制浮点数V都可以表示成下面的形式:

 比如

所以浮点数的存储其实存的是S,M,E相关的值

对于32位的浮点数(float),最高1位存储符号位S,后8位存储指数E,剩下的23位存储有效数字M 

对于64位的浮点数(double),最高1位存储符号位S,后11位存储指数E,剩下的52位存储有效数字M 

 2.1 浮点数存的过程

有效数字M

因为1\leq M< 2,所以M可以写成1.xxxxxxde形式,其中xxxxxx表示小数部分

在计算机内部保存M时,默认这个数的第一位总是1,因此可以舍去,只保留后面的xxxxxx部分,如,保存1.01时,只保存小数点后的01,等读取时再把小数点前的1加上, 这样做的目的是节省1位有效数字,以32位浮点型为例,留23位保存M,将第一位舍去后,等于可以保留24位有效数字

指数E

E为一个无符号整数(unsigned int),这意味着,如果E为8位,它的取值范围为0~255,如果E为11位,范围是0~2047,但是科学计数法中E是可以出现负数的,所以规定存入内存时E的真实值必须加上一个中间值,对于8位E,这个中间值是127,对于11位,这个值是1023,比如,2^10的E是10,保存32位浮点数时,必须保存成10+127=137,即10001001

2.2 浮点数取的过程

指数E从内存中取出可分为3种情况

1)E不全为0,或E不全为1

此时指数E的计算值减去127(或1023),得到真实值,再将有效值M前加上第一位的1

如,0.5

十进制:0.5
二进制:0.1
由于正数部分必须为1,则
将小数点右移1位:1.0*2^(-1)
其补码为:(-1)+127=126
表示为:01111110
位数去掉整数部分为0
补齐0到23位:0 01111110 00000000000000000000000

2)E全为0

此时指数E等于1-127(或1-1023)为真实值,有效数字M不再加上第一位的1,而是还原为0.xxxxxx的小数,这样是为了表示\pm 0,以及接近于0的很小的数字

0 00000000 00010000000000000000000

3)E为全1

此时代表正负无穷大的数字

0 11111111 00010000000000000000000

2.3相关练习

输出结果是?

#include <stdio.h>
int main()
{
	int n = 9;
	float* p = (float*)&n;
	printf("n的值为:%d\n", n);
	printf("*p的值为:%f\n", *p);

	*p = 9.0;
	printf("n的值为:%d\n", n);
	printf("*p的值为:%f\n", *p);
	return 0;
}

答案:

n的值为:9
*p的值为:0.000000
n的值为:1091567616
*p的值为:9.000000

解析:

以整型视角存储9

00000000 00000000 00000000 00001001 //9的二进制
0 00000000 000000000000000000001001 //p认为是float类型

当内存中E全为0时,这个数字无限接近0,用%f打印小数点后6为,结果为0.000000

以float视角存储9.0

1001.0 //二进制
1.001*2^3 //化为二进制科学计数法

此时S=0,M=1.001,E=3

0 10000010 00100000000000000000000 //浮点数视角
01000001 00010000 00000000 00000000 //整型视角

将上面的二进制用%d打印出来就是一个很大的数字

本次分享就到这里,感谢阅读!

  • 36
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值