当前位置: 首页 > news >正文

大端,小端;网络序,主机序

在网络编程中,常常遇到字节序问题。字节序指的是多字节数据类型在内存中存放的顺序,高位保存在低地址还是高地址,以此来划分大端还是小端。单字节数据类型,一个数据就一个字节,不涉及字节序的概念。内存的低地址保存数据的高位,就是大段;否则,就是小端。

(1)大端更符合我们直观的阅读顺序以及平时的习惯

(2)大端,小端,指的是字节序,大小端不同,字节存储顺序是不一样的。

大端,小端并不影响一个字节内部的 bit 的顺序。

对于一个字节的数据类型,比如 char,不受大端还是小端的影响。

(2)无论是大端序还是小端序,对于上边这个 int 类型的数据来书,这个数据的地址都是 0x400,即数据所在内存空间的低地址。用&取地址,返回的均是开始的地址。

1 大端和小端

大端和小端指的是 cpu 的属性,常见的 intel 的 x86 cpu 是小端的,mips 架构的 cpu 是大端的。

假设有一个 int 类型的数据,值为 0x12345678,保存在地址 0x400 ~ 0x403 这 4 个字节的位置。

大端:

在大端 cpu 上,存储情况如下图所示,高位存储在低地址。0x12 是高位,存储在地址 0x400 上;0x78 是低位,存储才 0x403 上。

小端:

小端与大端相反,低位存储在高地址。0x12 是高位,存储在 0x403;0x78 是低位,存储在 0x400。

如何判断大端还是小端 ?

如下代码可以判断大端还是小端,使用 union 数据结构的特点,union test 中有两个成员,一个是 char 类型,一个是 int 类型,union 占用 4 个字节,char 类型保存在 4 个字节的低地址。

如果 a 是 0x12,说明高位数据保存在了低地址,是大端;否则,是小端。

#include <stdlib.h> #include <stdio.h> #include <string.h> int main() { union test { char a; int b; }; union test t; t.b = 0x12345678; if (t.a == 0x12) { printf("大端\n"); } else { printf("小端\n"); } return 0; }

程序编译之后运行,打印小端,与 lscpu 查看到的信息是一致的。

如下代码,如果在小端机器上,输出为 0x1234,如果在大端机器上,输出为 0x5678。

#include <iostream> int main() { union { unsigned a; struct { char b; short c; }d; }e{0x12345678}; std::cout << e.d.c << std::endl; return 0; }

2 网络序和主机序

网络序与大端序是一致的,不管机器是大端的还是小端,发向网络的数据以及从网络中接收的数据都是大端的。大端,网络序与我们平时的思维习惯是一致的。

网络序规定,收到的数据的第一个字节是高位。在数据的发送侧是从低地址发送数据,接收侧先到来的数据也是存储在低地址。所以高位存储在低地址,网络序和大端是一致的。

主机序与 cpu 有关,可能是大端的,也可能是小端的。

3 转换函数

linux 下 的 <arpa/inet.h> 头文件中提供了 4 个函数。4 个函数中的 h 表示 host,主机序;n 表示 net,网络序;l 针对的是 u32 的数据;s 针对的是 u16 的数据。htonl 就是将 32 位的数据从主机序转化为网络序。

uint32_t htonl(uint32_t hostlong);

uint16_t htons(uint16_t hostshort);

uint32_t ntohl(uint32_t netlong);

uint16_t ntohs(uint16_t netshort);

#include <stdlib.h> #include <stdio.h> #include <string.h> #include <arpa/inet.h> int main() { unsigned int a = 0x12345678; unsigned short b = 0x1234; printf("unsigned int 0x%x, host to net: 0x%x\n", a, htonl(a)); printf("unsigned int 0x%x, net to host: 0x%x\n", a, ntohl(a)); printf("unsigned short 0x%x, host to net: 0x%x\n", b, htons(b)); printf("unsigned short 0x%x, net to host: 0x%x\n", b, ntohs(b)); return 0; }

怎么对 64 位数据进行字节序转换 ?

如下代码实现了 64 位数据的字节序转换。最容易出错的是函数的最后一行 return (unsigned long)(((unsigned long)net_low << 32) | net_high);,其中 net_low 需要使用 unsigned long 进行转换,否则会发生数据丢失。

#include <arpa/inet.h> #include <stdio.h> #include <sys/mman.h> #include <sys/resource.h> uint64_t htonll(uint64_t val) { uint32_t low = val & 0xFFFFFFFF; uint32_t high = (val >> 32) & 0xFFFFFFFF; low = htonl(low); high = htonl(high); //low需要做转化,否则移动之后数据丢失 return ((uint64_t)low << 32) | high; } uint64_t ntohll(uint64_t val) { uint32_t low = val & 0xFFFFFFFF; uint32_t high = (val >> 32) & 0xFFFFFFFF; low = ntohl(low); high = ntohl(high); return ((uint64_t)low << 32) | high; }
http://www.jsqmd.com/news/1137221/

相关文章:

  • 5分钟掌握BriefGPT:本地化文档智能助手终极指南
  • ROCK Pi N10(Rk3399Pro)开发记录 —— 基于Debian10系统的开发环境配置
  • Windows效率革命:PowerToys免费工具集完整指南
  • 终极指南:5步掌握SAM2 Hiera-Tiny图像特征提取模型的完整应用
  • iCloud照片下载器:三分钟掌握苹果照片库完整备份终极方案
  • Web安全实战指南:从SQL注入到内网渗透的靶场学习路径
  • 抖音,你所不知道的功能
  • Azure WebJobs SDK核心功能解析:触发器、绑定与扩展全攻略
  • Shell脚本加固效率量化:Shellharden自动化工具与手动修复的基准测试对比
  • NVIDIA Spark RAPIDS:3个简单步骤让Apache Spark性能提升10倍 [特殊字符]
  • 深度解析吉里吉里Z:高效构建跨平台游戏引擎的实战指南
  • NS-USBLoader:一站式Switch游戏管理工具的完整技术解析与使用指南
  • TikTokDownloader:跨平台内容采集的终极解决方案
  • 如何实现ImGui Java跨平台部署:Windows、Linux、macOS三平台构建终极指南
  • FancySelect自定义模板教程:打造个性化下拉选择框
  • Java单列集合——List
  • win10优化 电脑
  • 分钟买不了吃亏系列: nginx动态域名解析
  • LTX-2.3 Foley LoRA完全指南:从安装到生成专业音效的简单步骤
  • 解锁Emby/Jellyfin媒体播放新境界:本地播放器深度集成与进度同步完全指南
  • JavaScript_数组常用方法【2】
  • CC Switch技术深度解析:构建企业级AI代理网关的架构决策与实践指南
  • 3个技巧:用Nativefier将任意网页变身原生桌面应用
  • 华为昇腾310B1平台解码过程中遇到的几个错误以及解决方法
  • LV30条码扫描器与PIC18F86J55硬件集成及解码优化
  • Linux防火墙端口开放:firewalld、iptables、ufw 3种工具实战与选型指南
  • 为什么选择HQTrack?5大核心优势助力视频目标追踪任务效率提升300%
  • 2.1.2工具篇-Excel——用Excel清洗和处理数据
  • 为什么Fish Shell成为开发者最爱的命令行工具:5个颠覆性功能深度解析
  • OpenChat架构深度解析:构建企业级智能对话系统的核心技术实践