【进阶C语言】数据在内存中的存储

简介: 数据类型的介绍整形数据在内存中的存储方式浮点型数据在内存中的存储方式

一、数据类型的介绍

1.整形家族

(1)char--字符型


单位:一个字节,包括unsigned char和signed char


(2)short--短整形


单位:两个字节,包括unsigned short[int]和signed short[int]


(3)int--整形


单位:四个字节,包括unsigned int和signed int


(4)long--长整型


单位:4/8个字节,包括unsigned long和signed long

2.浮点型家族

(1)float--单精度浮点数


单位:四个字节


(2)double--双精度浮点数


单位:八个字节


3.构造类型

(1)数组类型


(2)结构体类型struct


(3)枚举类型enum


(4)联合类型union


4.指针类型

(1)int *pi--整形指针


(2)char *pc--字符型指针


(3)double *pf--双精度指针


(4)void *pv--空类型指针


5.空类型

void 表示空类型(无类型)

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


二、整形数据在内存中的存储方式

前言:整形在内存中存储的是补码的二进制序列(在初阶C语言的操作符1中有详解介绍原、反、补码)


1.原、反、补码

(1)概念介绍


整数的二进制表示形式有原码、反码和补码三种,对于有符号的数据类型来说,这三种表示方法均可以分为符号位和数值位两部分,第一位数字(最高位)表示符号位,不差于数值大小的表示,正数用0表示,负数则是用1表示。若是无符号数据类型,则没有符号位,每一位都参与数值的表示。


(2)原码


直接将数值按照正负数的形式翻译成二进制就可以得到原码,有多少个比特位就有多少位二进制位。正数的原码、反码和补码都相同;但是负数则需要更具下面的定义去计算。

(3)反码

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

(4)补码

反码 +1 就得到补码。


2.补码在内存中的存储方法

(1)使用补码存储的意义


官方定义:


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

一处理;

同时,加法和减法也可以统一处理( CPU 只有加法器 )此外,补码与原码相互转换,其运算过程

是相同的,不需要额外的硬件电路。

民间定义:方便转换和计算,如计算1-1的时候


(2)存储顺序


一个内存单元为一个字节,一般数据在内存中的显示形式都是十六进制,四个二进制数字=一个十六进制,八个二进制=一个字节。


代码:

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

a的补码:11111111 11111111 11111111 11110110

b的补码11111111 11111111 11111111 11110110

image.png

image.png

分析图:

image.png

    为什么在内存中的存储顺序是反着的呢?所以下面的大小端介绍会告诉你答案!


3.大小端介绍(*)

(1)官方定义


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

中;

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

址中。

(2)大小端民间定义


我们知道:


    内存也是有顺序的,所以内存也就有高地址和低地址之分。


    数据的每位数字内容也是有大小之分的,如:个位、十位和百位。所以数据也就有了低字节位(最低位,如个位)和高字节位


引出概念:


大端字节序储存:把一个数据的低字节位数据放在内存的高地址储存,数据的高字节位数据放在内存的低地址位储存。


小端字节序存储:把一个数据的低字节位数据存放在内存的低地址中,数据的高字节位数据存储在内存的高地址处。


     大小端的存储顺序是由编译器决定;而且对一个字节的数据没有作用


(3)简单实例操作


代码:

#include<stdio.h>
int main()
{
  int a = 0x11223344;//十六进制数字
  return 0;
}

数据在内存中存储:

image.png

(4)实战练习

题目:设计程序判断该机器的字节序

代码:

#include<stdio.h>
int main()
{
  int a = 1;
  char* p = (char*) & a;//只能访问一个字节
  if (*p == 1)
    printf("小端\n");
  else
    printf("大端\n");
  return 0;
}

运行结果:

image.png

4.数据的存储与打印(*)--char类型

前言:我们已经知道了整形数据是以二进制的补码在内存中存储的,接下来我们更进一步了解他的打印方式(包括有符号和无符号的深度刨析),前面在操作符2中我们已略微了解到了一种方法:整形提升。


signed char--8个比特位,所以最大可以存的数字是-128-127


unsigned char--0-256


(1)char类型存储整形范围


引例:

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

运行结果:

image.png

为什么同样都是存储-1,结果却大相径庭呢?接下来让我们走入char类型的世界


char类型大介绍:

  首先,char内存占一个字节,一个字节=8个bit,所以就只能存放8个二进制位,所以可以存放的数据大小有范围。

对于signed char(有符号的char):

image.png

由此可知,存入char的整形数据的范围是:-128--127。

当然超过这个范围的数据也可以存进去,但是实际的效果还是在:-128--127

对于unsigned char(无符号char):

image.png

现在你已经知道了char类型的范围,接下来一起进入实战模拟吧。

(2)实战模拟

(1)实战1

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

运行结果:

image.png

%d:以十进制的形式,打印有符号的整数

%u:以十进制的形式,打印无符号的整数

结果分析:

第一步:分析存入各个变量中的数据

image.png

第二步:分析打印的过程

image.png

(2)实战2

#include<stdio.h>
int main()
{
  unsigned int i;
  for (i = 9; i >= 0; i--)
  {
    printf("%u\n",i);
  }
  return 0;
}

运行结果:

image.png

这个程序不应该执行九次就停止运行了吗,怎么还能得出这么大的数字而且还死循环了。

image.png


(3)实战3

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

运行结果:

image.png

strlen是计算字符串长度的,当遇到\0的时候才会停下。结果显示该数组的大小为255

image.png

三、浮点型数据在内存中的存储方式

     浮点数其实就是小数,精度越高,小数的位数越高。浮点数也是以二进制的形式存储的,但不是像整形数据一样以补码的形式存储。


1.认识浮点数

(1)浮点数类型分类


float---单精度浮点数


double---双精度浮点数


long double---长精度浮点型


(2)浮点数形式


直接形式:3.14159


科学计数法的形式:1E54==1.0*10^54


2.浮点数转换规则

(1)定义(表示规则)


自我描述:任何一个二进制浮点数V都可以这么表示


表达式:V=(-1)^S*M*2^E


(-1)^S表示符号位,S为次方;当S=0,V为正数;当S=1,V为负数。


M表示有效数字,1<=M<2


2^E表示表示指数位


(2)浮点数的二进制表示

image.png

例如:十进制的:10.5;转化成二进制:1010.1

(3)思路分解

十进制:10.5

二进制:1010.1

转换成浮点数表示形式:V=(-1)^S*M*2^E

image.png

3.浮点数存储规则

弄清楚了浮点数是怎么转换成二进制的,接下来了解二进制浮点数是怎么存到内存中的。


(1)存储位序


根据公式:V=(-1)^S*M*2^E。我们只需要将S、M和E存入内存中即可(以二进制存入)


规则:

   32位(bit)的浮点数(如:float),最高的一位是符号位S,接着的8位是指数E,剩下的23位为有效数字M。

     64位(bit)的浮点数(如:double),最高的一位是符号位S,接着的11位是指数位E,剩下的52位为有效数字M。


图示:


32位:

image.png

64位:

image.png

双精度浮点数储存模型


(2)规定

M:因为1<=M<2,所以M可以写成:1.xxxxxx的形式,其中xxxxxx表示小数部分。由于M的第一个数默认为1,所以1可以省略掉,只需要把xxxxxx存入内存中即可,后面不够自动补0。当读数的时候,再把1加回去。


E:E是一个无符号整数,但是E会出现有负数的情况。所以:对于存入内存E的真实值,必须加上一个中间数。对于8位的E,这个中间数是127;对于11位的E,这个中间数是1023。


例如:2^10,这个E=10,所以保存成32位浮点数时,必须保存成10+127=137,即10001001。

image.png

4.浮点数取出规则

取出的时候注意两点,第一个就是M的值;重要的是第二个,取出时,指数E有三种情况(因为存进去的时候加了一个中间值),这些都是指在内存中的E。下面逐一介绍。


(1)E不全为0不全为1(常规情况)


这种情况比较简单,直接还原就可以。


规则:指数E的计算值减去127(或1023),得到真实值;再将有效数字M前面加上第一位的1。


(2)E全为0


当存在内存中的E为0时,说明存入前指数E为-127(或-1023),表明此浮点数是一个特别小的数字。


规则:此时的指数E=1-127(或者1-1023),即为真实值。有效数字M不再加上第一位的1,而是直接还原成0.xxxxxxx的小数。


目的:为了表示±0,以及趋于0的很小的数字。


(3)E全为1


当内存中的E为全1的时候,说明原值是一个极大的值,趋于无穷。


这时,如果有效数字M全为0,表示±无穷大,正负取决于S。


5.实例与错误案例

(1)代码展示

#include<stdio.h>
int main()
{
  int n = 9;
  float* p = (float*)&n;//将n的地址赋予p
  printf("%d\n",n);
  printf("%f\n", *p);
  *p = 9.0;//通过指针修改n的值
  printf("%d\n", n);
  printf("%f\n", *p);
  return 0;
}

结果展示:

image.png

为什么会有作业的结果呢?这正是由于整数和浮点数的存储方式和取出不一样

(2)结果分析

image.png


相关文章
|
4天前
|
传感器 人工智能 物联网
C 语言在计算机科学中尤其在硬件交互方面占据重要地位。本文探讨了 C 语言与硬件交互的主要方法,包括直接访问硬件寄存器、中断处理、I/O 端口操作、内存映射 I/O 和设备驱动程序开发
C 语言在计算机科学中尤其在硬件交互方面占据重要地位。本文探讨了 C 语言与硬件交互的主要方法,包括直接访问硬件寄存器、中断处理、I/O 端口操作、内存映射 I/O 和设备驱动程序开发,以及面临的挑战和未来趋势,旨在帮助读者深入了解并掌握这些关键技术。
24 6
|
4天前
|
存储 数据建模 程序员
C 语言结构体 —— 数据封装的利器
C语言结构体是一种用户自定义的数据类型,用于将不同类型的数据组合在一起,形成一个整体。它支持数据封装,便于管理和传递复杂数据,是程序设计中的重要工具。
|
10天前
|
存储 C语言
C语言如何使用结构体和指针来操作动态分配的内存
在C语言中,通过定义结构体并使用指向该结构体的指针,可以对动态分配的内存进行操作。首先利用 `malloc` 或 `calloc` 分配内存,然后通过指针访问和修改结构体成员,最后用 `free` 释放内存,实现资源的有效管理。
56 12
|
10天前
|
存储 编译器 数据处理
C 语言结构体与位域:高效数据组织与内存优化
C语言中的结构体与位域是实现高效数据组织和内存优化的重要工具。结构体允许将不同类型的数据组合成一个整体,而位域则进一步允许对结构体成员的位进行精细控制,以节省内存空间。两者结合使用,可在嵌入式系统等资源受限环境中发挥巨大作用。
33 11
|
2天前
|
并行计算 算法 测试技术
C语言因高效灵活被广泛应用于软件开发。本文探讨了优化C语言程序性能的策略,涵盖算法优化、代码结构优化、内存管理优化、编译器优化、数据结构优化、并行计算优化及性能测试与分析七个方面
C语言因高效灵活被广泛应用于软件开发。本文探讨了优化C语言程序性能的策略,涵盖算法优化、代码结构优化、内存管理优化、编译器优化、数据结构优化、并行计算优化及性能测试与分析七个方面,旨在通过综合策略提升程序性能,满足实际需求。
16 1
|
8天前
|
存储 C语言 计算机视觉
在C语言中指针数组和数组指针在动态内存分配中的应用
在C语言中,指针数组和数组指针均可用于动态内存分配。指针数组是数组的每个元素都是指针,可用于指向多个动态分配的内存块;数组指针则指向一个数组,可动态分配和管理大型数据结构。两者结合使用,灵活高效地管理内存。
|
2月前
|
监控 算法 应用服务中间件
“四两拨千斤” —— 1.2MB 数据如何吃掉 10GB 内存
一个特殊请求引发服务器内存用量暴涨进而导致进程 OOM 的惨案。
|
1月前
|
C语言
【c语言】动态内存管理
本文介绍了C语言中的动态内存管理,包括其必要性及相关的四个函数:`malloc`、``calloc``、`realloc`和`free`。`malloc`用于申请内存,`calloc`申请并初始化内存,`realloc`调整内存大小,`free`释放内存。文章还列举了常见的动态内存管理错误,如空指针解引用、越界访问、错误释放等,并提供了示例代码帮助理解。
43 3
|
2月前
|
存储 C语言
数据在内存中的存储方式
本文介绍了计算机中整数和浮点数的存储方式,包括整数的原码、反码、补码,以及浮点数的IEEE754标准存储格式。同时,探讨了大小端字节序的概念及其判断方法,通过实例代码展示了这些概念的实际应用。
64 1
|
2月前
|
存储
共用体在内存中如何存储数据
共用体(Union)在内存中为所有成员分配同一段内存空间,大小等于最大成员所需的空间。这意味着所有成员共享同一块内存,但同一时间只能存储其中一个成员的数据,无法同时保存多个成员的值。