IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> C++知识库 -> C语言——数据的储存 -> 正文阅读

[C++知识库]C语言——数据的储存

前言

C语言中有4种基本数据类型——整形、浮点型、指针和聚合类型(如数组和结构等)。所有其他类型都是从这4种基本类型的某种组合派生而来的。其中整形和浮点型使用的最多。本文将简要讲解整形和浮点型是怎样在内存储存的。

整形在内存的储存

计算机中整形有三种表示方法,即原码,反码和补码,三种形式都由符号位和数值位两边部分组成。最高位是符位,0代表正,1代表负

原码、反码、补码之间的关系

正数的原码反码补码相同,负数的原码反码补码按如下规则计算
原码:直接将十进制数翻译为二进制,最高位放符号位
反码:原码符号位不变,其他位按位取反
补码:反码+1

为什么存在原码、反码、补码

存在原码反码补码的概念,其实是为了引进补码
在计算机系统中,数值一律用补码来表示和存储。原因在于,

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

如何理解第一点:
比如我要计算1-1=0

int a=1;
int b=-1;
int c=a+b;

1的原码:00000000 00000000 00000000 00000001
-1的原码:10000000 00000000 00000000 00000001
两者相加得到:10000000 00000000 00000000 00000010代表-2,显然错误
但是如果我们使用补码
1的反码:00000000 00000000 00000000 00000001
1的补码:00000000 00000000 00000000 00000001
-1的反码:11111111 11111111 11111111 11111110
-1的补码:11111111 11111111 11111111 11111111
两者补码相加得到:1 00000000 00000000 00000000 00000000
而整形和整形相加的结果将被处理为整形,所以只截取后8位,而这就等于我们想要的结果0
对于其他例子也是同样的原理

如何理解第二点:
从原码反码补码的关系来看我们会用补码-1再符号位不变其他位按位取反得到原码,但实际上,对补码按位取反再+1就能得到原码,这个操作和从原码到补码的操作是相同的
比如-1
-1的原码:10000000 00000000 00000000 00000001
-1的反码:11111111 11111111 11111111 11111110
-1的补码:11111111 11111111 11111111 11111111
补码按位取反:100000000 00000000 00000000 00000000
再+1:10000000 00000000 00000000 00000001
而这就是-1的原码!

大小端介绍

当我们打开vs调试的内存窗口的时候我们会发现类似下面的情况
在这里插入图片描述
数据高位的内容竟然放在内存的低位上,看起来数据再内存里就像是倒着存放的,其实这里涉及到编译器大小端模式的概念

什么是大小端

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

为什么存在大小端

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

当前机器采用大端模式还是小端模式其实可以通过一个简单的程序判断

百度2015年系统工程师笔试题: 请简述大端字节序和小端字节序的概念,设计一个小程序来判断当前机器的字节序。(10分)

在这里插入图片描述

#include<stdio.h>
//int check_sys()
//{
//	int a = 1;
//	char* p = (char*)&a;
//	if (1 == *p)
//		return 1;
//	else
//		return 0;
//}
//改进1
//int check_sys()
//{
//	int a = 1;
//	char* p = (char*)&a;
//	return *p;
//}
//改进2
int check_sys()
{
	int a = 1;
	return *(char*)&a;
}
int main()
{
	if (1 == check_sys())
		printf("小端\n");
	else
		printf("大端\n");

	return 0;
}

关于整形储存的一些例题

例一

#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;
}

在这里插入图片描述
解析:
由于是在32位环境下,-1的
原码:10000000 00000000 00000000 00000001
反码:11111111 11111111 11111111 11111110
补码:11111111 11111111 11111111 11111111
而char类型是8bit的,保存到变量里的时候只保存低8位,于是就是11111111
%d要求以有符号数整数形式打印,打印前需要先整形提升,由于a和b都是有符号数,整形提升高位补符号位,都是
11111111 11111111 11111111 11111111
转换成原码打印就是-1
而c是无符号数,整型提升高位补0,得到
00000000 00000000 00000000 11111111
最高位是0,%d认为这是个正数,原反补码相同,直接转为10进制就是打印出来的255

例二

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

在这里插入图片描述
先分析a和b中存的是什么
128的
原码:00000000 0000000 00000000 10000000
反码:同上
补码:同上
所以a存的是10000000
-128的
原码:10000000 00000000 00000000 10000000
反码:11111111 11111111 11111111 01111111
补码:11111111 11111111 11111111 10000000
所以b存的是10000000
%u要求以无符号整数形式打印,打印前需要整形提升,由于a和b都是有符号数,整型提升后均为
11111111 11111111 11111111 10000000
%u把上面这个二进制数看成无符号数直接转成10进制打印,就得到了屏幕上的结果

那为什么a以%d形式打印的结果是-128呢?这同样是由于本应是数值位的1占了符号位,整形提升后,导致它变成了一个负数。a整形提升的结果是
11111111 11111111 11111111 10000000,
这是补码,打印要以原码的形式打印,%d以有符号数的视角看上面这个数,先-1在按位取反得到
100000000 00000000 00000000 10000000
而这就是-128

例三

#include<stdio.h>
int main()
{
	int i = -20;
	unsigned int j = 10;
	if ((i + j) > 0)
	{
		printf("a\n");
	}
	else
	{
		printf("b\n");
	}
	printf("%d\n", i + j);
	printf("%u\n", i + j);
	return 0;
}

在这里插入图片描述
解析
-20的
原码:10000000 00000000 00000000 00010100
反码:11111111 11111111 11111111 11101011
补码:11111111 11111111 11111111 11101100
10的
原码:00000000 00000000 00000000 00001010
反码:同上
补码:同上
i+j补码相加得到
11111111 11111111 11111111 11110110
而我们注意到i是int型的,j是unsigned型的,执行i+j前会对i先进行算术转换成unsigned int型,结果也被视为unsigned int型,那么
11111111 11111111 11111111 11110110
将被视为一个大于零且相当大的数,故打印了字符a,
而这个数恰好就是4,294,967,286
在这里插入图片描述
当我们以%d形式打印i+j时,最高位1被视为符号位,要先转换位原码再打印结果就是-10
我们可以这样理解,数据类型只是决定看待内存中数据的视角,不影响我存入值的操作
例四

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

死循环
解析
i的数据类型是unsigned int,无论如何表达式i>=0都为真。我们不妨看下i的变化(利用Sleep函数):先从9到0,0-1,i中存的值从
00000000 00000000 00000000 00000000
变成
11111111 11111111 11111111 11111111
上面这个值被解析成无符号数
在这里插入图片描述

例五

#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;
}

在这里插入图片描述
解析
注意’\0’的ASCII码值为0
我们知道char类型能保存的值的范围是-128到127,那么假如对一个char变量一直+1,它的值怎么变化呢?
在这里插入图片描述
可见a的值将会从0一直变到127,从127直接跳到-128,再从-128变到0,形成了一个闭环
在这里插入图片描述

例六

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

在这里插入图片描述
死循环,原因如上图,只要把例五种的那个原上的补码按照无符号数解析即可,i从0到255到0形成循环。

浮点型在内存的存储

关于浮点型存储的一个例题

可能不少初学C语言的同学打印浮点数的时候把转换说明写成了%d,导致结果是0.00000,很久也找不出程序哪错了,这里将解决你的疑惑。

#include<stdio.h>
int main()
{
	int n = 9;
	float* pFloat = (float*)&n;
	printf("n的值为:%d\n", n);
	printf("*pFloat的值为:%f\n", *pFloat);
	*pFloat = 9.0;
	printf("num的值为:%d\n", n);
	printf("*pFloat的值为:%f\n", *pFloat);
	return 0;
}

在这里插入图片描述
要理解这个程序的结果,我们先要了解浮点数再内存是怎样保存的
根据国际标准IEEE(电气和电子工程协会) 754,任意一个二进制浮点数V可以表示成下面的形式:

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

举例来说:
十进制的5.0,写成二进制是 101.0 ,相当于 1.01×2^2 。
那么,按照上面V的格式,可以得出s=0,M=1.01,E=2。
十进制的-5.0,写成二进制是 -101.0 ,相当于 -1.01×2^2 。那么,s=1,M=1.01,E=2。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在上面的程序种整形9在内存中为
00000000 00000000 00000000 00001001
%f以浮点数的方式解析这个值,由于阶码E=0,浮点数的指数直接取1-127即-126,并且精度部分不再+1,以表示一个非常接近0的数,
V=(-1)^0 × 0.00000000000000000001001×2^(-126)=1.001×2^(-146)
就得到了打印的0.000000

再看把浮点数9.0以整形形式打印,先看9.0是如何储存的

(-1)^0*1.001*2^3//S=0,E=3+127=130,M=1.001

在内存中为
0 10000010 001 0000 0000 0000 0000 0000
以有符号十进制整数解析就得到
1,091,567,616

  C++知识库 最新文章
【C++】友元、嵌套类、异常、RTTI、类型转换
通讯录的思路与实现(C语言)
C++PrimerPlus 第七章 函数-C++的编程模块(
Problem C: 算法9-9~9-12:平衡二叉树的基本
MSVC C++ UTF-8编程
C++进阶 多态原理
简单string类c++实现
我的年度总结
【C语言】以深厚地基筑伟岸高楼-基础篇(六
c语言常见错误合集
上一篇文章      下一篇文章      查看所有文章
加:2021-08-11 11:59:07  更:2021-08-11 11:59:13 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/18 18:36:02-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码