您现在的位置是:首页 >其他 >学C的第二十三天【继续深度剖析数据在内存中的存储:3. 浮点型在内存中的存储(重点);练习:1. 有序序列判断;2. 获得月份天数(多组输入);3. 使用指针打印数组内容;4. 使用指针使字符串逆序】网站首页其他

学C的第二十三天【继续深度剖析数据在内存中的存储:3. 浮点型在内存中的存储(重点);练习:1. 有序序列判断;2. 获得月份天数(多组输入);3. 使用指针打印数组内容;4. 使用指针使字符串逆序】

高高的胖子 2024-08-08 12:01:02
简介学C的第二十三天【继续深度剖析数据在内存中的存储:3. 浮点型在内存中的存储(重点);练习:1. 有序序列判断;2. 获得月份天数(多组输入);3. 使用指针打印数组内容;4. 使用指针使字符串逆序】

=========================================================================

 相关代码gitee自取:C语言学习日记: 加油努力 (gitee.com)

 =========================================================================

接上期:学C的第二十二天【深度剖析数据在内存中的存储:1. 数据类型介绍;2. 整型在内存中的存储】_高高的胖子的博客-CSDN博客

========================================================================= 

                 

3. 浮点型在内存中的存储

(一). 字面(直接写出来的)浮点数:

                          

3.14159

                     

1E10        -->        1.0 * 10^10     (1.0 乘 10的10次方)   -->        E底数10

                 

                

(二). 浮点数类型:

               

浮点数:数据乘以相关权重后,小数点位置可以浮动的

          

float        --        单精度浮点数        --        4字节

            

double        --        双精度浮点数        --        8字节

                 

long double        --        更长的双精度浮点数        --        8字节

            

(可在 float.h头文件 查看该类型范围等信息)         

              

            

(三). 浮点数存储的例子:

            

内存中存储数据的时候,

%f 浮点数的形式 获取数据

%d 整数的形式 获取数据

不一样

             

#include <stdio.h>

int main()
{
	int n = 9;//4字节

	float* pFloat = (float*)&n;
	//把 n 的地址强制转换为 float类型 赋给指针变量 pFloat
	printf("n的值为:%d
", n);
	//使用 %d 打印 n
	printf("*pFloat的值为:%f
", *pFloat);
	//使用 %f 打印 地址中的内容

	*pFloat = 9.0;
	//使用指针变量把 9.0 赋给 n的空间
	printf("num的值为:%d
", n);
	//使用 %d 打印 赋值后的 n
	printf("*pFloat的值为:%f
", *pFloat);
	//使用 %f 打印 赋值后的 地址中的内容

	return 0;
}

          

说明 浮点数整型 在内存中 存储获取形式不一样的(存储方式有差异

(以 整型 存储,可以用 整型 获取,但不能用 浮点数类型 获取

              

               

(四). 浮点数存储规则

               

浮点数在计算机内部的表示方法

根据 国际标准IEEE(电气和电子工程协会)754 ,

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

              

(-1) ^ S * M * 2 ^ E

               

  • (-1) ^ S  表示 符号位,当 S=0, V为正数;当 S=1, V为负数

任何数0次方都等于 1-11次方等于 -1

            

  • M 表示 有效数字大于等于 1小于 2。                                 

(这里 V 是二进制浮点数,所以这里 1 <= M < 2 )

             

  • 2 ^ E  表示 指数位 。                                                                

(几进制 这里就是 几^E,这里 V 是二进制浮点数,所以是 2^E

“小数点移了几位”,这里的 E 就是 几 )

             

例子:

              

               

(五). IEEE 754 规定的 浮点数存储模型:

                 

float -- 单精度浮点数存储模型:

             

对于32位的浮点数float),

最高的1位符号位S

接着的8位指数E

剩下的23位有效数字M

             

                 

                 

double -- 双精度浮点数存储模型:

             

对于64位的浮点数double ),

最高的1位符号位S

接着的11位指数E

剩下的52位有效数字M

             

                 

            

(六). IEEE 754 对 有效数字M 和 指数E 的特别规定:

               

有效数字M:

           

前面说过,1<=M<2,也就是说,M可以写成 1.xxxxxx 的形式,其中 xxxxxx 表示小数部分

         

有效数字M的保存

IEEE 754 规定,在计算机内部保存M时,默认这个数的第一位总是1,因此可以被舍去,只保存后面的 xxxxxx 部分

小数点前面不存只存小数点后面

           

有效数字M的读取

比如保存 1.01 时,只保存 01 ,等到读取的时候,再把第一位的1加上去。这样做的目的,是为了节省1位有效数字

               

32位浮点数 为例,留给 M 只有23位,将第一位的1舍去以后,等于可以保存 24位有效数字

          (可以使保存的小数精度再高一点)                  

                            

                            

指数E(比较复杂):

         

(1). 指数E 在内存中的存储:E 为一个 无符号整数(unsigned int)

这意味着,如果 E8位,它的取值范围0~255;如果 E 11位,它的取值范围0~2047

            

但是,科学计数法中 E 可以出现负数的,所以 IEEE 754 规定存入内存E真实值必须再加上一个中间数(可以把E修正为正数,对于8位floatE ,这个中间数127;对于11位E double,这个中间数1023

            

比如,2^10E10,所以保存成 32位浮点数 时,必须保存10+127=137,即10001001

                

符号位S有效数字M指数E 例子:)

(计算符号位SS为0,符号为S为1,符号为

计算指数E移动位数 + 中间值

计算有效数字M小数点后的数,再用0补齐剩余位数

                

                   

(2). 取出内存中的 指数E(三种情况):E不全为0或不全为1

这时,浮点数 就采用下面的规则表示:

指数E 计算值 减去中间值127(或1023),得到真实值存储时是真实值+中间值,再在 有效数字M 加上第一位的1

(存储时把第一位的1舍去了,其实就是按存储的相反形式取出,怎么放进去怎么取出来)。

              

              

(3). 取出内存中的 指数E(三种情况):E全为0

指数E 是通过 真实值+中间值 算出来的,如果E全是0,(32位系统)说明E的真实值是 -127指数是-127说明这个值是非常小的

这时,直接规定浮点数的 指数E 等于 1-127(或者 1-1023 ),即为真实值

有效数字M 加上第一位的1,而是还原为 0.xxxxxx 的小数

这样做是为了表示 ±0 ,以及 接近于0 的很小的数字

              

              

(4). 取出内存中的 指数E(三种情况):E全为1

指数E 是通过 真实值+中间值 算出来的,如果E全是1,(32位系统)说明E的真实值是 128指数是128说明这个值是非常大的

这时,如果 有效数字M 全为0,表示 ±无穷大正负取决于符号位s

              

            

(七). 解释浮点数存储的例子:

#include <stdio.h>

int main()
{
	int n = 9;//4字节

	float* pFloat = (float*)&n;
	//把 n 的地址强制转换为 float类型 赋给指针变量 pFloat
	printf("n的值为:%d
", n);
	//使用 %d 打印 n
	printf("*pFloat的值为:%f
", *pFloat);
	//使用 %f 打印 地址中的内容

	*pFloat = 9.0;
	//使用指针变量把 9.0 赋给 n的空间
	printf("num的值为:%d
", n);
	//使用 %d 打印 赋值后的 n
	printf("*pFloat的值为:%f
", *pFloat);
	//使用 %f 打印 赋值后的 地址中的内容

	return 0;
}

                  

                  

(1). 整型存储,以 整型取出 和 浮点数取出:

                  

                  

(2). 浮点型存储,以 整型取出 和 浮点数取出:

                


                

练习:

                  

1. 有序序列判断:

             

(1). 方法一 -- 先输入数据再判断:

//描述
//输入一个整数序列,判断是否是有序序列,有序,指序列中的整数从小到大排序或者从大到小排序(相同元素也视为有序)。
//输入描述:
//第一行输入一个整数N(3≤N≤50)。
//第二行输入N个整数,用空格分隔N个整数。
#include <stdio.h>
int main()
{
	int n = 0;//数组长度
	//输入n:
	scanf("%d", &n);

	int arr[50] = { 0 };
	//输入数组数据:
	int i = 0;
	for (i = 0; i < n; i++)
	{
		scanf("%d", &arr[i]);
	}

	//判断是否有序:升序 降序,
	//有序的情况下 都是大于号 或 都是小于号
	int flag1 = 0; //表示升序,满足升序关系 --> flag1=1
	int flag2 = 0; //表示降序,满足降序关系 --> flag2=1
	//判断过程中,
	//如果 flag1一直为1,flag2一直为0,说明是升序,
	//如果 flag2一直为1,flag1一直为0,说明是降序
	//等于的话,flag1和flag2都是0

	//相邻两个数进行比较,n个数,比较n-1对
	for (i = 0; i < n-1; i++)
	{
		if(arr[i] < arr[i+1]) //前一个数 小于 后一个数
		{
			flag1 = 1;
		}
		else if (arr[i] > arr[i+1]) //前一个数 大于 后一个数
		{
			flag2 = 1;
		}
	}

	//判断完后,看 flag1 和 flag2 的情况来判断有无序
	if (flag1 + flag2 == 2)//说明既有大于又有小于
	{
		printf("unsorted
");
	}
	else
	//==1的话 说明有序,==0的话,说明都相当,也是有序
	{
		printf("sorted
");
	}

	return 0;
}

                   

(2). 方法二 -- 边输入边判断(输入两个数后就可以判断一次大小了):

//描述
//输入一个整数序列,判断是否是有序序列,有序,指序列中的整数从小到大排序或者从大到小排序(相同元素也视为有序)。
//输入描述:
//第一行输入一个整数N(3≤N≤50)。
//第二行输入N个整数,用空格分隔N个整数。
#include <stdio.h>
int main()
{
	int n = 0;//数组长度
	//输入n:
	scanf("%d", &n);

	int arr[50] = { 0 };

	//判断是否有序:升序 降序,
	//有序的情况下 都是大于号 或 都是小于号
	int flag1 = 0; //表示升序,满足升序关系 --> flag1=1
	int flag2 = 0; //表示降序,满足降序关系 --> flag2=1
	//判断过程中,
	//如果 flag1一直为1,flag2一直为0,说明是升序,
	//如果 flag2一直为1,flag1一直为0,说明是降序
	//等于的话,flag1和flag2都是0
	
	//输入数组数据 并 进行判断:
	int i = 0;
	for (i = 0; i < n; i++)
	{
		scanf("%d", &arr[i]);
		//输入进来后就可以判断了
		if (i >= 1)//i大于1说明输入了 arr[0] 和 arr[1]
		{
			if (arr[i] < arr[i - 1]) //一个数 小于 前一个数
			{
				flag1 = 1;
			}
			else if (arr[i] > arr[i + 1]) //一个数 大于 前一个数
			{
				flag2 = 1;
			}
		}
	}


	//判断完后,看 flag1 和 flag2 的情况来判断有无序
	if (flag1 + flag2 == 2)//说明既有大于又有小于
	{
		printf("unsorted
");
	}
	else
		//==1的话 说明有序,==0的话,说明都相当,也是有序
	{
		printf("sorted
");
	}

	return 0;
}

               

2. 获得月份天数(多组输入)

                   

(1). 方法一 -- 使用switch语句进行日期分类:

                     

(代码:)

//描述
//KiKi想获得某年某月有多少天,请帮他编程实现。输入年份和月份,计算这一年这个月有多少天。
//输入描述:
//多组输入,一行有两个整数,分别表示年份和月份,用空格分隔。
//输出描述:
//针对每组输入,输出为一行,一个整数,表示这一年这个月有多少天。
#include <stdio.h>

//平年:
// 1  2  3  4  5  6  7  8  9  10 11 12
// 31 28 31 30 31 30 31 31 30 31 30 31
// 
//闰年:
// 1  2  3  4  5  6  7  8  9  10 11 12
// -- 29 -- -- -- -- -- -- -- -- -- --

// 1 3 5 7 8 10 12 -- 31天
// 4 6 9 11 -- 30天
// 2月 特殊
int get_days_of_month(int y, int m)
{
	int d = 0; //该年该月天数
	switch (m)
	{
		case 1:
		case 3:
		case 5:
		case 7:
		case 8:
		case 10:
		case 12:
		{
			d = 31;
			break;
			// 1 3 5 7 8 10 12 -- 31天
		}
		case 4:	
		case 6:	
		case 9:	
		case 11:	
		{
			d = 30;
			break;
			// 4 6 9 11 -- 30天
		}
		case 2://二月特殊,进行判断
		{
			d = 28; //平年
			if ((y%4==0 && y%100!=0) || (y%400==0))
			{
				d += 1; //闰年:29天
			}
		}
	}
	return d;//返回天数
}

int main()
{
	int y = 0;//年
	int m = 0;//月

	//多组输入:
	while (scanf("%d %d", &y, &m) == 2)
	{
		int d = get_days_of_month(y, m);
		//写个函数计算该年该月的日期,
		//参数为 y 和 m
		//返回值为 对于日期
		printf("%d
", d);
	}

	return 0;
}

(自定义函数部分:)

(主函数部分:)

                             

(2). 方法二 -- 用数组存储各月的日期(妙呀~):

//描述
//KiKi想获得某年某月有多少天,请帮他编程实现。输入年份和月份,计算这一年这个月有多少天。
//输入描述:
//多组输入,一行有两个整数,分别表示年份和月份,用空格分隔。
//输出描述:
//针对每组输入,输出为一行,一个整数,表示这一年这个月有多少天。
#include <stdio.h>
//平年:
// 1  2  3  4  5  6  7  8  9  10 11 12
// 31 28 31 30 31 30 31 31 30 31 30 31
// 
//闰年:
// 1  2  3  4  5  6  7  8  9  10 11 12
// -- 29 -- -- -- -- -- -- -- -- -- --

// 1 3 5 7 8 10 12 -- 31天
// 4 6 9 11 -- 30天
// 2月 特殊

int get_days_of_month(int y, int m)
{
	int d = 0;
	int days[] = { 0,31,28,31,30,31,30,31,31,30,31,30,31 };
	//			   0 1  2  3  4  5  6  7  8  9  10 11 12
	//使用一个数组把 日期 放进去,把第一个元素随便设置,
	//让1月下标为1,2月下标为2,以此类推
	d = days[m]; //刚好传进来的月份对应数组中的月份下标
	if (((y%4==0 && y%100!=0) || (y%400==0)) && m==2)
		//如果是闰年 并且 月份是2月
	{
		d += 1;//在平年2月的基础上再加1变成29日
	}
	return d;
}

int main()
{
	int y = 0;//年
	int m = 0;//月

	//多组输入:
	while (scanf("%d %d", &y, &m) == 2)
	{
		int d = get_days_of_month(y, m);
		//写个函数计算该年该月的日期,
		//参数为 y 和 m
		//返回值为 对于日期
		printf("%d
", d);
	}

	return 0;
}

                    

3. 使用指针打印数组内容

            

(1). 方法一 -- 用for循环进行循环打印:

//使用指针打印数组内容
#include <stdio.h>
int main()
{
	float arr[] = { 3.14f, 99.9f, 66.5f, 0.0f };
	
	float* p = arr; //把数组名(首地址)放进指针中
	int sz = sizeof(arr) / sizeof(arr[0]); //元素个数

	//使用指针打印数组内容
	int i = 0;
	for (i = 0; i < sz; i++)
	{
		printf("%f ", *p);
		p++;
	}

	return 0;
}

                    

(2). 方法二 -- 用while循环进行循环打印:

//使用指针打印数组内容
#include <stdio.h>
int main()
{
	float arr[] = { 3.14f, 99.9f, 66.5f, 0.0f };

	float* p = arr; //把数组名(首地址)放进指针中
	int sz = sizeof(arr) / sizeof(arr[0]); //元素个数
	float* q = arr + sz; //使用指针表示元素个数

	//使用指针打印数组内容
	while (p < q)//对比地址大小来判断用不用打印
	{
		printf("%f ", *p++);//先解引用再p++
	}

	return 0;
}

             

(3). 自定义函数用while循环进行循环打印:

//使用指针打印数组内容
#include <stdio.h>

void print(float* p, int sz)
{
	float* q = p + sz; //使用指针表示元素个数

	//使用指针打印数组内容
	while (p < q)//对比地址大小来判断用不用打印
	{
		printf("%f ", *p++);//先解引用再p++
	}

}

int main()
{
	float arr[] = { 3.14f, 99.9f, 66.5f, 0.0f };

	float* p = arr; //把数组名(首地址)放进指针中
	int sz = sizeof(arr) / sizeof(arr[0]); //元素个数

	print(arr, sz); //调用自定义函数

	return 0;
}

               

4. 使用指针使字符串逆序

//字符串逆序
#include <stdio.h>
#include <string.h>
int main()
{
	char arr[10001] = { 0 };
	gets(arr);//输入数组数据,gets可以把空格也读入
	int len = strlen(arr);

	//进行逆序:使用 left 和 right
	char* left = arr; //左指针
	char* right = arr + len - 1; //右指针

	while (left < right)//两指针中间还有值就继续换
	{
		char tmp = *left;
		*left = *right;
		*right = tmp;
		left++;
		right--;
	}

	printf("%s
", arr);

	return 0;
}

                   

                   

 补充:

               

  • 32位系统下:

int4个字节指针表示地址空间个数,总共有2^32个,故占4个字节

              

  • 64位系统下:

int4个字节指针表示地址空间个数,总共有2^64个,故占8个字节

         

  • 整型指针+1,是向后偏移一个整型

         

  • 指针 - 指针 得到的是 指针和指针之间的元素个数

            

  • 指针就是地址指针比较大小,就是地址比较大小地址也是有大小之分

风语者!平时喜欢研究各种技术,目前在从事后端开发工作,热爱生活、热爱工作。