摘要:外部设备会自己处理字节序的问题。本地序和网络序从前面对于字节序的介绍可以知道采用小端序,而等采用大端序。协议很好的解决了这个问题,协议规定使用大端字节序作为网络字节序。提供了一组接口用于整型数据在本地序和网络序之间的转换。
我们学习了整型在内存中是以原反补码的形式存储的,我们还学习了浮点型在内存中是以符号位(S)指数位(E)有效数字(M)的形式存储的。
那么我们知道了数据在内存中是以什么样的形式存储的,可是我们知道内存中有高地址有低地址,数据是一个字节分配一个地址,可是仅
char
类型是一个字节,其他类型大都大于一个字节的大小,那么大于一个字节的数据是从【高地址】往【低地址】存放还是从【低地址】往【高地址】存放呢?
举一个栗子?:当你声明一个变量的时候,操作系统会给你分配一块空间,但是如果你创建的这个变量的类型是【short/int/float/double】这些大小大于一个字节的类型的数据,操作系统反正是已经把这些类型相应的大小的空间分配给你了,你内部怎么存储可不关它的事了,也就是我们将一个4字节的数据存入分配好的一段4字节的物理容器里, 该怎么存放呢?这时,我们存放进去的数据的字节是该依据一般人们的读写习惯从左往右依次写入, 还是从右向左写入呢? 不论哪种方式只要保证写入和读出的数据一致即可。
对于字节序列的存储格式,必然需要说到CPU的两大派系,那就是IBM的Power PC系列的CPU和Intel的x86系列的CPU。Power PC系列采用big endian方式存储数据,而x86系列则采用little endian方式存储数据。那么big endian和little endian都是些什么鬼呢??
在几乎所有的机器上,多字节对象都被存储为连续的字节序列,而字节序指的是多字节的数据各字节在内存中的存储顺序,分为大端存储模式(Big-Endian)和小端存储模式(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 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)
|
|(系列持续周更)
|
数据存储系列往期回顾:
详解浮点型在内存中的存储
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/119678.html
摘要:在位机器上,指针变量的大小为个字节。指针类型的强制类型转换对指针变量进行强制类型转换的一般形式将保存的类型指针强制转换为类型指针后赋值给,其中还是为,没有改变。 前言 大家好,我是努力学习的少年,今天这篇文章是专门写关于指针的知识点,因为指针内容比较多,所以我将指针的这篇文章我将它分为...
摘要:数据的存储前言数据类型汇总整型家族浮点型家族自定义类型指针类型。整型家族注在之后的标准规定,将类型数据划分为整型家族,因为字符在内存中会将其转化为码值进行存储。 ...
摘要:写在前面博客主页的江湖背景的江湖背景欢迎关注点赞收藏留言本文由原创,首发首发时间年月日最新更新时间年月日坚持和努力一定能换来诗与远方向未见花闻学习参考书籍深入理解计算机系统作者水平很有限,如果发现错误,请留言轰炸哦万分感谢感谢感谢 ?写在前面 ?博客主页:kikoking的江湖背景?...
阅读 4375·2021-09-09 09:33
阅读 2381·2019-08-29 17:15
阅读 2369·2019-08-29 16:21
阅读 970·2019-08-29 15:06
阅读 2612·2019-08-29 13:25
阅读 577·2019-08-29 11:32
阅读 3246·2019-08-26 11:55
阅读 2586·2019-08-23 18:24