数据在内存中的存储2

简介: 数据在内存中的存储2


数据在内存中的存储1https://developer.aliyun.com/article/1477743


整型提升

整型提升是指将较小的整数类型转换为较大的整数类型的过程。举个例子,如果有一个int类型的变量和一个char类型的变量进行加法运算时,char类型的变量会被提升为int类型再进行运算。这是因为int类型大于char类型,这样提升能有效避免精度的丢失。

整型提升的规则:

  1. 有符号整数提升按照数据类型的符号位来提升,提升时补符号位
  2. 无符号整数提升,高位直接补0

下面举两个整型提升的例子

例题1

//有符号整型提升
//负数整型提升
char a = -1;
内存中存储:11111111(8位)
整型提升后:11111111111111111111111111111111(32位)
//正数整型提升
char a = 1;
内存中存储:00000001(8位)
整型提升后:00000000000000000000000000000001(32位)
//无符号整型提升
unsigned char a = -1;
内存中存储:11111111(8位)
整型提升后:00000000000000000000000011111111(32位)
unsigned char a = 1;
内存中存储:00000001(8位)
整型提升后:00000000000000000000000000000001(32位)

现在我们来看一道例题

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

问:求以上程序的输出结果?

先看a再打印出来之前经历的转换

a
原码:10000001
反码:11111110
补码:11111111
打印时整型提升(补符号位,这时符号位为1):
11111111111111111111111111111111(此时这个二进制再计算机内存中,为补码)
打印原码:10000000000000000000000000000001
打印结果为:-1

由于signed char == char,所以b打印的结果也为-1

再来看c

c
原码:10000001
反码:11111110
补码:11111111(-1会先被转化成补码赋给无符号的c)
打印时整型提升(无符号位提升,直接补0):
00000000000000000000000011111111(此时这个二进制再计算机内存中,为补码)
打印原码:00000000000000000000000011111111(无符号数原码和补码相等)
打印结果为:255

讲到这里,刚刚的题也给大家解释清楚了,现在想再补充一个内容,以char举例,竟然char的范围是-128~127/0~255,那么用二进制码表示的位置是怎样的呢?这里展示一个图解,能极大的帮助大家理解这个问题。

以上两张图,外圈是存储再计算机中的补码表示形式,内圈是对应类型原码所表示的十进制数值,此图用以表示有符号和无符号对应的数字存储形式,其实不但应用于char类型,同样也适用于int,long,longlong等类型。

这里还要注明一点,char类型中10000000中按照常理来说表示的应该是-0才对,但是再计算机编译环境中的规定,表示的是-128,不只是char,int,long等其他类型也是如此。所以,再有符号类型中,负数最小值的绝对值一般比正数的最大值大1。

下面再来看例题二,一下就简单多了

例题2

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

问:以上代码打印结果?

对于-128,计算机中放入的对应值在上图中已经显而易见,10000000,而%u则表示打印无符号的整型,在-128的整型提升中,10000000,变成了11111111 11111111 11111111 10000000(有符号数整型提升补符号位),在打印无符号数的时候结果为4294967168。

例题3

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

问:以上代码的打印结果?

可以注意到128已经超出了char能存储的范围,但是这个程序硬是将这样一个值放入其中的化,那么128会转化成补码10000000,放入a,那a的补码10000000解读过来又是什么呢?是-128,所以这是和例题二便重合了,打印结果依然是 4294967168。

例题4

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

问:以上代码的打印结果?

我们之前已经介绍过了char类型数据在内存中的存储,题目代码中,是由-1逐渐存到-1000,那在内存中便是-1,-2,-3。。。-127,-128,127,126,125。。。3,2,1,0,-1,-2。。。

以以上这种方式存储在内存中,并且存储的数字是一个完美的闭环,在打印字符串长度的过程中,我们知道strlen计算的截止标志是'\0',那么strlen最后计算的便是-1,-2,-3。。。-127,-128,127,126,125。。。3,2,1这之间数字的个数了,程序最终运行打印得255。

如果不了解strlen和字符串函数,我的上一篇博客有详细讲到大家可以参考(链接如下):

字符串函数-C语言-CSDN博客

例题5

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

问:以上代码输出结果?

由于unsigned char类型最大值也无法超过255,所以此程序会循环打印Hello world!

例题6

#include <stdio.h>
int main()
{
  int a[4] = { 1,2,3,4 };
  int* ptr1 = (int*)(&a + 1);
  int* ptr2 = (int*)((int)a + 1);
  printf("%x,%x\n", ptr1[-1], *ptr2);
  return 0;
}

问:以上代码输出结果?

&a取出整个数组的地址,加一个1跳过一个数组指向末尾,在打印ptr1[-1]时相当于*(ptr1 - 1),所以前一个打印的值是4

第二个指针在初始化的时候转成了整型,加一再解引用相当于往后移了一个字节,最后转成int*类型指针最后打印结果为0x02000000

浮点数在内存中的存储

再以一道题引入

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

问:以上代码输出结果?

先把答案给大家,看看大家是否能理解

n的值为:9
*pFloat的值为:0.000000
num的值为:1091567616
*pFloat的值为:9.000000

相信第二行和第三行的结果会与本来所想的结果不同吧

具体原因还是要来了解一下浮点数再内存中的存储

浮点数在内存中的存储遵循IEEE754标准,是目前最广泛使用的浮点数表示方法

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

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

其中:(-1)^S表示符号位,S=0时V为正数;S=1时V为负数

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

2 ^E表示指数位

单精度浮点数,也就是float:有一个符号位,8个指数位以及23位尾数位

双精度浮点数,也就是double:有一个符号位,11位指数位以及52位尾数位

拿5.0来举例:

十进制的5.0,写成二进制为101.0,相当于1.01*2^2。

可以得到S=0,M=1.01,E=2。在计算机保存M时,默认第一位总是1,因此可以被舍去,只保留后面的部分。在保存1.01的时候,在计算机中保存的数为01,在读取时,在加上之前省略的1,变回1.01,再将剩下的用0填充。因此M在储存1.01时,便为01000000000000000000000(23位)

再来看E,E作为一个无符号整数,再E为8位时,取值范围是0~255;如果为11位,则取值为0~2047,但是在实际运用的过程中,也常常会碰到指数为负的情况,这怎么办呢?据此,IEEE754规定,存入内存时在指数位E之间加一个中间数,对于八位的E,这个中间数为127;而对于11位的E,这个数为1023,指数位存入的数据便是(八位:127+E)(十一位:1023+E)所以就可以据此来表示指数位的正负了。

最终5.0在计算机中存储的数据为:

0 10000001 01000000000000000000000

从内存中读取浮点数

在读取浮点数的过程中,根据数值E的不同,可以分以下三类情况

E不全为0或一

这样的指数值代表了有效的浮点数。在解析中,指数值会见去中间量,得到最终的指数值。

对语32位float类型浮点数,中间值为127,如刚才存入的E:10000001,最终指数值为(129-128=2) 对于64位的double也是同理。

E全为0

这种情况下,浮点数表示出来的是一种极其小的数值,浮点数小于等于10^-127/10^-1023,这时,返回浮点数时M便不在第一位补1,而是直接还原为0.000.....的小数

E全为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;
}

9以整型形式存储的过程中,得到的二进制为:

0000 0000 0000 0000 0000 0000 0000 1001

将此数以浮点数形式读出,会发现E全为0,是上方提到的情况,所以小数就为0.000000

而浮点数9.0,为二进制1001.0,转换成存入计算机中的形式1.001*2^3,S=0,E=127+3=130,M为00100000000000000000000,所以二进制表示式为

0 10000010 00100000000000000000000

将此数转化成整型打印出来便是 1091567616了。

博主的话

一篇博客写下来真的耗时耗力,本来想着上午快快肝完,这一写就是将近一整天┭┮﹏┭┮,如果感觉本篇博客对你有帮助的话,还请留个小赞,放个收藏,点个关注再走啊!如果本博客有任何错误和不完善还请各位大佬再评论区指正,那就先到这里了,下篇博客再见。比

相关文章
|
4天前
|
存储
数据在内存中的存储之整数存储
数据在内存中的存储之整数存储
11 0
|
3天前
|
存储 算法
【三种方法】求一个整数存储在内存中二进制中的1的个数附两道课外练习题
【三种方法】求一个整数存储在内存中二进制中的1的个数附两道课外练习题
7 0
|
11天前
|
存储 NoSQL Oracle
Oracle 12c的内存列存储:数据的“闪电侠”
【4月更文挑战第19天】Oracle 12c的内存列存储以超高速度革新数据处理,结合列存储与内存技术,实现快速查询与压缩。它支持向量化查询和并行处理,提升效率,但需合理配置以平衡系统资源。作为数据管理员,应善用此功能,适应业务需求和技术发展。
|
21天前
|
存储 编译器
数据在内存中的存储1
数据在内存中的存储
|
1月前
|
存储 编译器 程序员
【C语言】整形数据和浮点型数据在内存中的存储
【C语言】整形数据和浮点型数据在内存中的存储
16 0
|
1月前
|
存储 JSON 监控
Higress Controller**不是将配置信息推送到Istio的内存存储里面的**。
【2月更文挑战第30天】Higress Controller**不是将配置信息推送到Istio的内存存储里面的**。
14 1
|
2月前
|
存储 编译器 C语言
C语言:数据在内存中的存储形式
C语言:数据在内存中的存储形式
|
1月前
|
存储 C语言
C语言--------数据在内存中的存储
C语言--------数据在内存中的存储
26 0
|
1月前
|
存储 Windows 容器
浮点数在内存中的存储
浮点数在内存中的存储
18 2
|
1月前
|
存储 小程序 C语言
【深度剖析数据在内存中的存储】C语言
【深度剖析数据在内存中的存储】C语言