惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Cisco Talos Blog
Cisco Talos Blog
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
爱范儿
爱范儿
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
Jina AI
Jina AI
雷峰网
雷峰网
The Register - Security
The Register - Security
The Cloudflare Blog
博客园 - 【当耐特】
M
MIT News - Artificial intelligence
I
InfoQ
博客园 - 三生石上(FineUI控件)
H
Help Net Security
博客园 - 司徒正美
Vercel News
Vercel News
WordPress大学
WordPress大学
S
SegmentFault 最新的问题
云风的 BLOG
云风的 BLOG
B
Blog
Google DeepMind News
Google DeepMind News
B
Blog RSS Feed
L
LangChain Blog
人人都是产品经理
人人都是产品经理
GbyAI
GbyAI
T
The Blog of Author Tim Ferriss
T
Tailwind CSS Blog
aimingoo的专栏
aimingoo的专栏
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
Recorded Future
Recorded Future
小众软件
小众软件
Martin Fowler
Martin Fowler
罗磊的独立博客
Stack Overflow Blog
Stack Overflow Blog
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
酷 壳 – CoolShell
酷 壳 – CoolShell
腾讯CDC
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
Apple Machine Learning Research
Apple Machine Learning Research
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
博客园 - Franky
Hugging Face - Blog
Hugging Face - Blog
Y
Y Combinator Blog
V
Visual Studio Blog
F
Fortinet All Blogs
Microsoft Azure Blog
Microsoft Azure Blog
大猫的无限游戏
大猫的无限游戏
Engineering at Meta
Engineering at Meta
N
Netflix TechBlog - Medium
V
V2EX
Blog — PlanetScale
Blog — PlanetScale

文章列表 - 她和她的猫

使用小鹤双拼一个月 折腾 Hugo PaperMod 主题 去长鹿旅游休博园 1Password 自动化登录堡垒机 深入 Hyperf:Inject 注解是如何工作的? 译|理解容器镜像层 使用 Rust 实现高性能预写日志(Write Ahead Log) 译|使用 Linux 命名空间隔离系统 深入 Hyperf:HTTP 服务是如何处理请求的? Linux 文件权限学习笔记 译|了解 Bitcask:基于日志结构的 KV 存储引擎 还原 SM2 压缩公钥的几种方法 深入 Hyperf:HTTP 服务启动时发生了什么? 一次 Hyperf 注解失效问题分析 将博客迁移到又拍云 译|PHP 7 新的 Hashtable 实现 排查 ES 查询问题:深入了解 json_encode() 函数 浅析 Redlock 分布式锁实现原理 Yar 源码阅读笔记:RPC 服务端 Yar 源码阅读笔记:客户端的并行调用 Yar 源码阅读笔记:客户端的同步调用 Yar 源码阅读笔记:数据传输模块 Yar 源码阅读笔记:消息编码模块 Yar 源码阅读笔记:RPC 通信协议 Yar 源码阅读笔记:开篇 PHP 8 新特性介绍 基于 OpenSSL 实现国密 SM4 加解密 PHP 源码阅读笔记:编译与调试 PHP 什么是惊群问题 基于 GitHub Actions 定时推送网址到百度站长平台 聊聊五种 I/O 模型 Workerman 源码分析:文件上传 浅入浅出 HTTP 协议 PHP 网络编程:构建 MySQL 蜜罐获取攻击者微信 ID 使用 Workerman 接入 Bilibili 直播弹幕协议 使用 GitHub Actions 自动部署 Hexo 什么是二进制安全 【转载】PHP 程序员进阶之路 优化 Workerman 检查主进程是否存活的逻辑 基于 Redis 实现分布式锁 《PHP 实现 Base64 编码/解码》笔记 PHP 多进程下载必应壁纸 基于 Redis 实现延迟队列 【转载】PHP 实现 Base64 编码/解码 使用 MySQL FIELD() 函数自定义排序规则 Laravel 的 Facades 实现原理 C语言单链表实现约瑟夫环 数据结构学习笔记:顺序栈和链栈(C语言) 使用 QueryList + Redis 下载壁纸 在 Laravel 登录/注册中使用 mews/captcha 扩展包 使用 mews/captcha 扩展包为 Laravel 应用添加图片验证码 给 YOURLS 短网址系统编写插件《批量生成短网址》 给 YOURLS 短网址系统编写插件《Hello World!》 PHP 7.1 使用 json_encode 函数造成浮点类型数据出现精度问题 Laravel 授权策略(Policy)的基本使用 Ubuntu 系统下 WiFi 频繁掉线解决方法 Laravel 应用部署到 Nginx 服务器上的第一个坑 Ubuntu 16.04 LST 安装 Redis 和 Composer PHP 采集逐浪小说章节列表 Nginx 环境 SSL 强制 HTTP 301 跳转到 HTTPS 【工作&生活】第一周 ServiceStack.Redis 与多线程 Count the number of Duplicates C# Base64 加密及解密 ASP.NET MVC 使用动态类型传递数据 Django 使用 order_by() 对数据进行排序操作 ASP.NET + jQuery + AJAX 实现用户登录 ASP.NET 从客户端中检测到有潜在危险的 Request.Form 值 ASP.NET 使用 FormData 实现 AJAX 上传图片 Python 使用 BeautifulSoup 抓取网页 贪心算法之字符串的完美度 一言不和就造轮子之 CookieHelper ASP.NET 上传图片 DBHelper 类 C# 捕获屏幕源码 用 JavaScript 做一些有趣的事情 第一篇文章 好久不见。 如何防止非法调用 PHP 文件 清明小记。 PHP 生成随机字符串 Z-Blog PHP 程序做 301 跳转 PHP 处理数组常用的几个函数 Sublime Text 3 快捷键总结 PHP 使用 file_get_contents() 函数实现采集网页 使用 C# 编写学生信息管理系统 终于到了。 zblog报错: magic_quotes_gpc 和 safe_mode 世事无常。 如何使用 PHP 上传文件 安卓手机编写和运行 PHP 程序的软件 使用 AJAX 刷新、验证 PHP 图片验证码 PHP 生成图片验证码 PHP 之 session 的使用方法 PHP 之 cookie 的使用方法 PHP 实现分页原理详解 Thinkphp 框架中系统常量在 Js 文件中不解析的解决方法
大端模式和小端模式
她和她的猫 · 2021-06-14 · via 文章列表 - 她和她的猫

什么是大/小端模式

大端模式

大端模式(Big-Endian)又称大端字节序,由于在网络传输中一般使用的是大端模式,所以也叫网络字节序。

在大端模式中,将高位字节放在低位地址,低位字节放在高位地址。

举个例子,数值 0x12345678,其中 0x12 这一端是高位字节,0x78 这一端是低位字节。

该数值的存储顺序是这样的:

大端模式符合我们阅读和书写的方式,都是从左到右的。比如 12345678,我们只需要按照从左到右的顺序进行阅读和书写就是大端模式的存储顺序了。

小端模式

小端模式(Little-Endian)又称小端字节序,由于大多数计算机内部处理使用的是小端模式,所以也叫主机序。

在小端模式中,将高位字节放在高位地址,低位字节放在低位地址。

小端模式比较符合我们人类的思维模式,大的放大的那一边,小的放小的那一边。但是在计算机中存储的顺序与我们看到的顺序是相反的。

为什么会有大/小端模式

对于早期的计算机来说,先处理低位字节效率比较高,因为计算都是从低位开始的,所以大多数计算机内部处理使用的是小端模式。但是计算机发展到现在,计算机的处理器相较于以前已经进步很多了,先处理高位还是低位字节的影响已经可以忽略,但是为了向后兼容,保留了大/小端模式。

大端模式更适合程序员阅读,因为看到的内容与输出的内容是一致的。

如何判断主机是大端还是小端

通过读取低位地址

定义一个 16 位无符号的整数值,然后判断其低位字节存放的位置。

#include <stdio.h>

int main() {
    __uint16_t val = 0x1234;

    char a = ((char *) &val)[0]; // 低位地址
    char b = ((char *) &val)[1]; // 高位地址

    printf("a = %x\n", a);
    printf("b = %x\n", b);

    if (a == 0x34) {
        printf("小端模式\n");
    } else {
        printf("大端模式\n");
    }

    return 0;
}

通过 &val 取得 val 的内存地址,然后将其转为 char 类型的指针,再以数组的方式取指针地址上存储的值。

下标 0 可以取到低位地址上的值,下标 1 可以取到高位地址上的值。如果下标 0 取到的是 0x34,说明是小端模式,因为低位字节存储在低位地址上。

我的电脑是小端模式的,最后会输出:

利用联合体

联合体是一种特殊的数据结构,联合体中的成员变量共用同一段内存。

我们定义一个 test 联合体,设置两个成员变量 a 和 b。

#include <stdio.h>

int main()
{
    union test {
        __uint32_t a;
        char b;
    };

    union test val;

    val.a = 0x12345678;

    printf("%x\n", val.b);

    if (val.b == 0x78) {
        printf("小端模式\n");
    } else {
        printf("大端模式\n");
    }

    return 0;
}

如果主机是小端模式,0x12345678 的存储顺序为:

与第一种方法目的相同,都是通过获取低位地址的第一个字节,判断其存储的内容就可以知道主机是大端还是小端模式,不过这里利用了联合体成员变量共用内存的特点,实现方式更加巧妙。

使用 C 语言内置宏

除了上面两种自制的“土方法”,C 语言已经自带了一些宏用来判断主机的字节序。

endian.h 文件:

// 小端模式
# define LITTLE_ENDIAN	__LITTLE_ENDIAN
// 大端模式
# define BIG_ENDIAN	__BIG_ENDIAN
// 当前主机的字节序
# define BYTE_ORDER	__BYTE_ORDER
#include <endian.h>

int main()
{
    if (BYTE_ORDER == LITTLE_ENDIAN) {
        printf("小端模式\n");
    } else {
        printf("大端模式\n");
    }

    return 0;
}

大小端转换

为什么需要大小端转换

前面我们提到,在主机基本上使用的都是小端模式,但是在网络传输的时候使用的却是大端模式。

如果我们的程序仅仅是一个单机程序,不需要跟其它程序进行数据交互,那么是不需要进行大小端转换的。

如果程序需要与其它程序进行数据交互,那么在发送数据前,就要将数据从小端模式转换为大端模式。在接收到数据后,将数据从大端模式转换为小端模式。

手动实现转换逻辑

只需要将高位字节与低位字节进行交换,就可以实现大小端的转换。

下面是实现代码:

int main()
{
    __uint32_t val = 0x12345678;

    unsigned char *x = (unsigned char *) &val, tmp;

    // 0x78 与 0x12 进行交换
    tmp = x[0];
    x[0] = x[3];
    x[3] = tmp;

    // 0x56 与 0x34 交换
    tmp = x[1];
    x[1] = x[2];
    x[2] = tmp;

    // 输出:0x78563412
    printf("0x%x\n", val);

    return 0;
}

使用 C 语言内置宏

在 C 语言中已经提供了大小端转换宏在 endian.h 头文件中,下面列出部分:

// 转换 16 位整数
htobe16(x)
be16toh(x)

// 转换 32 位整数
htobe32(x)
be32toh(x)

// 转换 64 位整数
htobe64(x)
be64toh(x)
  • h 的意思是 host,表示小端模式。
  • be 的意思是 big-endian,表示大端模式。
  • 16、32、64 的意思是 16 位、32 位、64 位整数,表示不同位数的整数转换。
int main()
{
    __uint32_t val = 0x12345678;
    // 输出:0x78563412
    printf("0x%x\n", htobe32(val));

    return 0;
}

总结

计算机在处理数据的时候,只会按照顺序去读取字节,不关心数据是大端模式还是小端模式。

程序在读取到数据后,需要判断计算机的大小端模式,来决定是否需要进行大小端转换。

如果读到的第一个字节是高位字节,那么就是大端模式;反之,如果读到的第一个字节是低位字节,那么就是小端模式。