C语言:深度解析各种数据在计算机内存中的存储

2023-11-19

数据的各种类型的存储

char     //字符数据类型
short    //短整型
int     //整形
long     //长整型
long long  //更长的整形
float    //单精度浮点数
double    //双精度浮点数

各种数据类型的意义是什么?

  1. 使用的时候需要在内存中开辟的空间,例如char需要1个字节,short需要2个字节等等
  2. 内存空间的使用

整形在计算机内存中的存储

原码反码补码

计算机中对于整数的存储有三种方法,也就是上述的三种,那么这三种均由符号位数值位组成。

其中,符号位的0表示,符号位的1表示,而数值位的存储对于正数和负数是不同的:
对于正整数来说,原码反码补码都是相同的,而对于负整数来说,保存的规则如下:

原码
直接将二进制按照正负数的形式翻译成二进制就可以了
反码
符号位不变,其他位按位取反即可得到**(1变为0,0变为1)**
补码
反码+1就是补码

我们要知道的是,在计算机内部,数据的存放都是存放的是补码


那么为什么?

为什么数据的存放都是补码?

早在计算机设计之初,聪明的学者就发现了,用补码来进行负数运算是可行的,那么具体怎么实操?下面有一个例子:

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

在这里插入图片描述
这里需要注意的是,在相加的过程中,数值位会溢出一位,计算机内部不会留存这一位


我们假设a为-10,b为-20,那么计算a+b的值是多少,在计算机内部应该怎么计算呢?

计算机内部只知道0和1,于是会把数字转换为补码进行运算,上图表示了a和b的补码,从上图可以看出,用补码进行计算是可行的,事实上,也只能用补码,用原码或补码进行计算都是不可行

我们到调试的内存窗口来看一下,其实会发现其他问题:

int main()
{
	int a = 10;
	int b = 20;
	return 0;
}

在这里插入图片描述
为什么这里表示的时候是0a 00 00 00而不是00 00 00 0a?


既然有这样的疑问,就需要提到的是计算机内部中大小端的概念了

什么是大小端?

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


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


整形提升

什么是整型提升?

通俗来讲,就是表达式中的字符和短整型的操作数在进行计算的时候要被转换为普通整形,这个过程就叫整形提升。

那么整型提升是做什么的?

表达式的整形运算一般是在CPU内进行,而CPU的整形运算器的操作数的字节长度恰好就是int的字节长度,也是CPU的通用寄存器长度。

因此,比如有两个char类型的数据相加,在执行这个加法的操作时也要先转换为CPU上的整形操作数的标准长度,也就是转换成整形数据

如何进行整形提升?

整形提升的原则是什么?
假如我们现在有下面的语句

char c1 = -1;
char c2 = 1;

整形提升的原则是,有符号的数据高位补充符号位,无符号的数据高位补充0


那么,在计算机内部我们都是用补码进行存储的,所以c1的存储是11111111,高位补充符号位,那么实际提升后的结果就是11111111111111111111111111111111 (因为是提升为int类型,所以共32个比特位),对于1,在内存中补码的形式是00000001,那么高位补充符号位的结果就是000000000000000000000001

关于整型提升的例子

//例1
int main()
{
char a = 0xb6;
short b = 0xb600;
int c = 0xb6000000;
if(a==0xb6)
printf("a");
if(b==0xb600)
printf("b");
if(c==0xb6000000)
printf("c");
return 0;
}

在这个例子中,当进行判断语句的时候,实际上就算是进行运算了,那么char类型和short类型在进行运算时就需要进行整形提升,而a的这个十六进制转换后是10110110,对于char类型的数据来说最多只能保存8位,因此就会把1当成符号位,所以整形提升会补1,此时这个数据就变成负数了

在这里插入图片描述


而同理,对于b这个数据来说,它的二进制表示其实是1011011000000000,最高位也是1,也会当成负数处理

在这里插入图片描述
因此,a,b都进行了整形提升,但是c不需要进行整形提升,所以最终输出的结果是c

//例2
int main()
{
char c = 1;
printf("%u\n", sizeof(c));
printf("%u\n", sizeof(+c));
printf("%u\n", sizeof(-c));
return 0;
}

在这个例题中,当进行到±这样的操作符的时候,其实就已经进行了运算,那么此时char类型数据要进行整形提升,所以会提升为int类型,对应的字节数也会变成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;
}

下面程序的输出是多少?

我们定义的时候,定义的是char类型,但打印的时候要求按照%d(十进制的整数)打印,那么这个过程就会发生整型提升
对于a来说,原来在计算机内部存储的是11111111,整形提升看到是有符号数据,那么高位补充符号位,就变成了:

//11111111111111111111111111111111  -> a的补码
//11111111111111111111111111111110  -> a的反码
//1000000000000000000000000001  -> a的原码

而输出的结果就是-1,对于b来说和a是一样的


那么对于c来说就有所差异了,由于c是无符号类型的char,因此在整形提升的时候补的就是0,而由于最高位符号位是0 是一个正数,因此原码反码补码相同,所以最终就是11111111,也就是255,因此输出为-1 -1 255


//例2
#include <stdio.h>
int main()
{
  char a = -128;
  printf("%u\n",a);
  return 0;
}

要得知这个的结果,那么首先看-128的存储形式:

//10000000000000000000000010000000  ->  原码
//11111111111111111111111101111111  ->  反码
//11111111111111111111111110000000  ->  补码

而char类型只能容纳1个字节,也就是8位,所以只能容纳10000000

因此,在内存中存储的就是这个数据

在进行运算时,由于会发生整型提升,高位补充1

//11111111111111111111111110000000

而输出的时候是按照&=%u输出的 (无符号整形),因此对于上面这个数据不包含符号位,因此直接输出上面的数据,输出结果是一个很大的数字:4294967168


//例3
#include <stdio.h>
int main()
{
  char a = 128;
  printf("%u\n",a);
  return 0;
}

与例2相同


//例4
int i= -20;
unsigned  int  j = 10;
printf("%d\n", i+j);

具体分析如下:

#include <stdio.h>
int main()
{
	int i = -20;
	//10000000000000000000000000010100  原码
	//11111111111111111111111111101011  反码
	//11111111111111111111111111101100  补码
	unsigned int j = 10;
	//00000000000000000000000000001010
	//11111111111111111111111111101100  i的补码
	//11111111111111111111111111110110  i+j的补码
	//11111111111111111111111111110101  i+j的反码
	//10000000000000000000000000001010  i+j的原码
	//1010  2 8 
	printf("%d\n", i + j);//-10
	return 0;
}

由此可以看出,计算机内部的运算是用补码进行运算的


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

上面运行结果看似是循环1000次,输出为1000,但实际上结果并非如此,运行结果是255,原因是什么?

在解决问题前,首先明确signed char类型的表示范围:

-128~127

char在内存中占1个字节,而1个字节能拥有8个比特位,也就是说char可以有00000000~11111111,但从头到尾的表示并不是一直递增的,当符号位为1的时候就为负数而11111111再加上1就会回到00000000,因此整体形成了一个闭环,那么由此就可以画出下面的图:
在这里插入图片描述
那么知道了signed char的表示方法,实际上unsigned char就显得更简单一些,表示的范围是

0~255

在这里插入图片描述

于是,知道了这个原理后再回到上面的问题,由题意,是想把-1,-2,-3…-1000存储到数组中,而由于char类型的存储范围是有限的,当存储到**-128后继续存储就变成127**,然后继续递减,直到0
但这并没有解决为什么输出是255的原因,原因在于strlen函数结束的标志是 \0,而 \0对应的ASCII码值恰好为1,而又由于char类型实际上是用int类型存储的,因此当strlen函数访问到数组中存储的0时,它就会认为这是所谓的\0,这是字符串结束的标志,于是就会输出255


//例6
#include <stdio.h>
unsigned char i = 0;
int main()
{
  for(i = 0;i<=255;i++)
 {
    printf("hello world\n");
 }
  return 0;
}

这个就相对简单一些,由于unsigned char的存储范围是0~255,当存储到255再加下去就又会回到0继续循环,于是会死循环输出hello world

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

C语言:深度解析各种数据在计算机内存中的存储 的相关文章

随机推荐

  • 新仙魔九界研发及设计分析

    玩法本质属于捕鱼RPG玩法 美术风格属于国风 非常有特色 目前整体的玩法 性能优化和体验各方面都做的不错 从第三方数据平台获知该游戏目前月流水在600万 且有未来还有巨大的增长空间 这也不是波克城市第一次做出尝试 早在2015年就尝试过一个
  • druid 解析select查询sql获取表名,字段名,where条件

    解析select sql生成QueryModelInfo param dbTypeName mysql oracle param selectSql return public static QueryModelInfo parse Str
  • Vue将页面中Echarts动态图导出Gif动图

    Vue将页面中Echarts动态图导出Gif动图 首先 准备工作需要三个依赖的js 第一个js采用npm进行安装 npm install html2canvas 第二个js采用下载放入项目中进行使用 下载链接 https pan baidu
  • $refs用法

    refs获取dom元素 今天我们主要说一下几点 1 vue 获取普通元素 基础 2 vue 获取列表 基础 一 vue获取dom节点 普通元素 我们之前获取页面上的dom元素 很容易 比如原生js的 document getElements
  • Angular实现发送请求下载文件

    Angular实现下载文件到本地 第一步 获取下载地址 获取下载地址 download let param 参数 json形式 this download param subscribe res any gt if res success
  • 【无标题】AttributeError: ‘TransposedFont‘ object has no attribute ‘getbbox‘

    Python 运行 Wordcloud的时候出现了这个错误 from wordcloud import WordCloud from PIL import Image import matplotlib pyplot as plt topi
  • 3D游戏编程与设计作业——血条预制

    一 作业要求 血条 Health Bar 的预制设计 具体要求如下 分别使用 IMGUI 和 UGUI 实现 使用 UGUI 血条是游戏对象的一个子元素 任何时候需要面对主摄像机 分析两种实现的优缺点 给出预制的使用方法 二 实现效果 血条
  • ROS自学实践(10):ROS节点同时订阅多个话题并进行消息时间同步

    一 前言 在进行SLAM建图或自动驾驶系统设计的过程中 往往涉及到多种传感器进行环境感知和信息采集 这就不仅需要一个节点接收多个传感器数据 还要求传感器数据的时间戳同步 这样才能实现环境数据的实时感知和处理 本文基于ROS操作系统 从C 和
  • Android 5.x 权限问题解决方法

    0 关于selinux的基础知识 可以参见http www cnblogs com shell812 p 6379321 html TE语言规则 参见http www cnblogs com shell812 p 6379370 html
  • npx报错“Path must be a string. Received undefined”in windows解决方法

    在使用Windows上使用较老版本的nodejs 如何我使用的v8 9其自带的npx的版本为9 7 在Windows上使用会存在 Path must be a string Received undefined 的错误 通过 GitHub
  • QT程序运行时依赖设置

    本文主要解决Qt程序运行时容易遇到的两个问题 找不到相关的Qt6Core dll Qt6Widgets dll等库 这种情况下 把相关DLL所在目录添加到系统的环境变量 PATH 中 就可以了 无法导入Qt相关的插件 qt qpa plug
  • PCL调错:(3)error C2589“(“:“::“右边的非法标记

    错误提示 造成该错误的原因是 pcl库中的函数模板max与Visual C 中的全局的宏max冲突 百度了以下看了一共有两种解决方案 第一种不用修改库里边的源代码 设置项目属性 在预定义处理器中添加定义NOMINMAX来禁止使用Visual
  • Android 旧项目引入 (kotlin)插件简单记录

    1 确认自己 AS 的kotlin 插件 已经安装 2 选择kotlin plugin updates 文件显示 3 选择configure kotlin in project 进入选择 Android gradle 的选项 由于我这边配置
  • JAVA中数组冒泡排序和选择排序

    冒泡排序的思想 两两之间比较大小 小的数在前 大的数在后 共比较i 1次 static void MaoPaoArray int a for int i 0 i lt a length 2 i for int j 0 j lt a leng
  • tortoisegit 常见错误disconnected no supported authentication methods available(server sent: publickey)

    本文转载自 https blog csdn net yym6789 article details 53807640 1 安装好小乌龟git后 用小乌龟的pull 从github上拉取项目 遇到错误 disconnected no supp
  • 7 整数反转 c++

    leetcode7 整数反转 题目描述 给出一个 32 位的有符号整数 你需要将这个整数中每位上的数字进行反转 注意 如果反转后整数溢出那么就返回 0 算法思路 使用一个64位的long long类型来存储结果整数 避免反转后结果溢出报错
  • C++ 装饰器模式

    什么是装饰器模式 装饰器模式是一种结构型设计模式 实现了在不改变现有对象结构的的同时又拓展了新的功能 装饰器本质上是对现有对象的重新包装 同时装饰器又称为封装器 如何理解装饰器模式 以笔记本电脑为例 当我们购买了一台新笔记本电脑 但我们发现
  • 在openwrt使用C语言增加ubus接口(包含C uci操作)

    在openwrt使用C语言增加ubus接口 包含C uci操作 文章目录 在openwrt使用C语言增加ubus接口 包含C uci操作 创建自己的软件包 软件包结构 编写代码和启动脚本 重点 案例大致分析 实现过程 ubus demo i
  • java调用脚本语言笔记(jython,jruby,groovy)

    java调用脚本语言笔记 jython jruby groovy 有两种方法 1 java se 6以后实现了jsr 223规范 java代码 java view plain copy ScriptEngineManager factory
  • C语言:深度解析各种数据在计算机内存中的存储

    文章目录 数据的各种类型的存储 各种数据类型的意义是什么 整形在计算机内存中的存储 原码反码补码 为什么数据的存放都是补码 什么是大小端 整形提升 什么是整型提升 那么整型提升是做什么的 如何进行整形提升 关于整型提升的例子 关于整形数据存