C语言数据在内存中的存储

1.整数在内存中的存储

我们知道数据在内存中都是以2进制的形式存储的;比如int,char,double,float这些类型的数据都是以2进制的形式去存储的,那么这些数据又是如何去存入/取出的呢?

前面我们知道,整数分为有符号整数和无符号整数;而整数在内存中存储有三种表示方式,分别是原码,反码和补码;三种表示方式均分为符号位和数值位;

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

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

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

对于有符号的整数来说原码,反码和补码是要进行计算的,而对于无符号的整数来说原码,反码和补码来说都相同;下面我们来一一介绍:

有符号整数

对于有符号整数来说,分为正数和负数;

正数的原码,反码,补码相同;

负数的原码,反码和补码要进行计算;

举例说明:

对于有符号整数来说最高位是符号位,0表示正数,1表示负数;后面31位表示数值位也就是有效位;

无符号整数

无符号整数的32个比特位都表示有效位;

举例说明:

2.大小端字节序

我们在观察内存的时候,不难发现我们观察到的数据储存是倒着存的;

比如:

我们发现是先存44 33 22 11;这种倒着存放数据的也就是我们的小端字节序存储;

什么是大端,什么又是小端呢?

大端存储:是指数据的低位字节内容保存在内存的⾼地址处,⽽数据的⾼位字节内容,保存在内存的低地址处。

小端存储:是指数据的低位字节内容保存在内存的低地址处,⽽数据的⾼位字节内容,保存
在内存的⾼地址处。

这个不难理解,

练习1

判断当前机器上大端还是小端存储;

可以用指针来,也可以用联合体来表示,联合体的方法可以看前面的联合体介绍;

cpp 复制代码
int main()
{
	int a = 1;
	char* pa = (char*)& a;
	if (*pa)
	{
		printf("小端\n");
	}
	else
	{
		printf("大端\n");
	}
	return 0;
}

练习2

cpp 复制代码
#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或者short以int类型去打印的话,我们是不足32个比特位的(char 8bit)(short 16bit),那么我们就要补齐剩下的位,那么这里也分2种情况;

如果是无符号整形的话,直接补0即可;

如果是有符号整形,按自己的符号位往上补;也就是我们上面的那种情况;

接下来我们继续:

练习3

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

题目解析:

练习4

cpp 复制代码
#include <stdio.h>
int main()
{
 char a = 128;
 printf("%u\n",a);
 return 0;
}

题目解析:

我们发现不管是有符号的char,还是无符号的char这貌似是一个循环;

所以这一题可以显而易见的看出我们存进去的依旧是-128;

那么打印的结果就是跟上一题一样;

练习5

cpp 复制代码
#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;
}

题目解析:

练习6

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

题目解析:

3.浮点数在内存中的存储

首先我们先来看一段代码:

cpp 复制代码
#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可以表⽰成下⾯的形式:
V = (−1) ^s∗ M ∗ 2^E
• (−1)S 表⽰符号位,当S=0,V为正数;当S=1,V为负数

• M 表⽰有效数字,M是⼤于等于1,⼩于2的

• 2^E 表⽰指数位

如果我们要把它写成上面那种形式的话就是:


IEEE 754规定:
对于32位的浮点数,最⾼的1位存储符号位S,接着的8位存储指数E,剩下的23位存储有效数字M(float)
对于64位的浮点数,最⾼的1位存储符号位S,接着的11位存储指数E,剩下的52位存储有效数字M(double)


1.浮点数存的过程

前面我们知道,M是大于等于1,小于2的,通常写成1.xxxxxxxx,所以再存浮点数的有效数字M的时候不会去存小数点前面的1,只会存小数点后面的数,等取出来的时候再放个1放前面;

对于指数E来说,它是一个无符号的整数,但是指数E是可能为负数的,比如0.5,此时指数E为-1;所以IEEE 754规定,在存入E的时候加上一个中间数,如果是4位浮点数的话就加上127,如果是8位浮点数的话就加上1023;。⽐如,2^10的E是10,所以保存成32位浮点数时,必须保存成10+127=137,即10001001。

我们举例说明:

2.浮点数取的过程

S和M取出的方式比较简单,主要E情况特殊,有三种情况:

1.E不为全0或不为全1

这种情况是属于正常的情况,取出来的时候直接减去127(1023)即可;

⽐如:0.5 的⼆进制形式为0.1,由于规定正数部分必须为1,即将⼩数点右移1位,则为1.0*2^(-1),其阶码为-1+127(中间值)=126,表⽰为01111110,⽽尾数1.0去掉整数部分为0,补⻬0到23位00000000000000000000000,则其⼆进制表⽰形式为:

cpp 复制代码
0 01111110 00000000000000000000000

2.E为全0

这种情况比较特殊,我们加上了127(1023)最后E还是为全0;那只有可能E为-127(-1023),说明这是一个无限接近于0的数字;此时就不能直接减去中间数了,这个时候1-127(1-1023)即为真实值;有效数字也不在加上一,而是加上0;这样做是为了表⽰±0,以及接近于0的很⼩的数字;

3.E为全1

这时,如果有效数字M全为0,表⽰±⽆穷⼤(正负取决于符号位s);

cpp 复制代码
0 11111111 00010000000000000000000

题目解析:

下面我们来看一下最开始的那一道题目;

相关推荐
炸膛坦客16 小时前
单片机/C/C++八股:(二十六)IIC 专题(I²C)---- 上集
c语言·c++·单片机
灯澜忆梦16 小时前
GO_并发编程---定时器
开发语言·后端·golang
-银雾鸢尾-17 小时前
C#中的StringBuilder相关方法
开发语言·c#
-银雾鸢尾-17 小时前
C#中结构体与类的区别;抽象类与接口的区别
开发语言·c#
大模型码小白18 小时前
【Python零基础教程】继承、多态与魔法函数:面向对象编程三大核心特性详解
java·大数据·开发语言·人工智能·python·ai编程
段一凡-华北理工大学20 小时前
向量数据库实战:选型、调优与落地~系列文章12:文本分块策略实战:chunk_size 怎么选?重叠多少?
开发语言·数据库·后端·oracle·rust·工业智能体·高炉智能化
Ljwuhe20 小时前
C++——多态
开发语言·c++
心平气和量大福大21 小时前
C#-WPF-Window主窗体
开发语言·c#·wpf
从零开始的代码生活_1 天前
C++ 继承详解:访问控制、对象模型、菱形继承与设计取舍
开发语言·c++·后端·学习·算法
云小逸1 天前
【C++ 第七阶段:模板、泛型编程与工程综合详解】
开发语言·c++