【C语言】整数和浮点数在内存中的存储

大家可能在学习的时候会经常疑惑数据在内存中是怎样存储的,今天用一篇博客给你讲清楚!!!从此不再疑惑!!!

文章目录

  • 1. 整数在内存中的存储
  • 2. 大小端字节序和字节序判断
    • 2.1 什么是大小端
    • 2.2 为什么有大小端
    • 2.3 练习
      • 2.3.1 练习1
      • 2.3.2 练习2
      • 2.3.3 练习3
      • 2.3.4 练习4
      • 2.3.5 练习5
      • 2.3.6 练习6
  • 3. 浮点数在内存中的存储
    • 3.1 练习
    • 3.2 浮点数的存储
      • 3.2.1 浮点数存的过程
      • 3.2.2 浮点数取的过程
    • 3.3 题目解析


1. 整数在内存中的存储

整数的2进制表示方法有三种,即 原码、反码和补码。
有符号的整数,三种表示方法均有符号位和数值位两部分,符号位都是用0表⽰“正”,用1表示“负”,最⾼位的⼀位是被当做符号位,剩余的都是数值位。

正整数的原、反、补码都相同。
负整数的三种表示方法各不相同。

原码:直接将数值按照正负数的形式翻译成⼆进制得到的就是原码。
反码:将原码的符号位不变,其他位依次按位取反就可以得到反码。
补码:反码+1就得到补码。

对于整型来说:数据存放内存中其实存放的是补码。
为什么呢?

在计算机系统中,数值⼀律⽤补码来表⽰和存储。
原因在于,使⽤补码,可以将符号位和数值域统⼀处理;
同时,加法和减法也可以统⼀处理(CPU只有加法器)此外,补码与原码相互转换,其运算过程是相同的,不需要额外的硬件电路。

2. 大小端字节序和字节序判断

我们来看一段代码:

int main()
{
	int n = 0x11223344;//十六进制数字
	return 0;
}

我们调试一下,在内存中观察一下:
在这里插入图片描述
我们可以看到,它在内存中是反着存的(以字节为单位存储,但是字节的存储是倒着的)。

这里进行几点说明:

  1. 整数在内存中存储的是二进制的补码。
  2. 在调试窗口中观察的时候,为了方便观察展示,显示的是16进制的值。
  3. 存储的顺序是倒过来的。

那么为什么是倒着存储的呢?这个就和我们下面要讲到的大小端的问题有关了。

2.1 什么是大小端

  • 其实超过⼀个字节的数据在内存中存储的时候,就有存储顺序的问题。
    比如:
    在这里插入图片描述
    或者这样设计:
    在这里插入图片描述
    或者还有其他的顺序存储,按照理论上讲,这几种都是可以的。数据在往里面存的时候,再怎样把它拿出来就行了啊!
    但是我们说如果那么没有顺序的存储方法就不是很常规,就比较乱,所以最终就留下来了两种方式(1. 按照正的顺序存储,2. 按照反的顺序存储):
    在这里插入图片描述

按照不同的存储顺序,我们分为大端字节序存储和小端字节序存储,下面是具体的概念:
大端(存储)模式
是指数据的低位字节内容保存在内存的⾼地址处,而数据的⾼位字节内容,保存在内存的低地址处。
在这里插入图片描述
小端(存储)模式
是指数据的低位字节内容保存在内存的低地址处,⽽数据的⾼位字节内容,保存在内存的⾼地址处。
在这里插入图片描述
上述概念需要记住,方便分辨大小端。

  • 那么我们是怎么理解高位节和低位节呢?
  • 0x 11 22 33 44这样一个十六进制的数字中的44是低位字节,11是高位字节,我们可以会借助数学中的知识来记忆(比如:123中的3在个位上,是处于低位,1是在百位上,处于高位)。

当前VS上使用的就是小端存储。

2.2 为什么有大小端

为什么会有大小端模式之分呢?

这是因为在计算机系统中,我们是以字节为单位的,每个地址单元都对应着⼀个字节,⼀个字节为8bit 位,但是在C语⾔中除了8 bit 的 char 之外,还有16 bit 的 short 型,32 bit 的 long 型(要看具体的编译器),另外,对于位数大于8位的处理器,例如16位或者32位的处理器,由于寄存器宽度大于⼀个字节,那么必然存在着⼀个如何将多个字节安排的问题。因此就导致了大端存储模式和小端存储模式。

例如:⼀个 16bit 的 short 型 x ,在内存中的地址为 0x0010 , x 的值为 0x1122 ,那么0x11 为⾼字节, 0x22 为低字节。对于大端模式,就将 0x11 放在低地址中,即 0x0010 中,0x22 放在⾼地址中,即 0x0011 中。小端模式,刚好相反。我们常用的 X86 结构是小端模式,而KEIL C51 则为大端模式。很多的ARM,DSP都为小端模式。有些ARM处理器还可以由硬件来选择是大端模式还是小端模式。

2.3 练习

2.3.1 练习1

  • 题目:
    请简述⼤端字节序和⼩端字节序的概念,设计⼀个⼩程序来判断当前机器的字节序。(10分)-百度笔试题。

  • 思路:

这里我是用1来作为验证的对象的,其实我们想到的就是如果1在内存中是大端存储的话是00 00 00 01,小端存储的话是01 00 00 00。那么我们只要看一下在内存中存储的第一个字节是什么就可以了,所以接下来的问题就是如何只取出一个字节,我们知道char类型的数据是1个字节的,我们就想到了将其强转成char类型。

  • 代码:
#include<assert.h>
#include<stdio.h>
int judge(int* p)
{
	assert(p);
	if (*(char*)p == 1)
	{
		return 1;
	}
	else
	{
		return 0;
	}
}
int main()
{
	int n = 1;
	int ret = judge(&n);
	if (ret == 1)
	{
		printf("小端\n");
	}
	else
	{
		printf("大端\n");
	}
	return 0;
}
  • 代码优化:
#include<assert.h>
#include<stdio.h>
int judge(int* p)
{
	assert(p);
	return *(char*)p;
}

int main()
{
	int n = 1;
	int ret = judge(&n);
	if (ret == 1)
	{
		printf("小端\n");
	}
	else
	{
		printf("大端\n");
	}

	return 0;
}

2.3.2 练习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);
 return 0;
}

请问上面代码的输出结果是什么?

在解决这个题之前,我们大家要明确signed char(有符号的) 和 unsigned char(无符号的),这两个东西究竟是什么玩意儿?只有搞明白,我们才能正确做出这道题。

  1. signed char :
    我们知道char类型的数据在内存中占1个字节,8个bit位。
    那接下来我们大概列一下char类型的数据在内存中的补码;
    在这里插入图片描述
    在char类型的数据中,如果是signed char类型的,那么内存中最高位就会被当作符号位。
    我们可以看一下signed char类型的数据的取值范围大概是多少:
    在这里插入图片描述
    注意大家在算负数的时候,注意要还原到原码进行计算(补码取反+1 或者 -1取反)。
    所以我们可以看到signed char 类型的数据的取值范围是:( -128 ~127)
  2. unsigned char:
    我们还是先来大概列一下char类型的数据在内存中的补码:
    在这里插入图片描述
    因为是unsigned char是无符号的,所以它是没有符号位的,8个bit位每一位都是有效位。
    所以:
    在这里插入图片描述
    所以unsigned char 类型的数据的取值范围是:( 1~255 )。
    明确了以上的这些,我们才能着手去做这道题。

题目解析:

#include <stdio.h>
int main()
{
	//这里说明一下,char 是有符号的char还是无符号的char是取决于编译器的
	//在vs上char==signed char
	char a = -1;
	//10000000 00000000 00000000 00000001 - 原码
	//11111111 11111111 11111111 11111110 - 反码
	//11111111 11111111 11111111 11111111 - 补码
	//但是由于我们定义的是 char 类型的,所以只存进去1个bit位,这个时候就会发生截断
	//只存 11111111 - a
	
	signed char b = -1;
	//11111111 - b

	unsigned char c = -1;
	//11111111 - c

	printf("a=%d,b=%d,c=%d", a, b, c);
	return 0;
}

注意: 上面写的补码都是在内存中存的补码,但是站在不同类型的数据往外面取的时候就不一定是这样了,我们接着往下看:

  1. 以%d的形式打印a
    我们以char的类型存进去的时候是11111111
    我们取出来的时候是以%d的视角去取的,它是4个字节的,并且char是有符号的。
    所以我们在取的时候会认为11111111的第一位是符号位,并且会发生整型提升(补符号位),所以取出来的是:11111111 11111111 11111111 11111111,然后再还原成原码10000000 00000000 00000000 00000001,所以打印出来是 -1。

  2. 以%d的形式打印b
    我们以signed cha的视角存进去的是11111111
    我们取出来的时候是以%d的视角去取的,它是4个字节的,并且singed char是有符号的。
    所以我们在取的时候会认为11111111的第一位是符号位,并且会发生整型提升(补符号位),所以取出来的是:11111111 11111111 11111111 11111111,然后再还原成原码10000000 00000000 00000000 00000001,所以打印出来是 -1。

  3. 以%d的形式打印c
    我们以unsigned cha的视角存进去的是11111111。
    我们取出来的时候是以%d的视角去取的,它是4个字节的,并且unsinged char是无符号的。
    所以我们在取的时候会认为11111111的第一位是符号位,并且会发生整型提升(补符号位),所以取出来的是:00000000 00000000 00000000 11111111,因为是以%d(有符号)的形式打印的,所以认为最高位是符号位,这里的最高位是0,所以是正数,又因为正数的原码,反码,补码相同,所以直接打印出来是 255。

2.3.3 练习3

  • 题目:
#include <stdio.h>
int main()
{
	char a = -128;
	printf("%u\n", a);//以%u打印是无符号的打印
	return 0;
}

我们来看这道题目的输出结果是什么?

  • 题目解析:
#include <stdio.h>
int main()
{
	char a = -128;
	//100000000 - -128
	//111111111 11111111 11111111 10000000 - 整型提升
	//而%u又认为内存中存的是一个无符号数,
	//正数的原码反码补码都相同,所以就会直接打印。
	printf("%u\n", a);//以%u打印是无符号的打印
	return 0;
}
  • 代码结果:
    在这里插入图片描述

题目:

#include <stdio.h>
int main()
{
 char a = 128;
 printf("%u\n",a);
 return 0;
}

我们再来看这道题目的输出结果是什么?

  • 题目解析:

我们可以看到,这道题和上面的那一道不一样的就是-128变成了128。那么结果会有什么不一样呢?
我们前面分析了char==signed char,而signed char的取值范围是( -128~127 ),那么很明显是存不下的,那这个该怎么办呢?。大家要明确我们该怎样存就怎样存,存不下是因为我们再取的时候就可能不是这个数了。所以我们存进去的还是10000000。只是取出来就不一定是128了。
既然存进去的和上一题一样,都是10000000,那么后面的分析和上一题就完全一样了,代码的运行结果也是一样的。

2.3.4 练习4

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

那么这个代码的结果又是什么呢?

  • 题目解析:

我们这里定义了一个char类型的数组,有1000个元素。然后用循环对数组中的元素进行赋值,然后最后求的是字符串的长度,统计的是’\0‘(ASCII码值是0)之前的字符个数。
那么,我们前面分析过char类型的数据的取值范围是( -128~127 ),所以,很明显 a[i] = -1-i;计算出来的-1, -2, -3…, -127,-128,-129, -130…是无法正确进行存储的。那么究竟存的是多少呢?
在这里插入图片描述
这个其实就是我们的char类型数据的一个变化图,那么,我们-1, -2, -3…, -127,-128, -129, -130…这个数据是逆时针走的,那么-128之后就是127,126,125…一直往后走直到遇见了0,那么负数时128个数字,正数是127个数字,所以这道题答案是255。

2.3.5 练习5

  • 题目:
#include <stdio.h>
unsigned char i = 0;
int main()
{
 for(i = 0;i<=255;i++)
 {
 printf("hello world\n");
 }
 return 0;
}
  • 题目解析:

这个答案是死循环,为什么呢?
在这里插入图片描述
大家还记得这个signed char 类型数据的一个轮回图吧!
我们再来看一下unsigned char 类型数据的轮回图:
在这里插入图片描述
我们这里的数据是顺时针的,所以当我们的 i=255 时,i再进行+1操作时候不是256,而是0了,如此往复,一直这样循环,没有终止。

  • 题目:
#include <stdio.h>
int main()
{
 unsigned int i;
 for(i = 9; i >= 0; i--)
 {
 printf("%u\n",i);
 }
 return 0;
}
  • 题目解析:

这个题就很简单了,因为我们定义的i是unsigned int 类型的,所以,它不可能小于0的,所以i >= 0,这个条件是恒成立的,所以这个代码也是死循环。

2.3.6 练习6

  • 题目:
#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;
}

同样的,这段代码的运行结果是什么?

  • 题目解析:

我们先来看int *ptr1 = (int *)(&a + 1);,这里的(&a + 1)是表示&a(取出整个数组的地址),然后进行+1操作,这个时候就跳过了整个数组。
在这里插入图片描述
&a是int类型的,&a+1之后也是int类型的,然后我们对其进行强制类型转换成了( int* )类型,然后将其赋给了int类型的指针变量ptr1。
那么,ptr[-1]就等价于
( ptr1-1),ptr1是int* 类型的,-1操作时候向后移动了4个字节,然后对其解引用4个字节,答案就是4。
在这里插入图片描述
接下来,我们再看 int *ptr2 = (int *)((int)a + 1);,这里的((int)a + 1)中的()的优先级比较高,所以我们先执行对a进行强制类型转换为int类型,然后再进行+1操作,此时,就有坑了,因为我们已经a强制类型转换成了int类型,那么此时它就是一个数,不再是地址,+1操作的时候也是一个数+1得到另一个数。
假设:a是0x0012ff40
+1: 0x0012ff41
那么此时再将其强制类型转换成( int* )类型。那么此时它又是一个地址了,大家可以看一下我们假设的0x0012ff40和0x0012ff41差了1个字节,那么此时这个指针只是向后偏移了1个字节。

我们来画图理解一下:![在这里插入图片描述](https://img-blog.csdnimg.cn/direct/325ee155142a4960b1dfc7c3034bd47b.png
这是1,2 ,3, 4在内存中的存储,这里的个红框框代表4个字节,里面是按照十六进制进行存储的。
在这里插入图片描述
这里的一个篮框框就代表1个字节,我们在输出的时候输出的是ptr2,对ptr2进行了解引用,而ptr2的类型是int类型的,解引用的时候会访问4个字节:
在这里插入图片描述
即解引用时解引用的是黑色框框内的内容。我们又知道vs中是小端存储的,所以输出结果是02000000。

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

  1. 常⻅的浮点数:3.14159、1E10(1.0*10^10)等,浮点数家族包括: float、double、long double 类型。
  2. 浮点数表⽰的范围: float.h 中定义
    那我们可以打开这个文件看一下(这里给大家截取一部分):
    在这里插入图片描述

3.1 练习

接下来,我们先来看一段代码:

#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;
 }

大家先来自己看一下这个代码运行的结果是多少,然后再往下看答案,感受一下自己的思路和正确的思路偏差在哪.
我们从上往下一行一行地先大概来分析一下这段代码。

#include <stdio.h>
int main()
{
	int n = 9; //定义了一个int类型的常量n, 并赋值为9.
	float* pFloat = (float*)&n;//取出了n的地址,强制类型转换成( float* )类型,紧接着将其赋给float*类型的指针变量pFloat。
	printf("n的值为:%d\n", n);//以%d的形式打印n
	printf("*pFloat的值为:%f\n", *pFloat);//以%f的形式打印*pFloat

	*pFloat = 9.0;//通过指针间接访问n,并将其值改为9.0
	printf("num的值为:%d\n", n);//以%d的形式打印n
	printf("*pFloat的值为:%f\n", *pFloat);//以%f的形式打印*pFloat
	return 0;
}

看到这里你是不是还以为答案是9,9.0,9,9.0.
其实并不是,我们来看一下运行结果:
在这里插入图片描述
看完答案,你是不是有很多疑惑,没关系,我们带着疑问往下走,会得到答案的,我们一点一点地分析到底为什么是这样的一个答案。

我们知道,&n是int类型的,占4个字节,强制类型转换成float类型的之后,也是占4个字节啊,我们在解引用的时候应该是恰好能访问它本来存储的4个字节的空间的呀!
在这里插入图片描述

那么按理说,答案就应该是我们上面所说的答案啊,可是为什么那不是正确答案呢?
那就说明我们的浮点数在内存中的存储并不是像我们想的那样,不是和整数一样的。

3.2 浮点数的存储

根据国际标准IEEE(电⽓和电⼦⼯程协会) 754,任意⼀个⼆进制浮点数V可以表⽰成下⾯的形式:

V = (−1)^S * M *2^E

  • (−1)S 表示符号位,当S=0,V为正数;当S=1,V为负数
  • M 表⽰有效数字,M是⼤于等于1,⼩于2的
  • 2^E 表⽰指数位

举例来说:

⼗进制的5.0,写成⼆进制是 101.0 ,相当于 1.01×2^2 (小数点向左移动两位,那么就是2(因为这里是2进制,所以底数是2,我们大家熟悉的是10进制,那么科学计数法的底数就是10)的2次方)。
那么,按照上⾯V的格式,可以得出S=0,M=1.01,E=2。
⼗进制的-5.0,写成⼆进制是 -101.0 ,相当于 -1.01×2^2 。那么,S=1,M=1.01,E=2。

前面 ,我们举例的是一个小数点之后是0的,那么如果是5.5,怎么来用二进制来表示呢?
是101.101吗?
不是的哈,可不敢这样表示,我们来看一下每一位的权重。
在这里插入图片描述
那么这样对应下来,算出来的就不是5.5了,而是5.875了,5.5正确的二进制表示是101.1.

那么我们按照国际标准表示的5.5就是5.5 = (-1)^0 * 1.011 * 2 ^ 2(这样写不太好看,请看如下图)
在这里插入图片描述
在这里,S=0, M=1.011, E=2,因为所有的浮点数都能按照这个形式表示出来,只不过是不同的浮点数的S, M, E的值是不同的,而这个形式中的其他数据是不变的,那么就意味着,我们在存储浮点数的时候只需要将S, M, E存储好就行了,事实上,浮点数在内存中的存储,九四存储的S, M, E相关的值。

那么浮点数在内存中究竟是怎么存储的呢?

IEEE 754规定:

  • 对于32位的浮点数(float),最高的1位存储符号位S,接着8位存储指数E,剩下的23位存储有效数字M。
    在这里插入图片描述
    对于64位的浮点数(double),最高的1位存储符号位S,接着的11位存储指数E,剩下的52位存储有效数字M。
    在这里插入图片描述

3.2.1 浮点数存的过程

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是可能出现负数的(比如:0.5的二进制表示的指数是-1),所以IEEE 754规定,存⼊内存时E的真实值必须再加上⼀个中间数,对于8位的E,这个中间数是127;对于11位的E,这个中间数是1023。
⽐如,2^10的E是10,所以保存成32位浮点数时,必须保存成10+127=137,即10001001。

这里大家可能会有一个疑惑,如果这个负数加上中间数之后,还是一个负数该怎么办呢?

大家在这里需要注意,即使是浮点数它也是有最大值和最小值的,那就意味着E是有最大值和最小值的,既然它是这样设计的,那么就是设计的时候就已经计算好的,所以才这样规定的。

接下来,我们就还以5.5为例来看一下浮点数在内存中的存储究竟是怎样的。

int mian()
{
	float f = 5.5;
	//二进制表示:(-1)^0 * 1.011 * 2^2
	//其中,S = 0, M = 1.011, E = 2
	//S(1位)在内存中的存储:0
	//E(8位)在内存中的存储:2+127(float类型) = 129, 10000001
	//M(23位)在内存中的存储:01100000000000000000000(位数不够时,补0)
	//所以浮点数5.5在内存中的存储是01000000101100000000000000000000
	return 0;
}

那这个时候是不是感觉这个二进制太长了,看着不得劲,那么我们可以将其写成16进制的数字,会比较好看些。

//我们知道,一位16进制的数字对应的是4个2进制的数字。
//0100 0000 1011 0000 0000 0000 0000 0000
//4    0    B    0    0    0    0    0
//40 B0 00 00

我们来调试一下,看看内存中的存储方式是不是和我们说的一样。
在这里插入图片描述
我们在这里看到果真和我们写的一样(这里提示一下,大家看到内存中是反过来存储的,这是小端存储,不太了解的同学可以往这篇博客的上面翻翻,翻到最上面,看一下目录,就可以直接找到大小端的讲解)。

大家在举例子的时候可不敢乱举哈,你看完之后,特别兴奋,感觉自己又掌握了一个新知识,你就开始随便举例子,比如,你举了一个3.14这个浮点数,你会发现你用二进制表示的时候,无法精确的表示3.14,无论你怎么用小数点后的01来凑0.14,你可能都无法精确的凑出来。也就是说,如果小数点后的位太多,就可能导致浮点数在内存中无法精确的保存。所以,浮点数在内存中可能会出现这种情况,无法在内存中精确保存。

3.2.2 浮点数取的过程

前面介绍了浮点数存的过程,那么浮点数在取的时候又是怎样的呢?
常规情况下,就是按照存的相反过程往回返就可以了。
但是也有不常规的情况。在浮点数取的过程分为3种情况。

  1. E不全为0或不全为1
  • 这时,浮点数就采⽤下⾯的规则表示,即指数E的计算值减去127(或1023),得到真实值,再将有效数字M前加上第⼀位的1。
  • ⽐如:0.5 的⼆进制形式为0.1,由于规定正数部分必须为1,即将⼩数点右移1位,则为1.0*2^(-1),其阶码为-1+127(中间值)=126,表⽰为01111110,⽽尾数1.0去掉整数部分为0,补⻬0到23位00000000000000000000000,则其⼆进制表示形式为:
    0 01111110 00000000000000000000000
  1. E全为0
    这时,浮点数的指数E等于1-127(或者1 -1023),但是如果其加上一个中间数之后为0,就代表这个指数是非常小的,比如说这个指数是-127,那么此时1.xxx * 2^-127是一个非常非常小的。那么,有效数字M不再加上第⼀位的1,⽽是还原为0.xxxxxx的⼩数。这样做是为了表⽰±0,以及接近于0的一个很⼩的数字。
    1 0 00000000 00100000000000000000000
  2. E全为1
  • 我们以float为例,E全为1的时候,这个时候存的是255,255是通过( 真实的E+127) 得到的,那么此时E也是非常大的,可能是128这样的值,那么此时1.xxx * 2^128,这也是一个非常非常大的数字。
  • 这时,如果有效数字M全为0,表⽰±⽆穷⼤(正负取决于符号位s);
    0 11111111 00010000000000000000000

3.3 题目解析

下⾯,让我们回到上面的练习,来看解析吧,同志们!

int main()
{
	int n = 9;
	//00000000 00000000 00000000 00001001----9在内存中的二进制补码

	float* pFloat = (float*)&n;
	printf("n的值为:%d\n", n);
	//n是int类型的,又以%d的形式输出,这里输出9是没有任何问题的

	printf("*pFloat的值为:%f\n", *pFloat);
	//这里是以%f的形式打印的,那么站在pFloat的角度来看,
	//它会认为自己指向的是一个float类型的数据,那么9在内存中的存储就是下面这样的了
	//   0 00000000 00000000000000000001001
	//   S    E            M
	//那么,这个时候我们要取出来这个数据,就是我们说的E全为0的情况,
	//那么,它就是一个非常非常小的数字,是一个无限接近于0的数字

	*pFloat = 9.0;
	//将9.0这个浮点数通过指针变量pFloat间接访问n,pFloatde 的类型是float*类型的
	//此时,pFloat是以浮点数的视角存储9.0的
	//我们按照标准规定写出9.0
	//(-1)^0 * 1.001 * 2^3
	//此时,S=0, M=1.001, E=3
	//0 10000010 00100000000000000000000
	printf("num的值为:%d\n", n);
	//此时,n已经被pFloat按照浮点数的视角存进去了,此时打印要以整数的的视角往回拿
	//那么此时整数的视角的二进制序列就是下面这样的:
	//01000001 00010000 00000000 00000000
	//那么此时以%d的形式打印,%d表示的是有符号的,那么最高位就是符号位,最高位是0,是正数
	//那么补码就是其原码,直接 打印出来就好了。

	printf("*pFloat的值为:%f\n", *pFloat);
	//前面我们说过存的时候是按照float的视角存进去的,那取的时候也是按照float取的话,
	//那么,和存进去的时候是没差的,所以打印的就是9.0。
	return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/612004.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

[VulnHub靶机渗透] Hackademic: RTB1

&#x1f36c; 博主介绍&#x1f468;‍&#x1f393; 博主介绍&#xff1a;大家好&#xff0c;我是 hacker-routing &#xff0c;很高兴认识大家~ ✨主攻领域&#xff1a;【渗透领域】【应急响应】 【Java、PHP】 【VulnHub靶场复现】【面试分析】 &#x1f389;点赞➕评论➕收…

day2_greedyIntervalsLRU/LFU

二、贪心算法之区间调度问题 0.计算一个区间集合中无重复的区间的最大数量(模板) public int intervalSchedule(int[][] intvs) {if (intvs.length 0) return 0;// 按 end 升序排序Arrays.sort(intvs, (a, b) -> Integer.compare(a[1], b[1]));// 至少有一个区间不相交in…

Baidu Comate 编程插件:提升开发效率的利器

文章目录 引言简介目的 Baidu Comate插件概述定义与功能市场现状竞品分析 安装与配置VsCode 安装&#xff1a;注意事项 版本选择 核心特性详解功能介绍代码生成实时续写错误纠正 使用体验体验地址 引言 简介 基于文心大模型&#xff0c;结合百度积累多年的编程现场大数据和外…

专业做护眼灯的有哪些品牌?几款专业儿童卧室灯品牌分享

在当今时代&#xff0c;我们观察到一个不容忽视的现象&#xff1a;孩子们的视力问题日益增多&#xff0c;这无疑向众多家长发出了警示。它提醒着我们&#xff0c;除了追求学术成就之外&#xff0c;孩子们的视觉健康同样重要&#xff0c;不容忽视。因此&#xff0c;选择一款适合…

leetcode刷题:对称二叉树

题目&#xff1a; 给你一个二叉树的根节点 root &#xff0c; 检查它是否轴对称。 示例 1&#xff1a; 输入&#xff1a;root [1,2,2,3,4,4,3] 输出&#xff1a;true 示例 2&#xff1a; 输入&#xff1a;root [1,2,2,null,3,null,3] 输出&#xff1a;false 提示&#xf…

以导航产品为核心,东软想为车企扫除出海障碍

得益于新能源汽车领域多年的布局&#xff0c;以及在汽车智能化方面的先发优势&#xff0c;近年来&#xff0c;中国汽车品牌在质与量上都得到了极大提升&#xff0c;并带来强大的竞争力。 据海关总署公布的数据&#xff0c;过去三年&#xff0c;中国汽车出口规模连续突破式发展…

LeetCode算法题:7. 整数反转

给你一个 32 位的有符号整数 x &#xff0c;返回将 x 中的数字部分反转后的结果。 如果反转后整数超过 32 位的有符号整数的范围 [−2^31, 2^31 − 1] &#xff0c;就返回 0。 假设环境不允许存储 64 位整数&#xff08;有符号或无符号&#xff09;。 示例 1&#xff1a; 输…

会赚钱的人都在做这件事:你了解吗?

在我们日常生活的点滴中&#xff0c;以及在各种场合的交互中&#xff0c;利他思维始终扮演着不可或缺的角色。当我们追求合作与共赢时&#xff0c;单方面的自我立场显然是不够的&#xff0c;真正的关键在于换位思考&#xff0c;寻找并满足对方的需求。 互利互赢的核心理念正是利…

【挑战30天首通《谷粒商城》】-【第一天】【10 番外篇】 解决docker 仓库无法访问 + MobaXterm连接VirtualBox虚拟机

文章目录 课程介绍 1、解决docker 仓库无法访问 2、 MobaXterm连接VirtualBox虚拟机 Stage 1&#xff1a;下载MobaXterm选择适合你的版本 Stage 2&#xff1a;vagrant ssh 连接&#xff0c;开启ssh访问 Stage 2-1&#xff1a;su获取root账号权限,输入密码&#xff08;默认vagra…

纯血鸿蒙APP实战开发——阅读翻页方式案例

介绍 本示例展示手机阅读时左右翻页&#xff0c;上下翻页&#xff0c;覆盖翻页的功能。 效果图预览 使用说明 进入模块即是左右翻页模式。点击屏幕中间区域弹出上下菜单。点击设置按钮&#xff0c;弹出翻页方式切换按钮&#xff0c;点击可切换翻页方式。左右翻页方式可点击翻…

【软件测试】3.开发模型

目录 1.常见的开发模型 1.1瀑布模型 1.2螺旋模型 1.3增量模型和迭代模型 1.4敏捷模型 1.4.1特点&#xff1a; 1.5Scrum模型&#xff08;三个角色和五个重要会议&#xff09; 1.5.1三个角色&#xff1a; 1.5.2Scrum工作流程&#xff08;五个会议&#xff09; 1.6测试模…

如何选择适合自己网站的SSL证书提供商?

在互联网技术飞速发展的今天&#xff0c;确保数据安全已成为网站运营的基石。HTTPS证书作为一项重要的安全认证协议&#xff0c;对于保护数据传输的安全性至关重要。本文将为您提供一份详尽的指南&#xff0c;帮助您了解如何申请和部署HTTPS证书。 一、选择SSL证书提供商 首先…

JUC下的CompletableFuture详解

详细介绍 CompletableFuture是Java 8引入的一个实现Future接口的类&#xff0c;它代表一个异步计算的结果。与传统的Future相比&#xff0c;CompletableFuture提供了更丰富的功能&#xff0c;比如链式调用、组合异步操作、转换结果、异常处理等&#xff0c;极大地增强了Java在…

给网络镜像模式下的 WSL2 使用 127.0.0.1代理的方法

网络镜像模式下的WSL2虽然复制了宿主机windows的ip&#xff0c;但是仍然无法访问127.0.0.1的代理。经过调查&#xff0c;发现因为WSL2从应用商店下载而来&#xff0c;所以可能是UWP应用&#xff0c;所以需要用工具解除环回代理限制。

mysql 不停的重启关闭

早上在使用phpstudy的时候&#xff0c;发现自己的mysql5.7和5.8都出现了问题&#xff0c;就是不停的重启&#xff0c;在梳理了状况之后&#xff0c;可能是硬盘的内存空间不足&#xff0c;或者硬盘出现了问题&#xff1b;于是我将mysql 重新安装了一次&#xff0c;整个问题就解决…

数据结构(四)————二叉树和堆(中)

制作不易&#xff0c;三连支持一下呗&#xff01;&#xff01;&#xff01; 文章目录 前言一、堆的概念及结构二、堆的实现三.堆的应用 总结 前言 CSDN 这篇博客介绍了二叉树中的基本概念和存储结构&#xff0c;接下来我们将运用这些结构来实现二叉树 一、堆的概念及结构 1…

一篇文章,系统性聊聊Java注解

你好&#xff01; 这类系统性聊聊***知识点的文章&#xff0c;是希望给大家带来对某个技术的全貌认识&#xff0c;如果大家喜欢&#xff0c;后续可以陆续更新此系列 下面&#xff0c;开始今天的分享 在之前&#xff0c;我们已经分享过注解相关的三个面试题&#xff0c; 今天的…

信号量、PV操作及软考高级试题解析

信号量 在并发系统中&#xff0c;信号量是用于控制公共资源访问权限的变量。信号量用于解决临界区问题&#xff0c;使得多任务环境下&#xff0c;进程能同步运行。此概念是由荷兰计算机科学家Dijkstra在1962年左右提出的。信号量仅仅跟踪还剩多少资源可用&#xff0c;不会跟踪…

Cloudera简介和安装部署

ChatGPT Cloudera 是一个基于 Apache Hadoop 的数据管理和分析平台。它是由 Hadoop 的几位创始人及早期贡献者于 2008 年创立的公司&#xff0c;并随着公司的不断发展&#xff0c;Cloudera 开始提供企业级的解决方案&#xff0c;帮助企业更好地利用 Hadoop 生态系统进行大数据…

2024.05.10作业

TCP服务器 头文件 #ifndef WIDGET_H #define WIDGET_H#include <QWidget> #include <QTcpServer> #include <QTcpSocket> #include <QList> #include <QMessageBox> #include <QDebug>QT_BEGIN_NAMESPACE namespace Ui { class Widget; …
最新文章