C语言进阶篇-----数据在内存中的存储

目录

一、数据类型介绍

1、类型的基本归类

二、整型在内存中的存储

1、原码、反码、补码

2、大小端介绍

3、练习

三、浮点型在内存中的存储

1、举例

2、浮点数存储规则

3、解释前面的题目,代码分析

四、总结


一、数据类型介绍

这些是经常使用的类型

    char	//字符数据类型
	short	//短整型
	int		//整型
	long	//长整型
	long long//更长的整型
	float	 //单精度浮点数
	double	 //双精度浮点数

C语言中还有很多类型

例如:布尔类型 _Bool 专门用来表示真假的。

_Bool flag = true;

使用布尔类型要引入头文件:#include<stdbool.h>

#include<stdio.h>
#include<stdbool.h>
int main()
{
	_Bool flag1 = true;
	_Bool flag2 = false;
	if (flag1)
	{
		printf("Hello\n");
	}
	if (flag2)
	{
		printf("haha\n");
	}
	return 0;
}

运行结果

布尔类型是C99标准中才引入的。因为C语言中使用0表示假,非0表示真,所以在C语言中很少使用这个类型。

在C语言中布尔类型其实是对int类型的一种重命名,本质上它还是int类型。true 表示 1,false 表示0。

 还有很多类型,可以通过C语言类型这个网址去查看。

1、类型的基本归类

(1)整型家族

char
unsigned char
signed char

//[int] 表示 int 可以省略
short
unsigned short[int]
signed short[int]

int
unsigned int
signed int

long
unsigned long[int]
signed long[int]

unsigned:无符号数,赋的值只能是整数,如果赋一个负数,会转换成对应的正数(转换正数不是取绝对值)。

signed:有符号数,赋值时可以符正数也可以赋负数。

  • signed int      等价于  int
  • signed short  等价于  short
  • signed long   等价于  long

char 到底是 signed char 还是 unsigned char 是取决于编译器的实现。

常见的编译器下:char 等价于 signed char

🎄  

#include<stdio.h>
int main()
{
	int n = 10;
	printf("%d\n", n);

	n = -10;
	printf("%d\n", n);
	return 0;
}

运行结果

🎄  

#include<stdio.h>
int main()
{
	unsigned int n = 10;
	printf("%d\n", n);

	n = -10;
	printf("%d\n", n);
	return 0;
}

运行结果

  • 看到这可以会认为前面所说的结论是错误的,unsigned int 可以输出负数。
  • unsigned int 是不能打印出负数的,打印出负数其实是输出格式不对,unsigned int 打印要以 %u 的形式打印。

🎄  

#include<stdio.h>
int main()
{
	unsigned int n = 10;
	printf("%u\n", n);

	n = -10;
	printf("%u\n", n);
	return 0;
}

运行结果

🤔 打印出来是-10的补码,至于为什么是这个值文章后面会讲到。

(2)浮点数家族

float
double

(3)构造类型(自定义类型)

数组类型
结构体类型 struct
枚举类型 enum
联合类型 union

数组也是自定义类型,去掉数组名就是数组的类型。

例如:

  • int a[10]; 这个数组的类型是 int [10]
  • int a[5]; 这个数组的类型是 int [5]
  • int a[10]与int a[5]是两个不同类型的数组。

只要数组的类型发生变化或者数组元素个数发生变化,数组的类型就会彻底发生变化。数组可以通过改变元素个数或数组类型该改变它的类型,所以数组是自定义类型。

(4)指针类型

int* pi;
char* pc;
float* pf;
void* pv;

(5)空类型

void 表示空类型(无类型), 通常应用于函数的返回类型、函数的参数、指针类型。

二、整型在内存中的存储

一个变量的创建是要在内存中开辟空间的。空间的大小是根据不同的类型而决定的。

🤔 接下来我们看一下数据在内存中到底是如何存储的?

#include<stdio.h>
int main()
{
	int a = 10;
	int b = -10;
	return 0;
}

变量a在内存中的存储

变量b在内存中的存储

要想知道整型为什么在内存中这样存储就要先了解原码、反码、补码。

1、原码、反码、补码

计算机中的整数有三种表示方法,即原码、反码和补码。

三种表示方法均有符号位和数值位两部分。

符号位:用0表示“正”,用1表示“负”。

数值位:负整数的三种表示方法各不相同。

  • 原码:直接将二进制按照正负数的形式翻译成二进制
  • 反码:将原码的符号位不变,其他位依次按位取反
  • 补码:反码+1

正整数的原、反、补码都相同。

对于整形来说:数据存放内存中存放的是补码。

🤔 为什么在计算机系统中,数值一律用补码来表示和存储?

原因在于,使用补码,可以将符号位和数值域统 一处理; 同时,加法和减法也可以统一处理(CPU只有加法器)。

🎄 举例:1-1

此外,补码与原码相互转换,其运算过程是相同的,不需要额外的硬件电路。 

  • 原码-->补码:原码符号位不变其他位按位取反得到反码,反码+1得到补码
  • 补码-->原码:补码符号位不变其他位按位取反,取反后+1得到原码

了解了原码、反码 、补码,回到前面的问题 10 与 -10 在内存中的存储。

编译器是以十六进制数显示的,所以变量a在内存中存储的数据显示为:0a 00 00 00,变量b在内存中存储的数据显示为:f6 ff ff ff。

至于为什么是倒着存放的,这个问题就是接下来所说的大小端的问题。

2、大小端介绍

当一个数值在内存中存放的时候,它所占空间的大小是超过一个字节的时候,它在内存中的存储就有顺序。

(1)什么是大端小端

大端(存储)模式,是指数据的低位保存在内存的高地址中,而数据的高位,保存在内存的低地址 中;

小端(存储)模式,是指数据的低位保存在内存的低地址中,而数据的高位,,保存在内存的高地 址中。

(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处理器还可以 由硬件来选择是大端模式还是小端模式。

原因二:

        因为数据的存储是由计算机内存硬件决定的,内存硬件是由内存硬件厂商决定。在早期有很多内存硬件厂商,每个厂商都有自己的见解;并没有达成一致,每个人都没有足够的理由来说服对方。由此产生了两种存储方案:大端、小端。不管大端小端,只要存储和取的规则是一样的那么得到的值就是一样的,所以并不影响用户的使用。

(3)理解大小端:

  • 每个字节都有地址,所有的地址都是不同的;地址有高地址和低地址之分(按照字节为单位),那么肯定是有大小的!
  • 数据也要按照字节为单位划分成若干块,也是有高权值位和低权值位之分的。
  • 数据如何存放:只要同等条件去取,怎样存放都是可以的;

【权值:对于多位数,处在某一位上的“1”所表示的数值的大小,称为该位的位权;例如十进制第2位的位权为10,第3位的位权为100;而二进制第2位的位权为2,第3位的位权为4,对于 N进制数,整数部分第 i位的位权为N^(i-1),而小数部分第j位的位权为N^-j.】(例:一个地址0x11 22 33 44 ,11的权值大于44的权值)

(4)大小端的基本概念:

        a.大端:按照字节为单位,低权值位数据存储在高地址处,就叫做大端;

        b.小端:按照字节为单位,低权值位数据存储在低地址处,就叫做小端;

例:int a = 0x11223344

 记忆大小端的口诀:小小小 【权值位小、地址数字小(低地址)、小端存储】

🎄 例题:设计一个小程序来判断当前机器的字节序。

😎 解题思路:在内存中存放1,1的十六进制为0x00 00 00 01,然后取它的第一个字节,如果取出的是0,就是大端存储,取出来的是1,就是小端存储。

#include<stdio.h>
int main()
{
	int a = 1;
	char* p = (char*)&a;
	if (1 == *p)
	{
		printf("小端\n");
	}
	else
	{
		printf("大端\n");
	}
	return 0;
}

 将上面的代码写成一个函数

#include<stdio.h>
int check_sys()
{
	int a = 1;
	char* p = (char*)&a;
	if (1 == *p)
		return 1;
	else
		return 0;
}
int main()
{
	int ret = check_sys();
	if (1 == ret)//函数返回1表示小端,返回0表示大端
	{
		printf("小端");
	}
	else
	{
		printf("大端");
	}
	return 0;
}

代码优化

#include<stdio.h>
int check_sys()
{
	int a = 1;
	return *(char*)&a;
}
int main()
{
	int ret = check_sys();
	if (1 == ret)//函数返回1是小端,返回0是大端
	{
		printf("小端");
	}
	else
	{
		printf("大端");
	}
	return 0;
}

运行结果

这道题还有一种写法,使用联合体类型,这个方法后面在联合体的内容时会写出来。

3、练习

🎄

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

代码分析

  • -1的原码:1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1
  • -1的反码:1 1 1 1 1 1 1 1 1  1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0
  • -1的补码:1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1

内存中存放的是补码,因为a的类型是char,占一个字节(8个比特位),所以将 -1 存放到a里面会发生截断,截断后就会将最低的8个比特位保存在a中。

所以a中存放的是:1 1 1 1 1 1 1 1

将数据存放的时候不需要关注是有符号的还是无符号,只要存进去就可以,所以变量b与变量c中存放的是:1 1 1 1 1 1 1 1

当使用数据的时候(打印数据的时候)需要关注是有符号还是无符号。

以 %d 的形式打印是打印整数,但是变量a、b、c都是char类型,所以在打印的时候会发生整型提升。

在VS这个编译器中char等价于signed char,所以变量a是有符号的,整型提升时如果是有符号的,在提升时高位补符号位

变量a中存放的是:1 1 1 1 1 1 1 1,因为类型是char,所以符号位是1

整型提升后:1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 -->补码

以 %d 的形式打印的是原码,将补码转换为原码

  • 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1  1 1 1 1 1 1 1 1 1 1 1 1 -->补码
  • 1 0 0 0 0 0 0 0 0 0 0  0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -->反码
  • 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 -->原码

所以变量a打印的结果为-1,因为char等价于signed char,所以变量b打印的结果也是-1。

整型提升时如果是无符号的,在提升时高位补0

变量c中存放的是:1 1 1 1 1 1 1 1,整型提升时再高位补0

整型提升后:0 0 0 0 0 0 0 0 0 0 0 0 0 0  0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1

以 %d 的形式打印,就将内存中存放的数看做是一个有符号数,最高位是0,表示是正数,正数的原反补相同,所以变量c打印的结果为 255。

运行结果

🎄

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

代码分析

  • -128的原码:1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0
  • -128的反码:1 1 1 1 1 1 1 1 1  1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1
  • -128的补码:1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0

内存中存放的是补码,因为a的类型是char,占一个字节(8个比特位),所以将 -128 存放到a里面会发生截断,截断后就会将最低的8个比特位保存在a中。

所以a中存放的是:1 0 0 0 0 0 0 0

以 %u(无符号整型的打印格式) 的形式打印是打印整数,但是变量a是char类型,所以在打印的时候会发生整型提升。

变量a是有符号的,整型提升时如果是有符号的,在提升时高位补符号位

变量a中存放的是:1 0 0 0 0 0 0 0,因为类型是char,所以符号位是1

整型提升后:1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 -->补码

以 %u 的形式打印,就将内存中存放的数看做是一个无符号数,无符号数的原反补相同,所以变量a打印出来的结果是直接将整型提升后的补码转换成十进制。

运行结果

🎄

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

代码分析

  • 128的原码:0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0

变量a是正数,正数的原反补相同。内存中存放的是补码,因为a的类型是char,占一个字节(8个比特位),所以将 128 存放到a里面会发生截断,截断后就会将最低的8个比特位保存在a中。

所以a中存放的是:1 0 0 0 0 0 0 0

以 %u(无符号整型的打印格式) 的形式打印是打印整数,但是变量a是char类型,所以在打印的时候会发生整型提升。

变量a是有符号的,整型提升时如果是有符号的,在提升时高位补符号位

变量a中存放的是:1 0 0 0 0 0 0 0,因为类型是char,所以符号位是1

整型提升后:1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 -->补码

以 %u 的形式打印,就将内存中存放的数看做是一个无符号数,无符号数的原反补相同,所以变量a打印出来的结果是直接将整型提升后的补码转换成十进制。

运行结果

🎄

#include<stdio.h>
int main()
{
	int i = -20;
	unsigned int j = 10;
	printf("%d\n", i + j);
	//按照补码的形式进行运算,最后格式化成为有符号整数

	return 0;
}

代码分析

  • -20的原码:1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 1 0 0
  • -20的反码:1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 0 1 1
  • -20的补码:1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 1 0 0
  • 10的原码:0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 1 0

j 是无符号整型,原反补相同,i + j 是按照补码的形式计算

  • i 的补码:1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 1 0 0
  • j 的补码:0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 1 0
  • i    +    j :1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 1 0

以 %d 的形式打印出来的是原码

  • 补码:1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 1 0
  • 反码:1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 1
  • 原码:1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 1 0

所以最终打印的结果是:-10

运行结果

🎄

#include<stdio.h>
#include<Windows.h>
int main()
{
	unsigned int i;
	for (i = 9; i >= 0; i--)
	{
		printf("%u\n", i);
		Sleep(1000);//打印一次后睡眠一秒
	}
	return 0;
}

代码分析

因为变量 i 是无符号整型,不可以产生负数,所以循环中 i >= 0 的判断条件恒成立。

当 i = 9 时满足条件进入循环打印 9 然后执行 i-- ,i 一直递减到0时,满足循环条件进入循环打印 0 然后执行 i--,此时 i = -1 。存数据是不关注数据的类型。

  • -1的原码:1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1
  • -1的反码:1 1 1 1 1 1 1 1 1  1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0
  • -1的补码:1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1

打印数据的时候(取数据时),要看数据的类型。以%u(无符号数据的打印格式)的形式打印,它的原反补都相同,所以最终打印出来的结果是:4,294,967,295。

所以当 i = 0 时再执行 i--,i 的值就变成了:4,294,967,295,然后再执行 i--,值为4,294,967,294 。i 一直递减,当减到 i = 0 时再执行 i--,i 的值又变成了:4,294,967,295,程序进入死循环

运行结果

🎄

int main()
{
	char a[1000];
	int i;
	for (i = 0; i < 1000; i++)
	{
		a[i] = -1 - i;
	}
	printf("%d", strlen(a));
	return 0;
}

代码分析

要做这道题就要先了解一个char类型中到底能放什么数值。

  • signed char : 1 byte  -> 8bit    取值范围:-128 ~ 127
  • unsigned char  :1 byte  -> 8bit   取值范围:0 ~ 255

有符号的二进制序列是一个循环

由此可以推出:signed short : 2 byte  -> 16bit    取值范围:-32768 ~ 32767

使用这个方式可以推出:signed int。

在编译器中可以使用 #include <limits.h> 这个头文件转到定义查看整型数据的范围

  •  了解了这些知识再看上面的代码,a[ i ] =  -1 - i;一直减下去会减到 -128,
  • -128再减下去就是127,127再减下去就到0,依次循环 1000 次。
  • 程序打印的是求字符串的长度,求字符串的长度关注的是 '\0','\0'的ASCII码值是0,所以在数组中找到出现的第一个0,在0之前的元素个数就是所求字符串的长度。
  • 数组中从 -1 到 -128,共128个元素,再从 127 到 1,共127个元素  。127+128=255。最终打印的结果为:255。

运行结果

🎄

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

代码分析

因为 i 是一个无符号char类型的变量,无符号char的取值范围是0~255,所以循环条件恒成立,程序进入死循环一直打印 hello world。

运行结果

使用无符号数做循环变量时可能会出错,所以要慎重使用。

三、浮点型在内存中的存储

  • 常见的浮点数:
  • 字面浮点型:3.14159
  • 科学计数法表示:1E10  --> 1.0 * 10^10
  • 浮点数家族包括: float、double、long double 类型。
  • 在编译器中可以使用 #include <float.h> 这个头文件转到定义查看浮点数的表示的范围

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

运行结果

由运行结果可知,整型数据与浮点型数据存储是不一样的。

2、浮点数存储规则

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

  • (-1)^S * M * 2^E
  • (-1)^s表示符号位,当s=0,V为正数;当s=1,V为负数。
  • M表示有效数字,大于等于1,小于2。
  • 2^E表示指数位。

举例说明:

  • 十进制的5.0,写成二进制是 101.0 ,相当于 1.01×2^2 。
  • 那么,按照上面V的格式,可以得出s=0,M=1.01,E=2。
  • 十进制的-5.0,写成二进制是 -101.0 ,相当于 -1.01×2^2 。
  • 那么,s=1,M=1.01,E=2。

再以5.5举例:

IEEE 754规定:
对于32位的浮点数,最高的1位是符号位s,接着的8位是指数E,剩下的23位为有效数字M

 对于64位的浮点数,最高的1位是符号位S,接着的11位是指数E,剩下的52位为有效数字M。

 IEEE 754对有效数字M和指数E,还有一些特别规定。 

(1)对有效数字 M 的规定:

  • 前面说过, 1 ≤ M < 2 ,也就是说,M可以写成 1.xxxxxx 的形式,其中xxxxxx表示小数部分。
  • IEEE 754规定,在计算机内部保存M时,默认这个数的第一位总是1,因此可以被舍去,只保存后面的 xxxxxx 部分。
  • 比如保存1.01的时候,只保存 01,等到读取的时候,再把第一位的1加上去。
  • 这样做的目的是:节省1位有效数字。
  • 以32位浮点数为例,留给M只有23位,将第一位的1舍去以后,等于可以保存24位有效数字,这样保存的数字精度就更高了。

(2)对指数 E 的规定:

  • 指数E,情况就比较复杂。首先,E为一个无符号整数(unsigned int)
  • 如果E为8位,它的取值范围为0~255;如果E为11位,它的取值范围为0~2047。
  • 但是科学计数法中的E是可以出现负数的,例如:
十进制的 0.5 转换成二进制就是 0.1;
0.1  -- > 1.0 * 2 ^ -1, M 是1.0,E 是 - 1
  • 所以IEEE 754规定,存入内存时E的真实值必须再加上一个中间数,对于8位的E,这个中间数是127;对于11位的E,这个中间数是1023。
  • 例如:2 ^ -1 的E是-1,保存32位浮点数时,必须保存为 -1+127 = 126,即 1111 1110;  2^10 的E是10,保存成32位浮点数时,必须保存成10+127=137,即1000 1001。
  • 2 ^ -1 的E是-1,保存64位浮点数时,必须保存为  -1 + 1023 = 1022,即 11 1111 1110。

指数E从内存中取出还可以再分成三种情况:

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

  • 浮点数就采用下面的规则表示,即指数E的计算值减去127(或1023),得到真实值,再将有效数字M前加上第一位的1。
  • 比如:
  • 0.5(1/2)的二进制形式为0.1,由于规定正数部分必须为1,即将小数点右移1位,则为
  • 1.0*2^(-1),其阶码为-1+127=126,表示为01111110,而尾数1.0去掉整数部分为0,补齐0到23位00000000000000000000000,则其二进制表示形式为:                               0   01111110   00000000000000000000000

(2)E全为0

  • 浮点数的指数E等于1-127(或者1-1023)即为真实值,有效数字M不再加上第一位的1,而是还原为0.xxxxxx的小数。这样做是为了表示±0,以及接近于0的很小的数字(无穷小)。

(3)E全为1

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

3、解释前面的题目,代码分析

  • 变量n的值为 9,是一个正数,正数的原反补都相同
  • 9的原、反、补码:0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 1
  • 定义了一个浮点类型的指针指向变量n所在的空间。n是以整型的形式存放在内存中。

整数:9

  • 第一个打印是以整型的形式打印,所以打印出来的结果是9。
  • 第二个打印是以浮点数的形式打印:

由于指数E全为0是E从内存中取出来的第二种情况。

因此,浮点数就写成:(-1)^0 × 0.00000000000000000001001×2^(-126)=1.001×2^(-146)
这是一个很小的接近于0的正数,所以打印出来的结果就是:0.000000。

浮点数:9.0


将二进制数: 0 1 0 0 0 0 0 1 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 转换成十六进制数:0x 41 10 00 00

 

由此可以观察到浮点数虽然有自己的存储方式,但是浮点数在内存中的存储也符合大小端的顺序。

  •  第三个打印是将浮点数以整数的形式打印,这个浮点数在内存中的补码就是刚才计算出来的二进制数:0 1 0 0 0 0 0 1 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0,以 %d 的形式打印是一个有符号数,这个二进制序列中最高位(符号位)是0,所以是正数,正数的原反补都相同,所以打印出来的结果就是直接将这个二进制序列还原成十进制得到的结果:

 所以打印出来的结果是:1091567616。

  • 第四个打印是将浮点数也浮点数的形式打印出来,所以结果是9.000000。

浮点数打印( %f、%lf )的结果默认小数点后有6位。

四、总结

这篇文章主要讲述了整型数据与浮点型数据在内存中的存储,还介绍了大小端的存储规则。通过做题,分析代码来深刻的理解数据在内存中的存储。

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

潇湘夜雨.

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值