数据在内存中的存储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了。

博主的话

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

相关文章
|
1月前
|
存储 编译器 数据处理
C 语言结构体与位域:高效数据组织与内存优化
C语言中的结构体与位域是实现高效数据组织和内存优化的重要工具。结构体允许将不同类型的数据组合成一个整体,而位域则进一步允许对结构体成员的位进行精细控制,以节省内存空间。两者结合使用,可在嵌入式系统等资源受限环境中发挥巨大作用。
62 11
|
2月前
|
监控 算法 应用服务中间件
“四两拨千斤” —— 1.2MB 数据如何吃掉 10GB 内存
一个特殊请求引发服务器内存用量暴涨进而导致进程 OOM 的惨案。
|
2月前
|
存储 C语言
数据在内存中的存储方式
本文介绍了计算机中整数和浮点数的存储方式,包括整数的原码、反码、补码,以及浮点数的IEEE754标准存储格式。同时,探讨了大小端字节序的概念及其判断方法,通过实例代码展示了这些概念的实际应用。
117 1
|
2月前
|
存储
共用体在内存中如何存储数据
共用体(Union)在内存中为所有成员分配同一段内存空间,大小等于最大成员所需的空间。这意味着所有成员共享同一块内存,但同一时间只能存储其中一个成员的数据,无法同时保存多个成员的值。
|
2月前
|
监控 Java easyexcel
面试官:POI大量数据读取内存溢出?如何解决?
【10月更文挑战第14天】 在处理大量数据时,使用Apache POI库读取Excel文件可能会导致内存溢出的问题。这是因为POI在读取Excel文件时,会将整个文档加载到内存中,如果文件过大,就会消耗大量内存。以下是一些解决这一问题的策略:
296 1
|
2月前
|
存储 弹性计算 算法
前端大模型应用笔记(四):如何在资源受限例如1核和1G内存的端侧或ECS上运行一个合适的向量存储库及如何优化
本文探讨了在资源受限的嵌入式设备(如1核处理器和1GB内存)上实现高效向量存储和检索的方法,旨在支持端侧大模型应用。文章分析了Annoy、HNSWLib、NMSLib、FLANN、VP-Trees和Lshbox等向量存储库的特点与适用场景,推荐Annoy作为多数情况下的首选方案,并提出了数据预处理、索引优化、查询优化等策略以提升性能。通过这些方法,即使在资源受限的环境中也能实现高效的向量检索。
|
2月前
|
缓存 安全 Java
使用 Java 内存模型解决多线程中的数据竞争问题
【10月更文挑战第11天】在 Java 多线程编程中,数据竞争是一个常见问题。通过使用 `synchronized` 关键字、`volatile` 关键字、原子类、显式锁、避免共享可变数据、合理设计数据结构、遵循线程安全原则和使用线程池等方法,可以有效解决数据竞争问题,确保程序的正确性和稳定性。
64 2
|
1月前
|
缓存 Prometheus 监控
Elasticsearch集群JVM调优设置合适的堆内存大小
Elasticsearch集群JVM调优设置合适的堆内存大小
291 1
|
22天前
|
存储 监控 算法
深入探索Java虚拟机(JVM)的内存管理机制
本文旨在为读者提供对Java虚拟机(JVM)内存管理机制的深入理解。通过详细解析JVM的内存结构、垃圾回收算法以及性能优化策略,本文不仅揭示了Java程序高效运行背后的原理,还为开发者提供了优化应用程序性能的实用技巧。不同于常规摘要仅概述文章大意,本文摘要将简要介绍JVM内存管理的关键点,为读者提供一个清晰的学习路线图。
|
1月前
|
Java
JVM内存参数
-Xmx[]:堆空间最大内存 -Xms[]:堆空间最小内存,一般设置成跟堆空间最大内存一样的 -Xmn[]:新生代的最大内存 -xx[use 垃圾回收器名称]:指定垃圾回收器 -xss:设置单个线程栈大小 一般设堆空间为最大可用物理地址的百分之80

热门文章

最新文章