数据在内存中的存储(2)

我们继续看我们没做完的题

练习2

复制代码
#define  _CRT_SECURE_NO_WARNINGS
#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);
	return 0;
}

这里先给大家具体看一下数据类型的取值范围

但是还有一个问题,最后我要输出的结果的占位符是"%d",打印的是十进制数字,所以我们还要进行"整型提升",整型提升补的是符号位

所以最后输出的结果就是"-1""-1""255"

练习3

复制代码
#define  _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main()
{
	char a = -128;
	char b = 128;
	printf("%u\n%u\n", a,b);
	return 0;
}

虽然只取低八位的比特位,但打印的是无符号十进制数,所以打印的是整型提升后的所有比特位

练习4

复制代码
#define  _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>
int main()
{
	char a[1000];  //下标是0到999
	int i;
	for (i = 0; i < 1000; i++)
	{
		a[i] = -1 - i;  //-1,-2,-3......
	}
	printf("%d", strlen(a));
	return 0;
}

这里先给大家另一个理解方式

如果顺时针方向循环转动就是0,1,2,3,......126,127,-128,-127......-2,-1,0,1,2......

逆时针方向循环转动就是-1,-2,-3,-127,-128,127,126......2,1,0,-1,-2......

而这里采用逆时针转动,直到遇到"\0"结束,而"\0"的ascii值为0,strlen遇到\0会停下来

练习5

复制代码
#define  _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
unsigned char i = 0;
int main()
{
	for (i = 0; i <= 255; i++)
	{
		printf("hello world\n");
	}
	return 0;
}

首先无符号char的取值范围是0~255,当i==255的时候就要加1,此时加1的结果不是256,而是0

所以这里会陷入死循环

复制代码
#define  _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main()
{
	unsigned int i;
	for (i = 9; i >= 0; i--)
	{
		printf("%u\n", i);
	}
	return  0;
}

当i==0的时候就要再减1,此时从数学上讲是-1,但unsigned int i=-1的时候是一个无符号数,但-1是一个有符号数,所以就会转化成一个非常大的数,然后再对这个非常大的数循环减

练习6

复制代码
#define  _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
//X86环境⼩端字节序
int main()
{
	int a[4] = { 1, 2, 3, 4 };
	int* ptr1 = (int*)(&a + 1);
	int* ptr2 = (int*)((int)a + 1);
	printf("%x,%x", ptr1[-1], *ptr2);
	return 0;
}

&a代表取出的是整个数组的地址然后加1,就跳到数组最后一个地址的后一位,然后再对地址-1就回到数组最后一个字符的地址后解引用

(int)a表示把数组首元素地址取出来后转化为整型数据后加1就是地址加1,比如0x11223343+1后就是0x11223344(多了一个字节),然后ptr2是一个整型类型的指针,解引用后就是多了一个字节的地址的数据,但这是小端存储,就要还原该有的数据,就是02000000

浮点数在内存中的存储

常见的浮点数:3.14159、1E10等,浮点数家族包括:float,double,long double等

浮点数的范围在float.h头文件中展现

我们先给大家一段代码来引入

复制代码
#define  _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
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;
}

这里如果从整数的形似输出浮点数不一样,从浮点数的形式输出整数也不一样,这里就先理解这么多,其他的下面来理解

浮点数的存储

根据国际标准IEEE(电气和电子工程协会)754,任意⼀个二进制浮点数V可以表示成下面的形式

任何一个十进制的浮点数都可以转化为二进制的浮点数,而二进制的浮点数可以用该公式表示

比如5.5这个浮点数我们可以写成101.1这个二进制数形式,还可以表示成(-2)^0*101.1*2^0

s=0,m=101.1,e=0

再比如,5.0这个浮点数,二进制101.0,相当于1.01*2^2,那么,按照上面V的格式,可以得出S=0,M=1.01,E=2

所以浮点数的存储其实就是在存储s,m,e这三个相关的值

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为无符号整数(unsignedint)

这意味着,如果E为8位,它的取值范围为0~255;如果E为11位,它的取值范围为0~2047。但是,我 们知道,科学计数法中的E是可以出现负数的,所以IEEE754规定,存⼊内存时E的真实值必须再加上 ⼀个中间数,对于8位的E,这个中间数是127;对于11位的E,这个中间数是1023。⽐如,2^10的E是 10,所以保存成32位浮点数时,必须保存成10+127=137,即10001001

复制代码
#define  _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main()
{
    float f = 5.5f;//
    //5.5
    //101.1
    //1.011*2^2
    //(-1)^0*1.011*2^2
    //s=0 m=1.011 e=2
    //但是e要加上一个中间值127才可以正确存储,所以e是129
    //m存储的是小数点后面的数,就是011,但后面还有很多位,就在后面补0
    //m就是01100000000000000000000,补够32个比特位
    //所以01000000101100000000000000000000(十六进制:40B00000)就是我们存到内存中的值

    return 0;
}

说明这里是小端存储,但是不是所有浮点数都可以准确的存储在内存中,必定会有差值

浮点数取的过程

现在我们回到当开始的代码

复制代码
#define  _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
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;
}

站在pFloat的角度,它认为它指向的是一个float类型的数据0 00000000 00000000001001,此时它的视角就是s=0,e=00000000,m=00000000001001

当内存中的e全为0的时候,真的e就是1-127=-126

有效的m不再取出数字后加1

最后结果就是(-1)^0*0.0 00000000 0000000000100182^-126,第二个输出就是0.000000

而9.0的二进制是1001.1,相当于1.001*2^3或(-1)^0*1.001*2^3,此时s=0,m=1.001,e=3

现在我把这个数9.0存到n里面去,0 10000010 (m不会把1.001前面的1村存进去,只会存001,但位数不够,在后面补0)00100000000000000000000,现在以%d(有符号数)的形式打印,最高位是0,所以第三个输出就是这个数转化为十进制数

相关推荐
拾贰_C3 小时前
【python| pytorch】卸载py库,手动法
开发语言·pytorch·python
认真学GIS3 小时前
逐3小时降水量!全国2421个气象站点1951-2024年逐3小时尺度长时间序列降水量(EXCEL格式)数据
人工智能·算法·机器学习
龙山云仓3 小时前
No098:黄道婆&AI:智能的工艺革新与技术传承
大数据·开发语言·人工智能·python·机器学习
是喵斯特ya3 小时前
python开发web暴力破解工具(基础篇)
开发语言·python·web安全
智航GIS3 小时前
ArcGIS大师之路500技---039趋势面法
算法·arcgis
量子炒饭大师3 小时前
Cyber骇客的LIFO深渊与FIFO管道 ——【初阶数据结构与算法】栈与队列
c语言·数据结构·c++·链表
智航GIS3 小时前
ArcGIS大师之路500技---038反距离权重法
算法·arcgis
Legendary_0083 小时前
Type-C一拖三快充线的核心优势与LDR6020方案深度解析
c语言·开发语言·电脑
YGGP3 小时前
【Golang】LeetCode 31. 下一个排列
算法·leetcode