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

主页链接: LSR的主页

专栏链接: 《C语言》

文章目录

一、大小端字节序

1. 大小端的定义

大小端字节序(Endianness)是计算机系统中多字节数据在内存中存储的两种不同方式。主要区别在于字节的排列顺序。

大端字节序(Big-Endian):高位字节存储在低地址,低位字节存储在高地址。类似于人类阅读顺序,从左到右由高到低。

小端字节序(Little-Endian):低位字节存储在低地址,高位字节存储在高地址。与人类阅读顺序相反,从右到左由低到高。

记忆口诀:匹配即小端,不匹配即大端。

c 复制代码
#include <stdio.h>

int main() {
    int num = 1;
    char *ptr = (char *)&num;
    if (*ptr == 1) {
        printf("Little-Endian\n");
    } else {
        printf("Big-Endian\n");
    }
    return 0;
}

2. 为什么会有大小端之分

2.1硬件设计的多样性

不同厂商的处理器(如Intel和ARM)在设计时选择了不同的存储方式:

大端模式(Big-Endian):数据的高位字节存储在低地址。

例如:数值 0x12345678 在内存中的存储顺序为:

12 34 56 78(地址递增方向)。

小端模式(Little-Endian):数据的低位字节存储在低地址。

例如:同一数值存储为:

78 56 34 12(地址递增方向)。

2.2历史与兼容性

大端模式早期被IBM、Motorola等厂商采用,符合人类读写习惯(从左到右高位到低位)。

小端模式由Intel x86架构推广,因低位字节先处理,更适合算术运算(如加法需从低位开始)。

总结:

大小端差异本质是硬件设计自由度的体现,需通过软件或协议(如TCP/IP)解决兼容性问题。


二、整数在内存中的存储

正整数的原、反、补码都相同。

负整数的三种表⽰⽅法各不相同。

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

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

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

对于整形来说:数据存放内存中其实存放的是补码,优势就是可以进行数之间的简单运算。


三、浮点数在内存中的存储

常见的浮点数:3.14159、1E10等,浮点数家族包括: float、double、long double 类型。

浮点数表⽰的范围: float.h 中定义

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

⼗进制的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。

重点在于S,M,E的具体值。

32位:

64位:

1. 存的过程

IEEE 754 对有效数字M和指数E,还有⼀些特别规定。

前面说过, 1≤M<2 ,也就是说,M可以写成 1.xxxxxx 的形式,其中 xxxxxx 表示小数部分。

IEEE 754 规定,在计算机内部保存M时,默认这个数的第⼀位总是1,因此可以被舍去,只保存后⾯的

xxxxxx部分。比如保存1.01的时候,只保存01,等到读取的时候,再把第⼀位的1加上去。这样做的目的,是节省1位有效数字。以32位浮点数为例,留给M只有23位,将第⼀位的1舍去以后,等于可以保

存24位有效数字。
至于指数E,情况就比较复杂

E位数 偏置值 取值范围

8 127 0~255

11 1023 0~2047

E以无符号整数形式 存储:8位E取值范围0~255,11位E取值范围0 ~2047

由于科学计数法中E可能为负值,IEEE 754规定存储时需加上偏置值:
8位E加127
11位E加1023

例如:2^10的E=10,存储为32位浮点数时实际存储值为10+127=137(二进制10001001)


2.取的过程

2.1 E有0有1

浮点数的表示遵循以下规则:首先将指数E的计算值减去127(或1023)得到真实值,然后在有效数字M前补上最高位的1。

以0.5为例,其二进制形式为0.1。根据规定,整数部分必须为1,因此将小数点右移1位得到1.0×2^(-1)。此时阶码为-1+127(中间值)=126,对应的二进制表示为01111110。尾数1.0去掉整数部分后为0,补0至23位得到00000000000000000000000。最终其二进制表示形式为:
0 01111110 00000000000000000000000


2.2 E全为0

此时,浮点数的指数E取值为1-127(或1-1023)表示真实值,有效数字M不再需要补上前导的1,而是直接采用0.xxxxxx的小数形式。这种设计可以准确表示±0以及极接近0的微小数值。
0 00000000 00100000000000000000000


2.3 E全为1

这时,如果有效数字M全为0,表示±无穷大(正负取决于符号位s)
0 11111111 00100000000000000000000


3. 代码分析

我们回到最初的代码上面:

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

9先以整数形式存储到n中:

复制代码
0000 0000 0000 0000 0000 0000 0000 1001

如果以整数形式打印,还是9。

如果以浮点数形式打印:

复制代码
0 00000000 00000000000000000001001

为一个无限接近0的数,所以打印0.000000。

接着9.0以浮点数的形式储存:

首先,浮点数9.0 等于⼆进制的1001.0,即换算成科学计数法是:1.001×2^3

所以: 9.0 = (−1) ^0 * (1.001) ∗ 2 ^ 3 ,那么,第⼀位的符号位S=0,有效数字M等于001后⾯再加20个0,凑满23位,指数E等于3+127=130,即10000010

所以,写成⼆进制形式,应该是S+E+M,即:

复制代码
0 10000010 00100000000000000000000

以浮点数打印就还是9.0

如果以整数打印:

复制代码
0100 0001 0001 0000 0000 0000 0000 0000

因为是正数,原码等于补码,即为1091567616。


相关推荐
JPX-NO2 小时前
Rust + Rocket + Diesel构建的RESTful API示例(CRUD)
开发语言·rust·restful
无敌最俊朗@2 小时前
STL-关联容器(面试复习4)
开发语言·c++
bybitq2 小时前
string,byte,rune,character?详解Golang编码-UTF-8
开发语言·后端·golang
wjs20242 小时前
HTML 框架:构建网页结构的基础
开发语言
无限进步_2 小时前
【C语言】栈(Stack)数据结构的实现与应用
c语言·开发语言·数据结构·c++·后端·visual studio
闻缺陷则喜何志丹2 小时前
【计算几何 SAT轴】P6732 「Wdsr-2」方分|普及+
c++·数学·计算几何·sat轴·凸多边形分离
embrace992 小时前
【C语言学习】预处理详解
java·c语言·开发语言·数据结构·c++·学习·算法
拼好饭和她皆失2 小时前
《二分答案算法精讲:从原理到实战(上篇)》
c++·算法
helloworddm2 小时前
C++与C#交互 回调封装为await
c++·c#·交互