【落羽的落羽 C语言篇】数据存储简介

news/2024/12/23 16:14:53 标签: c语言

在这里插入图片描述

文章目录

  • 一、整型提升
    • 1. 概念
    • 2. 规则
  • 二、大小端字节序
    • 1. 概念
    • 2. 练习
      • 练习1
      • 练习2
  • 三、浮点数在内存中的存储
    • 1. 规则
    • 2. 练习

一、整型提升

1. 概念

C语言中,整型算术运算至少是以“缺省整型类型”(int)的精度来进行的。为了达到这个精度,比int类型精度低的char和short等短整型类型的操作数,在使用前会转换成普通整型(int),这种转换被称为整型提升
不太好理解,我们先看一个奇怪的现象:

char a = 'a';
char b = 'b';
char c = a + b;
printf("a的ASCII值:%d\n", a);
printf("b的ASCII值:%d\n", b);
printf("c的值:%d\n", c);

你觉得这段代码的结果是什么样的?c的值是不是a和b的ASCII值相加呢?
在这里插入图片描述

很反直觉的结果。
这是因为a和b是char类型的数据,a+b在进行计算前,要进行整型提升,然后执行加法运算得到一个占据4字节的int类型数据,但这个数据要存储在只有一字节大小的c里呀。所以结果会被截断,然后再存储进c中。具体规律我们再来看:

2. 规则

进行整型提升的规则是:

  • 有符号整数的提升:补码高位补充符号位数
  • 无符号整数的提升:补码高位补充0

以char类型为例,char也属于整型家族,是有符号的类型

假如有char a = -1;,-1本来是一个十进制数,会占据四个字节,补码是11111111 11111111 11111111 11111110,但变量a只有八个比特位,只能存下前八位11111111。
如果之后的整数运算用到了变量a,就要先对a进行整型提升,11111111的符号位是1,高位补充1,结果是11111111 11111111 11111111 11111111(补码),即十进制数-1。

假如有char b = 1;,1本来是一个十进制数,会占据四个字节,补码是00000000 00000000 00000000 00000001,但变量b只有八个比特位,只能存下前八位00000001。
如果之后的整数运算用到了变量b,就要先对b进行整型提升,00000001的符号位是0,高位补充0,结果是00000000 00000000 00000000 00000001(补码),即十进制数1。

上面的两个例子中,a和b进行整型提升后,它们的值仍是-1和1。
但假如有char c = 128;,是什么结果呢?128本来是一个十进制数,会占据四个字节,补码是00000000 00000000 00000000 10000000,但变量c只有八个比特位,只能存下前八位10000000。这时,符号位变成了1。后续使用变量c进行整数运算时,要先对它整型提升,高位补充符号位1,结果是11111111 11111111 11111111 10000000(补码),是十进制数-128!

在这里插入图片描述

通过这个规律,我们可以总结出一个很重要的规则:由于整型提升的存在,char类型只有八个二进制位,char类型的变量只能存放-128 ~ 127的数,是一个循环。如果有char x = 127, y = x+1;那么y的值实际上就是-128。
在这里插入图片描述在这里插入图片描述

而若是unsigned char类型变量,由于是无符号类型,没有符号位,八个二进制位能表示出的最大十进制数是255,这种类型变量就只能存放0 ~ 255的数。
同样的道理,short类型的变量只能存放-32768 ~ 32767的数。

举个栗子:

char a = -128;
printf("%u",a);//%u是十进制的无符号整数

这段代码的结果是一个42亿多的数字。这是因为-128的补码本来是11111111 11111111 11111111 10000000,但a中只能存放前八位10000000。打印a也是一种对a的运算,要先进行整型提升。符号位是1,提升后是11111111 11111111 11111111 10000000。但%u说明这是一个无符号整数,最高位的1也要计算,最后这个32个二进制位换算成十进制就是42亿多的一个数。

在这里插入图片描述

在这里插入图片描述

二、大小端字节序

1. 概念

我们之前已经了解过整数在内存中的存储方式了——补码。
我们再来观察一个细节:
在这里插入图片描述

调试的时候,我们可以看到a中的0x11223344这个十六进制数是按着字节为单位,在内存中倒着存储的。(四个二进制数换算成一个十六进制数,也就是两个十六进制数占据一个字节)

超过一个字节的数据在内存中存储的时候,就会有存储顺序的问题,按照正序存放或是逆序存放,我们分为大端字节存储和小端字节存储模式:

  • 大端字节存储模式:数据的低位字节内容保存在内存的高地址处,数据的高位字节内容保存在内存的低地址处。如数0x1123344在内存中,四个地址由低到高存放的分别是11 22 33 44。

  • 小端字节存储模式:数据的低位字节内容保存在内存的低地址处,数据的高位字节内容保存在内存的高地址处。如数0x1123344在内存中,四个地址由低到高存放的分别是44 33 22 11。

回看上图,可以看出我的系统环境是小端字节存储模式。

2. 练习

练习1

百度的一道笔试题:设计一个小程序来判断当前机器的字节序

思路:我们将整数1存放到一个int变量中,看一看它四个字节中最地址最低的数据是什么。1的十六进制是0x00000001,四个字节由地址低到高:如果是大端应该是00 00 00 01,如果是小端就应该是01 00 00 00

#include<stdio.h>
int check()
{
    int i = 1;
    return *(char*)&i;
//char*强制类型转换&i,再解引用,就能找到i的四个当中地址最低的字节的内容,00或01
}

int main()
{
    int ret = check();
    if(ret==1)//如果ret是1,说明i中的1存放形式是01 00 00 00,是小端
        printf("小端");
    else//如果ret是0,说明i中的1存放形式是00 00 00 01,是大端
        printf("大端");
    return 0;
}

练习2

这段代码的结果是什么

//X86环境,小端字节序模式
#include<stdio.h>
int main()
{
    int a[4] = {1,2,3,4};
    int* p1 = (int*)(&a+1);
    int* p2 = (int*)((int)a+1);
    printf("%x,%x",p1[-1],*p2);//%x是十六进制数,但VS打印会省略非0数前的所有0和x
    return 0;
}

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

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

1. 规则

浮点数类型包括float、double、long double等
浮点数在内存中的存储方式相对复杂:
根据国际标准IEEE 754,任意一个二进制浮点数V都可以表示成下面的形式:

V = (-1)S × M × 2E

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

举例,十进制的5.0,写成二进制是101.0,相当于1.01×22。按照上面V的格式,S=0,M=1.01,E=2。十进制的-5.0,写成二进制是-101.0,相当于-1.01×22。按照上面V的格式,S=1,M=1.01,E=2。

同时,IEEE 754还规定:

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

前面说过,1<=M<2,也就是说,M的个位一定是1。因此,IEEE 754还规定,在计算机内部保存M时,默认这个数第一位总是1,因此可以被省略,只保存小数部分。比如保存1.012345时,只保存012345,等到读取的时候,再把第一位的1加上去。这样做的目的是节省一份空间,相对于可以多存储一位数字。

至于指数E,情况就比较复杂。
E是一个无符号整数(unsigned int)。如果E为8位,它的取值范围是0 ~ 255;如果E为11位,它的取值范围是0 ~ 2047。但是,科学计数法的E可以是负数。所以IEEE 754还规定,存入内存的真实值必须再加上一个中间数,对于8位的E中间数是127,对于11位的E中间数是1023。举个例子,如果一个浮点数的E是12,它保存成32位浮点数时,E会被保存成12+127=139,即10001011;它保存成64位浮点数时,E会被保存成12+1023=1035,即10000001011。

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

  • E中既有0也有1:将E的二进制序列换算成十进制数,减去127(或1023),得到真实值
  • E全为1:如果M不是0的话,V就是正负无穷大
  • E全为0:V是无限接近于0的很小的数字

理解就好~

2. 练习

通过前面的知识,思考一下这段代码的结果是什么:

#include<stdio.h>
int main()
{
	int n = 9;
	float* p = (float*)&n;
	*p = 9.0;
	printf("%d", n);
	return 0;
}

答案是:
在这里插入图片描述

解析:
将float类型的9.0存入n的32的比特位时,要遵循32位浮点数的存储规则。9.0的二进制是1001.0,即(-1)0 × 1.001 × 23,那么,第一位的符号位E是0,有效数字M等于001后面再加20个0补全23位,指数E等于3+127等于130,即10000010 。
所以,9.0在内存中的存储是:0 10000010 00100000000000000000000,但如果要被当成整数来解析时,这就是整数在内存中的补码了,直接换算成十进制的结果就是1091567616。

在这里插入图片描述

本篇完,感谢阅读


http://www.niftyadmin.cn/n/5796735.html

相关文章

Android笔试面试题AI答之Android基础(3)

文章目录 1.谈一谈 Android 的安全机制一、系统架构层面的安全设计二、核心安全机制三、其他安全机制与措施 2.Android 的四大组件是哪四大&#xff1f;3.Android 的四大组件都需要在清单文件中注册吗&#xff1f;4.介绍几个常用的Linux命令一、文件和目录管理二、用户和权限管…

GESP202309 二级【小杨的 X 字矩阵】题解(AC)

》》》点我查看「视频」详解》》》 [GESP202309 二级] 小杨的 X 字矩阵 题目描述 小杨想要构造一个 的 X 字矩阵&#xff08; 为奇数&#xff09;&#xff0c;这个矩阵的两条对角线都是半角加号 &#xff0c;其余都是半角减号 - 。例如&#xff0c;一个 5 5 5 \times 5 5…

C++的封装(十四):《设计模式》这本书

很多C学习者学到对C语言有一定自信后&#xff0c;会去读一下《设计模式》这本书。希望能够提升自己的设计水平。 据我所知&#xff0c;围绕C语言出了很多书。因为正好赶上泡沫经济时代。大家一拥而上&#xff0c;自己半懂不懂就出书&#xff0c;抢着出书收割读者&#xff0c;出…

Java阶段四-SpringBoot02

第4章-第2节 一、知识点 Mybatis-Plus、Lambda 二、目标 理解什么是Mybatis-Plus 理解Mybatis和Mybatis-Plus的区别 学会使用Mybatis-Plus的CRUD 条件构造器的使用 分页查询的使用 三、内容分析 重点 学会使用Mybatis-Plus的CRUD 什么是查询过滤&#xff0c;有什么用…

【系统移植】NFS服务器环境搭建——挂载根文件系统

什么是NFS&#xff1f; NFS&#xff08;Network File System&#xff09;即网络文件系统&#xff0c;其基于UDP/IP 使用NFS能够在不同计算机之间通过网络进行文件共享&#xff0c;能使使用者访问网络上其它计算机中的文件就像在访问自己的计算机一样&#xff0c;文件只存在于服…

C++中如何实现序列化和反序列化?

概念 在C中&#xff0c;序列化和反序列化通常需要通过自定义代码将对象的状态转换为字节流&#xff0c;或者将字节流转换回对象。这可以通过文件操作、网络传输或其他形式的存储来实现。 使用简单的文件流 我们可以通过 ofstream 和 ifstream 类来实现基本的序列化与反序列化…

[react 3种方法] 获取ant组件ref用ts如何定义?

获取ant的轮播图组件, 我用ts如何定义? Strongly Type useRef with ElementRef | Total TypeScript import React, { ElementRef } from react; const lunboRef useRef<ElementRef<typeof Carousel>>(null); <Carousel autoplay ref{lunboRef}> 这样就…

Vue.js 核心概念:模板、指令、数据绑定

Vue.js 核心概念&#xff1a;模板、指令、数据绑定 本文我们来聊一聊 Vue.js 的核心概念&#xff0c;重点讲解 Vue 中的 模板&#xff08;Template&#xff09;、指令&#xff08;Directives&#xff09; 和 数据绑定&#xff08;Data Binding&#xff09;。这些概念是 Vue.js…