深度剖析数据在计算机内存中的存储

编程入门 行业动态 更新时间:2024-10-10 15:28:37

<a href=https://www.elefans.com/category/jswz/34/1769690.html style=深度剖析数据在计算机内存中的存储"/>

深度剖析数据在计算机内存中的存储

本章重点

1.计算机中数据类型详细介绍 2.整形在内存中的存储方式 3.大小端字节序介绍以及判断方法

1. 数据类型详细介绍

C语言中我们学习的内置类型数据有以下几种。

类型数据类型名称占用内存空间字节
char 字符数据类型 1
short 短整型 2
int 整形 4
long 长整型 4
long long 更长的整形 8
float 单精度浮点数 4
double 双精度浮点数 8

类型的意义:使用这个类型开辟内存空间的大小(大小决定了使用范围)。

在VS2019下观察内置类型数据的大小

#include<stdio.h>
#include<limits.h>int main()
{INT_MAX;return 0;
}

limist这个库函数可以查看内置类型数据最大值与最小值

右键点击然后转到定义

通过转到定义可以清晰的查看内置数据的最大值与最小值

整形家族分类

charsigned charunsigned char
shor unsigned short signed short [int]
int unsigned int signed int
long unsigned long [int] signed long [int]

为什么char是属于整形家族的呢?因为char类型在内存中存储的是ASCII码值,是整形,因此归类为整形家族。

对整形家族具有无符号有符号的区分 那么char是unsigned char 还是signed char呢?

这是不确定的,但在VS2019上 char=signed char  short=signed short   int=signed int

2. 整形在内存中的存储:原码、反码、补码

一个变量在内存中存储是需要开辟空间的,而开辟的空间大小取决于变量的数据类型。

#include<stdio.h>
#include<limits.h>int main()
{int a = -10;10000000 00000000 00000000 00001010--源码11111111 11111111 11111111 11110101--反码11111111 11111111 11111111 11110110——补码int b = 20;00000000 00000000 00000000 00010100——源码==反码==补码return 0;
}

int开辟4个字节空间大小,4个字节=32个比特位(byte)。

而计算机中表达二进制数有三种方法 源码  反码 补码

三种表达方法均有符号位数值位两部分组成  0表示正 1表示负  而正数的源反补都相同

以下是8个比特位的内存空间所放二进制数值的所有可能以及转换。

有符号:signed 转换图

无符号:unsigned 转换图

则负数的源 反 补表达方式不同

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

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

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

补码到源码可以有两种方式获取

先取反+1 or 先-1取反

3. 大小端字节序介绍及判断

对于整形来说: 数据存放内存中其实存放的是补码。  为什么呢? 在计算机系统中,数值一律用补码来表示和存储。原因在于,使用补码,可以将符号位和数值域统 一处理; 同时,加法和减法也可以统一处理(CPU 只有加法器 )此外,补码与原码相互转换,其运算过程 是相同的,不需要额外的硬件电路。 通过调试查看内存存储空间
#include<stdio.h>
#include<limits.h>int main()
{int a = -10;10000000 00000000 00000000 00001010--源码11111111 11111111 11111111 11110101--反码11111111 11111111 11111111 11110110——补码ff ff ff f6int b = 20;00000000 00000000 00000000 00010100——源码==反码==补码00 00 00 14return 0;
}
a的地址 b的地址 a的补码是ff ff ff f6  b的补码是00 00 00 14  内存中存储的值是十六进制 通过调试发现内存存储的顺序是反的?这又是为什么呢? 这时候就要介绍大小端了。 大端(存储)模式,是指数据的低位保存在内存的高地址中,而数据的高位,保存在内存的低地址 中; 小端(存储)模式,是指数据的低位保存在内存的低地址中,而数据的高位,,保存在内存的高地 址中。 FF FF FF F6  万 千  百  个 位 00 00 00 14 万 千  百  个 位 为什么会有大小端? 为什么会有大小端模式之分呢? 这因为在计算机系统中,我们是以字节为单位的,每个地址单元都对应着一个字节,一个字节为8 bit。但是在C语言中除了8 bit的char之外,还有16 bit的short型,32 bit的long型 (要看具体的编译器),另外,对于位数大于8位的处理器,例如16位或者32位的处理器,由于寄存器宽度大于一个字节,那么必然存在着一个如何将多个字节安排的问题。因此就导致了大端存储模式和小端存储模式
例如:一个16bit的 short型x,在内存中的地址为 0x0010,x的值为 0x1122,那么0x11为高字节,0x22为低字节。对于大端模式,就将 0x11 放在低地址中,即 0x0010 中,0x22 放在高地址中,即 0x0011中。小端模式,刚好相反。我们常用的X86 结构是小端模式,而 KEIL C51则为大端模式。很多的ARM,DSP都为小端模式。有些ARM处理器还可以由硬件来选择是大端模式还是小端模式。

怎么判断机器字节序是大端还是小端呢?

#include<stdio.h>
int check_key()
{int n = 1;00000000 00000000 00000000 0000000100  00 00 01 小端字节 01 00 00 00 大端字节 00 00 00 01char b = *(char*)&n;return b;
}
int main()
{int ret = check_key();if (ret == 1){printf("小端\n");}else{printf("大端\n");}return 0;
}

我们先强制类型转换成char类型再取n的地址,再解引用访问地址,如果是1就是小端 否则大端。

4.代码习题

1.第一道习题

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

答案是 -1 -1 255    为什么呢?

#include <stdio.h>
int main()
{char a = -1;10000000 00000000 00000000 00000001——源码11111111 11111111 11111111 11111110——反码11111111 11111111 11111111 11111111——补码 ——截断11111111 -a整形提升11111111 11111111 11111111 11111111 ——补码10000000 00000000 00000000 00000000 ——取反10000000 00000000 00000000 00000001 ——源码signed char b = -1;10000000 00000000 00000000 00000001——源码11111111 11111111 11111111 11111110——反码11111111 11111111 11111111 11111111——补码 ——截断整形提升11111111 11111111 11111111 11111111 ——补码10000000 00000000 00000000 00000000 ——取反10000000 00000000 00000000 00000001 ——源码unsigned char c = -1;10000000 00000000 00000000 00000001——源码11111111 11111111 11111111 11111110——反码11111111 11111111 11111111 11111111——补码 ——截断整形提升1111111100000000 00000000 00000000 11111111 ——正数 源反补相同printf("a=%d,b=%d,c=%d", a, b, c);%d打印的是十进制数字 输出的是源码的值-1 -1 255return 0;
}

为什么会整形提升和截断呢? C 的整型算术运算总是至少以缺省整型类型的精度来进行的。 为了获得这个精度,表达式中的字符和短整型操作数在使用之前被转换为普通整型 这种转换称为整型 提升 。 整型提升的意义:
表达式的整型运算要在CPU的相应运算器件内执行,CPU内整型运算器(ALU)的操作数的字节长度一般就是int的字节长度,同时也是CPU的通用寄存器的长度。
因此,即使两个char类型的相加,在CPU执行时实际上也要先转换为CPU内整型操作数的标准长度。
通用CPU (general-purpose CPU) 是难以直接实现两个8比特字节直接相加运算 (虽然机器指令中可能有这种字节相加指令)。所以,表达式中各种长度可能小于int长度的整型值,都必须先转换为int或unsigned int,然后才能送入CPU去执行运算。 负数char的整形提升:一般高位补充符号位 无符号char的整形提升:一般高位补充0
char a = -1;
1000000 0000000 0000000 00000001 ——源码
1111111 1111111 1111111 11111110 ——反码
1111111 1111111 1111111 11111111 ——补码 因为char只有一个字节大小进行截断11111111 -a 截断
整形提升
1111111 1111111 1111111 11111111unsigned char a = -1;
1000000 0000000 0000000 00000001 ——源码
1111111 1111111 1111111 11111110 ——反码
1111111 1111111 1111111 11111111 ——补码 因为char只有一个字节大小进行截断11111111 -a 截断
整形提升
00000000 00000000 00000000 11111111 为正数 源反码相同

2.第二道习题

输出什么?
#include <stdio.h>
int main()
{char a = -128;printf("%u\n",a);return 0;
}

答案输出4,294,967,168 为什么呢?

因为是无符号数 没有符号位 全是数值位

不相信的同学 可以win+R 输出calc 弹出计算器切换到程序员

把整形提升后的二进制序列输入进去 就可以看到结果了。

#include <stdio.h>
int main()
{char a = -128;1000000 0000000 0000000 10000000 ——源码1111111 1111111 1111111 01111111 ——反码1111111 1111111 1111111 10000000 ——补码 截断10000000 -a整形提升11111111 11111111 11111111 10000000printf("%u\n", a);4,294,967,168return 0;
}

3..第三道习题

这个输出什么?
#include <stdio.h>
int main()
{char a = 128;printf("%u\n",a);return 0;
}

 答案还是4,294,967,168

#include <stdio.h>
int main()
{char a = 128;00000000 00000000 00000000 10000000——正数 源反码相同截断10000000整形提升11111111 11111111 11111111 10000000printf("%u\n", a);return 0;
}

原理与上题相同 

4.第四道习题

输出什么?
#include<stdio.h>
int main()
{
int i= -20;
unsigned int j = 10;
printf("%d\n", i+j); }

答案是-10

#include <stdio.h>
int main()
{int i = -20;1000000 00000000 00000000 00010100 ——源码11111111 11111111 11111111 11101011 ——反码11111111 11111111 11111111 11101100 ——补码unsigned int j = 10;00000000 00000000 00000000  00001010 ——正数 源反码相同相加补码11111111 11111111 11111111 11101100 -i00000000 00000000 00000000 00001010 -j11111111 11111111 11111111 11110110 补码相加结果10000000 00000000 00000000 0000100110000000 00000000 00000000 00001010 ——源码 -10printf("%d\n", i + j);-10return 0;}

5.第五道习题

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

答案是无限循环!为什么呢?因为从上面的转换图可以看出,无符号数是没有符号位的,当i--到0的时候,再-1 会变成全11111111 11111111 11111111 11111111 因为无符号数没有符号位全是数值位,会一直减下去 变成0 又从0开始循环 所以是一个死循环。

我们可以通过Sleep函数观察下此代码

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

6.第六道习题

输出什么?
#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;
}

答案是255。为什么呢?

从数学的角度来讲 代码理论应该是这样的 如下图

a[i]会从-1一直减到-1000 但实际上不是这样的。

char的范围是-128~127

当从-1减到-128时 再减去1 会变成127 126 ....到0

strlen是求字符串长度的,统计的是\0之前的出现的字符个数,而\0等同于0

-1到-128 是128个数字 127到0 一共是128个数字 但0==\0    所以是128+127=255

因此strlen只统计0之前出现的数字字符个数 因此输出结果是255。

更多推荐

深度剖析数据在计算机内存中的存储

本文发布于:2023-12-07 19:34:29,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1671649.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:深度   内存   计算机   数据

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!