数据在内存中存放的顺序之字节序(附图解判断本机大小端程序及例题)建议收藏食用

Posted 哆啦A梦_PJYA

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了数据在内存中存放的顺序之字节序(附图解判断本机大小端程序及例题)建议收藏食用相关的知识,希望对你有一定的参考价值。

数据在内存中存放的顺序(字节序之Little-Endian&Big-Endian)

引言📖

我们学习了整型在内存中是以原反补码的形式存储的,我们还学习了浮点型在内存中是以符号位(S)指数位(E)有效数字(M)的形式存储的。

我们知道了数据在内存中是以什么样的形式存储的,那么数据的存储顺序呢?我们知道内存中有高地址有低地址,数据是一个字节分配一个地址,可是仅char类型是一个字节,其他类型大都大于一个字节的大小,那么大于一个字节的数据是从【高地址】【低地址】存放还是从【低地址】【高地址】存放呢?

举一个栗子🌰:当你声明一个变量的时候,操作系统会给你分配一块空间,但是如果你创建的这个变量的类型是【short/int/float/double】这些大小大于一个字节的类型的数据,操作系统反正是已经把这些类型相应的大小的空间分配给你了,你内部怎么存储可不关它的事了,也就是我们将一个4字节的数据存入分配好的一段4字节的物理容器里, 该怎么存放呢?这时,我们存放进去的数据的字节是该依据一般人们的读写习惯从左往右依次写入, 还是从右向左写入呢? 不论哪种方式只要保证写入和读出的数据一致即可。

字节序📌之Little-Endian&Big-Endian

对于字节序列的存储格式,必然需要说到CPU的两大派系,那就是IBM的Power PC系列的CPU和Intel的x86系列的CPU。Power PC系列采用big endian方式存储数据,而x86系列则采用little endian方式存储数据。那么big endianlittle endian都是些什么鬼呢🧐?

在几乎所有的机器上,多字节对象都被存储为连续的字节序列,而字节序指的是多字节的数据各字节在内存中的存储顺序,分为大端存储模式(Big-Endian)和小端存储模式(Little-Endian)。

  1. Big-endian(大端模式):数据的高位字节保存在内存的低地址中,而数据的低位字节保存在内存的高地址中。
  2. Little-endian(小端模式):数据的高位字节保存在内存的高地址中,而数据的低位字节保存在内存的低地址中。

假设,一个4字节的int类型变量a,它的十六进制形式为0x11223344,(0x11为高位字节,0x44为低位字节)操作系统分配的空间的起始地址为0x000001,那么a的四个字节将被分别存在0x000001,0x000002,0x000003,0x000004的位置。

显然,Big-Endian的存储更贴切于我们平时的读写习惯。那么为什么不统一使用Big-Endian呢?

计算机电路先处理低位字节,效率比较高,因为计算都是从低位开始的,所以,计算机的内部处理都是小端字节序。

而大端序存储,由于符号位在高位,因此对于数据正负或大小的判断也就方便许多。另外,大端序也更符合人们的读写习惯。所以,除了计算机的内部处理,其他的场合几乎都是大端字节序,比如网络传输和文件储存。

这里需要注意⚠:只有读取的时候,才必须区分字节序,其他情况都不用考虑。

处理器读取外部数据的时候,必须知道数据的字节序,将其转成正确的值。然后,就正常使用这个值,完全不用再考虑字节序。即使是向外部设备写入数据,也不用考虑字节序,正常写入一个值即可。外部设备会自己处理字节序的问题。

本地序和网络序📌

从前面对于字节序的介绍可以知道x86采用小端序,而Power Pc等采用大端序。那么显然不同的处理器体系,采用的字节序可能是不同的。那么如此一来,不同机器之间的数据传输岂不是会出现问题?

本地序

本地序(也称主机序):指处理器本身所采用的字节序,因此有的大端序,有的小端序。

网络序

网络序:指网络传输采用的字节序。网络序是标准化的,统一采用大端序。因此,发送网络数据之前需要将本地序转换为网络序。

剖析本地序和网络序

为什么要注意本地序和网络序的问题呢?当然,如果你写的程序只在单机环境下面运行,并且不和别人的程序打交道,那么你完全可以忽略字节序的存在。但是,你的程序是要跟别人的程序产生交互的!

计算机网络的出现让大小端问题变的复杂化了,每个计算机都有自己的主机字节序。不同计算机之间通过网络通信时:我“说”的你听不懂,你“说”我也听不懂,这可怎么办?这时候就需要约定俗成的协议来解决问题。

TCP/IP协议很好的解决了这个问题,TCP/IP协议规定使用“大端”字节序作为网络字节序。

这样不管计算机采用哪种字节序,发送数据的时候必须将自己的主机字节序转换为网络字节序,对接收到的数据转换为自己的主机字节序。这样一来,也就达到了与CPU、操作系统无关,实现了网络通信的标准化。

数据从本地传输到网络,需要转换为网络序,接收到的网络数据需要转换为本地序后使用。C提供了一组接口用于整型数据在本地序和网络序之间的转换。

通信时的本地序和网络序之间相互转换这种常用的操作在Socket API这一层,一般都提供了封装好的转换函数。从主机字节序到网络字节序的转换函数:htons、htonl(C语言),从网络字节序到主机字节序的转换函数:ntohs、ntohl(C语言)。当然,明白了原理后也可以编写自己的转换函数。

判断当前机器的大小端📌

请简述大端字节序和小端字节序的概念,设计一个小程序来判断当前机器的字节序(百度笔试题)

思路:假设给上int a = 1;1在内存中16进制表示形式为0x 00 00 00 01,那么这段数据如果按大小端放置:

大端:00 00 00 01

小端:01 00 00 00

可以看出,可以通过判断第一个字节的内容判断大小端,第一个字节为0则是大端,为1则是小端。

可是怎么取第一个字节呢?这里我们可以用指针的类型的解引用来控制访问内存的大小char*就是只访问一个字节的内存。

int main(void)
{
	int a = 1;
	char* p = (char*)&a;
	if (*p == 1)
		printf("小端");
	else
		printf("大端");

	return 0;
}

把这个功能封装成一个函数:

int check_system()
{
	int a = 1;
	char* p = (char*)&a;
	
	if (*p == 1)
		return 1;//返回1,小端
	else
		return 0;//返回0,大端
}

那么这个函数呢,是可以优化的:

int check_system()
{
	int a = 1;
	char* p = (char*)&a;

		return *p;	
}

仍可以更简洁:

int check_system()
{
	int a = 1;
    
	return *(char*)&a;
}

总结:访问内存就需要考虑到指针。

大小端例题📌

#include <stdio.h>
int main()
{
	int a = 0x11223344;
    char *pc = (char*)&a;
    *pc = 0;
    printf("%x\\n", a);
    return 0;
}

假设a变量的地址为0x64,则a变量在内存中的模型为:

char*类型的指针变量pc只能指向字符类型的空间,如果是非char类型的空间,必须要将该空间的地址强转为char*类型。

pc实际指向的是整形变量a的空间,即pc的内容为0x64,即44;*pc=0,即将44位置中内容改为0,修改完成之后,a中内容为:0x11223300

(全剧终)感谢食用!

注:参考文章(https://www.ruanyifeng.com/blog/2016/11/byte-order.html)
|
|(系列持续周更)
|
数据存储系列往期回顾:
详解浮点型在内存中的存储

以上是关于数据在内存中存放的顺序之字节序(附图解判断本机大小端程序及例题)建议收藏食用的主要内容,如果未能解决你的问题,请参考以下文章

如何简单的判断机器的大小端字节序

字节对齐2

主机字节序 与 网络字节序

CPU的大小端及如果判断

请问 网络编程里的主机字节顺序和网路字节概念?

网络字节序