[C进阶] 数据在内存中的存储——整形篇

简介: [C进阶] 数据在内存中的存储——整形篇

前言

学习一门语言就像是了解一个陌生人,首先我们要做的是从外貌和举止来宏观上考察一个人的特点,然后逐渐的对其进行深入了解,最终变得知根知底,畅所欲言。本章我们对数据存储的探讨其实就是在对C语言进行深入了解,因为只有知根知底才能畅所欲言!

本章重点:

  1. 区分并能够灵活转换整数的三种表示方式:原码、反码、补码
  2. 理解整数在储存时的大小端模式。
  3. 学会区分和界定有符号数与无符号数的取值范围。

一、类型的基本归类

在C语言中我们将类型大体分为5类,即整形、浮点型、构造类型、指针类型、空类型。

(1)整形家族:

类型 关键字 分类
字符类型 char signed char
unsigned char
短整型 short signed short
unsigned short
整形 int signed int
unsigned int
长整型 long signed long
unsigned long
更长的整形 long long signed long long
unsigned lonf long

补充:除 char类型外。当short、int、long、long单独使用时系统默认为signed类型。char类型单独使用是否表示signed char与编译器有关。例如VS下char=signed char

问题1:为什么char类型归属整形家族?

char类型在内存中存储数据时,本质上存储的是数据的ASCII码值,而ASCII码值又作为整数存在,所以也将char类型归属为整形家族。

问题2:为什么一种类型存在unsigned和signed?

1、unsigned表示无符号、signed表示有符号。二者的区别在于能不能表示负数,有符号可以表示负整数,无符号则不行,只能表示非负整数。

2、另外,表示的数值范围不同。从二进位制的角度上说,有符号int就是以最高位为符号位(计算机二进位制是没有正负号的,只有0和1,因此最高位为0表示正整数或0,最高位为1表示负整数),使用补码的形式储存负数,而无符号int不用考虑符号的问题,它的二进位制最高位仍是有效数位而不是符号位。(2)浮点型家族:

类型 关键字
单精度浮点型 float
双精度浮点型 double

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

类型 关键字
数组类型 类型+数组名+[大小]
结构体类型 struct
枚举类型 enum
联合类型 union

(4)指针类型:

类型 关键字
整形指针 int *pi
字符指针 char *pi
浮点型指针 float *pi
空指针 void *pi

注意: void*pv用于临时存放其他类型的地址,但是由于void没有具体类型,所以不能对其进行pv++*pv等相关指针操作

(5)空类型:

类型 关键字
空类型 void

void 通常应用于函数的返回类型、函数的参数、指针类型

二、类型存在的意义

  1. 不同的类型通常在内存中开辟不同的空间,丰富的类型增加了我们的选择性,同时在一定程度上避免了空间的浪费。
  2. 不同的类型决定我们看待内存空间的视角。比如int类型数据表示内存空间存放的是整数,float表示存放的是小数……

三、整形的存储

我们之前讲过一个变量的创建是要在内存中开辟空间的。空间的大小是根据不同的类型而决定的。那接下来我们谈谈数据在所开辟内存中到底是如何存储的?

🍑3.1 原码、反码、补码

计算机中整数是以2进制数字存储的,整数有三种2进制表示方法,即原码、反码和补码。

三种表示方法均有符号位数值位两部分,符号位都是用0表示“正”,用1表示“负”,而对于数值位正数的原、反、补码都相同;负整数的三种表示方法各不相同。

原码: 直接将数值按照正负数的形式翻译成二进制就可以得到原码。

反码: 将原码的符号位不变,其他位依次按位取反就可以得到反码。

补码: 反码+1就得到补码。

其实不必将其想的太过于复杂,也就是说,正整数的原反补码相同,计算和存储时不需要转换。而负整数原反补码,各不相同,需要对其进行相关转换后才能进行计算。

📝例如:

1.int a=20的原、反、补

2.int b=-10的原、反、补

🍑3.2 整形在内存中的存储

现在我们已经知道了计算机中整数的3种二进制表示方法,那么整数在内存中究竟是以那种形式存储的呢?为了能够直观的观察到整数在内存中的存储形式,我们以-10为例,通过内存窗口观察可得到:

内存中-10的16进制表示形式为:ff ff ff f6转化为2进制为:11111111111111111111111111110110显然这不正是-10的补码吗?

结论: 对于整形来说,数据存放内存中其实存放的是补码。

为什么整形在内存中存放的是补码?

1.在计算机系统中,数值一律用补码来表示和存储。原因在于,使用补码,可以将符号位和数值域统 一处理;

2.同时,加法和减法也可以统一处理(CPU只有加法器)此外,补码与原码相互转换,其运算过程 是相同的,不需要额外的硬件电路。

📝例如:计算int c= 1-1

1.在此过程中将其分别转换为补码后,符号位与数值位可以统一进行加法运算从而得到正确结果

2.原码——>补码与补码——>原码的计算路径是一样的

📖综上两点,我们可以体会到计算机设计的巧妙。设计师们用补码将原本复杂的问题极度简化,其构思之精妙实在惊为天人。

🍑3.3 大小端介绍及判断

细心地你有没有发现当我们观察内存中存放的补码时,存放的顺序有点不对劲。这是为什么呢?

在计算机中,数值是以字节为单位进行储存,当一个数值超过了1个字节,要存储在内存中就需要考虑存储的顺序。这里我们就引出了数据存储的大小端:

🌳(1) 什么是大端小端?

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


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

🌳(2) 为什么有大端和小端?

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


我们常用的 X86 结构是小端模式,而 KEIL C51 则为大端模式。很多的ARM,DSP都为小端模式。有些ARM处理器还可以由硬件来选择是大端模式还是小端模式。

🌳(3) 设计一个小程序,判断当前机器的字节序

✍️思路:创建一个大于1字节的变量,用char*指针访问他们第一个字节的数值,因为char*指针访问顺序为从低地址到高地址,如果访问得到的数值是变量的低位,则机器为小端存储,反之为大端存储。

📑代码展示:

//返回1表示小端
//返回0表示大端
#include<stdio.h>
int check_sys()
{
  int a = 1;
  return *(char*)&a;
}

int main()
{
  if(check_sys() == 1)
    printf("小端\n");
  else
    printf("大端\n");

  return 0;
}

🤔思考题:

unsigned int a= 0x1234;
unsigned char b=*(unsigned char *)&a;

在32位大端模式处理器上变量b等于多少?答案是0x00,为什么呢?这道题留给大家思考!

🍑3.4 有符号数和无符号数的取值范围如何定?

📝以signed shortunsigned short类型为例:

推而广之:

类型 空间大小
signed char -128~127
unsigned char 0~255
signed short -32768~32767
unsigned short 0~65535
signed int -2147483648~2147483648
unsigned int 0~4294967295

📝牛刀小试:

下面代码在VS下输出的结果是?

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

🤔分析:用库函数strlen()求字符串长度,遇到‘\0’即ASCII码值为0时停止,返回0之前的字符长度。

总结

最后就浅浅总结一下吧!

本章主要讲解了基本的数据类型,着重介绍了整形家族及整形在内存中的储存。

本章重点:

  1. 区分并能够灵活转换整数的三种表示方式:原码、反码、补码
  2. 理解整数在储存时的大小端模式。
  3. 学会区分和界定有符号数与无符号数的取值范围。

铁汁们,我们下期再见!😊😊😊


相关文章
|
12天前
|
存储 编译器 数据处理
C 语言结构体与位域:高效数据组织与内存优化
C语言中的结构体与位域是实现高效数据组织和内存优化的重要工具。结构体允许将不同类型的数据组合成一个整体,而位域则进一步允许对结构体成员的位进行精细控制,以节省内存空间。两者结合使用,可在嵌入式系统等资源受限环境中发挥巨大作用。
35 11
|
2月前
|
监控 算法 应用服务中间件
“四两拨千斤” —— 1.2MB 数据如何吃掉 10GB 内存
一个特殊请求引发服务器内存用量暴涨进而导致进程 OOM 的惨案。
|
2月前
|
存储 C语言
数据在内存中的存储方式
本文介绍了计算机中整数和浮点数的存储方式,包括整数的原码、反码、补码,以及浮点数的IEEE754标准存储格式。同时,探讨了大小端字节序的概念及其判断方法,通过实例代码展示了这些概念的实际应用。
64 1
|
2月前
|
存储
共用体在内存中如何存储数据
共用体(Union)在内存中为所有成员分配同一段内存空间,大小等于最大成员所需的空间。这意味着所有成员共享同一块内存,但同一时间只能存储其中一个成员的数据,无法同时保存多个成员的值。
|
5月前
|
存储 分布式计算 Hadoop
HadoopCPU、内存、存储限制
【7月更文挑战第13天】
299 14
|
4月前
|
存储 编译器 C语言
【C语言篇】数据在内存中的存储(超详细)
浮点数就采⽤下⾯的规则表⽰,即指数E的真实值加上127(或1023),再将有效数字M去掉整数部分的1。
394 0
|
2月前
|
存储 弹性计算 算法
前端大模型应用笔记(四):如何在资源受限例如1核和1G内存的端侧或ECS上运行一个合适的向量存储库及如何优化
本文探讨了在资源受限的嵌入式设备(如1核处理器和1GB内存)上实现高效向量存储和检索的方法,旨在支持端侧大模型应用。文章分析了Annoy、HNSWLib、NMSLib、FLANN、VP-Trees和Lshbox等向量存储库的特点与适用场景,推荐Annoy作为多数情况下的首选方案,并提出了数据预处理、索引优化、查询优化等策略以提升性能。通过这些方法,即使在资源受限的环境中也能实现高效的向量检索。
|
2月前
|
存储 编译器
数据在内存中的存储
数据在内存中的存储
42 4
|
2月前
|
存储 Java
JVM知识体系学习四:排序规范(happens-before原则)、对象创建过程、对象的内存中存储布局、对象的大小、对象头内容、对象如何定位、对象如何分配
这篇文章详细地介绍了Java对象的创建过程、内存布局、对象头的MarkWord、对象的定位方式以及对象的分配策略,并深入探讨了happens-before原则以确保多线程环境下的正确同步。
57 0
JVM知识体系学习四:排序规范(happens-before原则)、对象创建过程、对象的内存中存储布局、对象的大小、对象头内容、对象如何定位、对象如何分配
|
2月前
|
存储 机器学习/深度学习 人工智能
数据在内存中的存储
数据在内存中的存储