一文看懂计算机中的大小端(Endianess)

文章目录

  • 前言
  • 一、什么是大小端
  • 二、如何判断大小端
  • 三、大小端的转换
    • 3.1 使用标准库函数
    • 3.2 手动实现大小端转换


前言

本文主要探讨计算机中大小端的相关概念以及如何进行大小端的判断和转换等。


一、什么是大小端

大小端(Endianess)是指计算机系统在存储多字节数据时,字节的顺序,即存储数据的字节顺序。

计算机系统的内存是以字节为单位进行划分的,每个地址单元都对应着一个字节,一个字节的大小为8bit,可以存放一个8位的二进制数,比如10101010。但是在C语言中除了8bit的char类型之外还有16bit的short类型,32bit的long类型,这主要取决于具体的编译器。且对于位数大于8位的处理器,例如16位或者32位的处理器,由于寄存器宽度大于1个字节,那么必然存在着如何将多个字节安排进入内存的问题,因为就产生的大端存储模式和小端存储模式。

如下所示为数据0x12345678在计算机存储器中的大小端存储模式。

在这里插入图片描述

大端(Big Endian): 数据的高位字节存放在低地址,低位字节存放在高地址。
小端(Little Endian): 数据的低位字节存放在低地址,高位字节存放在高地址。

二、如何判断大小端

以下是一些常见处理器架构及其对应的字节序(大小端)总结表:

处理器架构字节序
Intel x86Little-Endian
Power-PCBig-Endian
ARM默认 Little-Endian
STM32Little-Endian

判断系统的字节序(大小端)主要依赖于检查内存中数据的排列方式,例如我们可以通过定义一个联合体,将一个整型数据的地址与字符数组的地址重叠,从而通过查看存储顺序来判断字节序。

#include <stdio.h>typedef union {int i;char c[4]; // 假设 int 是 4 字节
} Endianness;int main() {Endianness e;e.i = 0x01020304; // 设定一个已知的整数if (e.c[0] == 0x04) {printf("小端\n");} else if (e.c[0] == 0x01){printf("大端\n");}return 0;
}

三、大小端的转换

在处理数据时,尤其是在网络通信和文件读写中,可能需要在大端(Big Endian)和小端(Little Endian)之间进行转换。以下是几种常见的大小端转换方法,包括使用标准库函数和手动实现。

3.1 使用标准库函数

在许多C标准库中,提供了网络字节序的转换函数,可以用来进行大小端的转换。以下是几个常用的函数:

  • htonl():将主机字节顺序转换为网络字节顺序(32位整数)
  • htons():将主机字节顺序转换为网络字节顺序(16位整数)
  • ntohl():将网络字节顺序转换为主机字节顺序(32位整数)
  • ntohs():将网络字节顺序转换为主机字节顺序(16位整数)

示例代码:

#include <stdio.h>
#include <arpa/inet.h>int main() {uint32_t num = 0x12345678;uint32_t converted_num = htonl(num); // 转换为网络字节序(大端)printf("Original: 0x%x\n", num);printf("Converted: 0x%x\n", converted_num);uint32_t back_to_host = ntohl(converted_num); // 转换回主机字节序printf("Back to Host: 0x%x\n", back_to_host);return 0;
}

编译输出如下:

jeff@jeff:/tmp$ gcc -o test test.c
jeff@jeff:/tmp$ ./test
Original: 0x12345678
Converted: 0x78563412
Back to Host: 0x12345678
jeff@jeff:/tmp$

3.2 手动实现大小端转换

如果没有标准库可用,可以手动实现大小端的转换,以下是一个手动转换32位和16位整数的示例。

32位整数转换

uint32_t swap_uint32(uint32_t num) {return ((num >> 24) & 0xff) | // 取出最高字节并移到最低位((num >> 8) & 0xff00) | // 取出次高字节并移到次低位((num << 8) & 0xff0000) | // 取出次低位并移到次高位((num << 24) & 0xff000000); // 取出最低位并移到最高位
}

16位整数转换

uint16_t swap_uint16(uint16_t num) {return (num >> 8) | (num << 8);
}

原理分析:

这里简单讲一下32位整数的转换原理,比如传进一个num = 0x12345678,那么我们的目的是想要输出num = 0x78563412。

0x12345678 的二进制形式为 00010010 00110100 01010110 01111000

1. 取出最高字节并移到最低位

(num >> 24) & 0xff00000000 00000000 00000000 00010010 & 
00000000 00000000 00000000 11111111结果为
00000000 00000000 00000000 00010010

2. 取出次高字节并移到次低位

(num >> 8) & 0xff0000000000 00010010 00110100 01010110 &
00000000 00000000 11111111 00000000结果为
00000000 00000000 00110100 00000000

3. 取出次低位并移到次高位

(num << 8) & 0xff000000110100 01010110 01111000 00000000 & 
00000000 11111111 00000000 00000000结果为
00000000 01010110 00000000 00000000

4. 取出最低位并移到最高位

(num << 24) & 0xff00000001111000 00000000 00000000 00000000 &
11111111 00000000 00000000 00000000 结果为
01111000 00000000 00000000 00000000 

最后再将结果进行或运算就得到0x78563412了,其实说白了就是用左移、右移操作符进行数据位的移动,然后用按位与提取指定数据位,最后再用按位或将数据拼接在一起。

示例代码:

#include <stdio.h>
#include <stdint.h> // 添加此行以包含 uint32_t 和 uint16_t 的定义uint32_t swap_uint32(uint32_t num) {return ((num >> 24) & 0xff) |((num >> 8) & 0xff00) |((num << 8) & 0xff0000) |((num << 24) & 0xff000000);
}uint16_t swap_uint16(uint16_t num) {return (num >> 8) | (num << 8);
}int main() {uint32_t num32 = 0x12345678;uint16_t num16 = 0x1234;uint32_t converted32 = swap_uint32(num32);uint16_t converted16 = swap_uint16(num16);printf("Original 32-bit: 0x%x, Converted: 0x%x\n", num32, converted32);printf("Original 16-bit: 0x%x, Converted: 0x%x\n", num16, converted16);return 0;
}

编译输出如下:

jeff@jeff:/tmp$ gcc -o test2 test2.c
jeff@jeff:/tmp$ ./test2
Original 32-bit: 0x12345678, Converted: 0x78563412
Original 16-bit: 0x1234, Converted: 0x3412
jeff@jeff:/tmp$

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/440726.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【AI学习笔记】基于Unity+DeepSeek开发的一些BUG记录解决方案

【AI学习笔记】基于UnityDeepSeek开发的一些BUG记录&解决方案 背景前摇&#xff1a;&#xff08;省流可不看&#xff09; Unity是大学学的&#xff0c;AI是研究生学的&#xff0c;DeepSeek是第一份实习偷师的&#xff0c;三合一的梦是最近开始做的&#xff0c;BUG是今天遇…

【数据结构】什么是哈希表(散列表)?

&#x1f984;个人主页:修修修也 &#x1f38f;所属专栏:数据结构 ⚙️操作环境:Visual Studio 2022 目录 &#x1f4cc;哈希表的概念 &#x1f4cc;哈希函数的构造方法 &#x1f38f;直接定址法 &#x1f38f;除留余数法 &#x1f38f;平方取中法 &#x1f38f;折叠法 &#x…

Bolt.new:终极自动化编程工具

兄弟们&#xff0c;终极写代码工具来了—— Bolt.new&#xff01;全方位的编程支持&#xff1a; StackBlitz 推出了 Bolt․new&#xff0c;这是一款结合了 AI 与 WebContainers 技术的强大开发平台&#xff0c;允许用户快速搭建并开发各种类型的全栈应用。 它的主要特点是无需…

前端reactvue3——实现滚动到底加载数据

文章目录 ⭐前言⭐react 实现滚动加载⭐vue3 实现滚动加载⭐总结⭐结束 ⭐前言 大家好&#xff0c;我是yma16&#xff0c;本文分享 前端react&vue3——实现滚动加载&#xff08;到底部加载&#xff09; scrollTop 属性 一个双精度浮点值&#xff0c;表示元素当前从原点垂直…

安全运营中心 (SOC) 团队对其安全工具感到失望

Vectra AI 表示&#xff0c;安全运营中心 (SOC) 从业人员认为&#xff0c;由于太多孤立的工具和缺乏准确的攻击信号&#xff0c;他们在检测和确定真实威胁的优先级方面正在失败。 人们对供应商的不信任感日益加深&#xff0c;认为供应商的工具在发现真正的攻击方面起的阻碍作用…

ctfshow-web入门(信息收集,持续更新中。。)

写在之前:近期打了个比赛,备受打击,入手了vip账号进修,加油! 文章目录 ctfshow-web1查看源代码ctfshow-web2burp抓包ctfshow-web3burp抓包ctfshow-web4访问robots.txtctfshow-web5dirscarch扫描PHPS文件泄露ctfshow-web6dirscarch扫描ctfshow-web7dirscarch扫描ctfshow-w…

【STM32开发之寄存器版】(六)-通用定时器中断

一、前言 STM32定时器分类 STM32103ZET6具备8个定时器TIMx(x 1,2,...,8)。其中&#xff0c;TIM1和TIM8为高级定时器&#xff0c;TIM2-TIM6为通用定时器&#xff0c;TIM6和TIM7为基本定时器&#xff0c;本文将以TIM3通用定时器为例&#xff0c;分析STM32定时器工作的底层寄存器…

You must konw JS!!(超详细的javascript套餐,适合计算机专业有基础的,包含常见前端开发面试题)

1.起源 JavaScript 起源于 1995 年&#xff0c;当时它主要是为了满足网页交互的需求而被创建。它最初的设计目的是为了让网页开发者能够在网页中添加一些简单的交互效果和动态内容。在那个时期&#xff0c;网页大多是静态的&#xff0c;而 JavaScript 的出现为网页带来了新的活…

jmeter学习(7)beanshell

beanshell preprocessor 发送请求前执行 beanshell postprocessor 发送请求前执行 获取请求相关信息 String body sampler.getArguments().getArgument(0).getValue(); String url sampler.getPath(); 获取响应报文 String responseprev.getResponseDataAsString(); 获…

CMake 教程跟做与翻译

目录 STEP 1: 入门与理解 cmake_minimum_required设置CMake版本的最小值 project声明工程属性 add_executable添加可执行文件 使用CMake构建工程 根据自己的构建工具自行构建 Reference STEP 1: 入门与理解 我们起手的&#xff0c;最基本的 CMake 项目是从单个源代码文件…

【Blender Python】1.概述和基础使用

概述 众所周知&#xff0c;Blender是一款开源免费的3D建模软件&#xff08;当然不限于3D建模&#xff09;。在Blender中&#xff0c;可以使用其内置的Python解释器执行Python代码&#xff0c;用于程序化的生成网格以及其他内容。你可以基于此创建Blender插件。 这个系列就是快…

Electron桌面应用打包现有的vue项目

1 环境准备 Node&#xff1a;v16.20.2&#xff08;本地vue项目nodejs版本&#xff09;Electron&#xff1a;22.3.7vue&#xff1a;2 版本管理 2 Vue项目准备 更新相关依赖npm install --registry https://registry.npmmirror.com/npm run dev 3、引入Electorn 安装指定版…

算法剖析:双指针

文章目录 双指针算法一、 移动零1. 题目2. 算法思想3. 代码实现 二、 复写零1. 题目2. 算法思想3. 代码实现 三、 快乐数1. 题目2. 算法思想3. 代码实现 四、 盛水最多的容器1. 题目2. 算法思想3. 代码实现 五、有效三角形的个数1. 题目2. 算法思想3. 代码实现 六、 和为 s 的两…

UART驱动学习三(TTY驱动部分源码解析)

目录 全局框架图一、tty_io.c 分析1. 关键数据结构和定义2. 文件操作结构体3. 初始化和注册4. 读写操作5. 挂起和恢复6. 信号处理7. 设备类8. 控制台通知9. 辅助函数10. 代码功能11. 带有注释的部分tty_io.c源码 二、tty_ldisc.c 分析1. 关键数据结构和定义2. 行规程操作函数3.…

Android车载——VehicleHal运行流程(Android 11)

1 概述 本篇主要讲解VehicleHal的主要运行流程&#xff0c;包括设置属性、获取属性、订阅属性、取消订阅、持续上报属性订阅等。 2 获取属性流程 2.1 获取属性流程源码分析 作为服务注册到hwServiceManager中的类是VehicleHalManager&#xff0c;所以&#xff0c;CarServic…

判断是否为二叉排序树(二叉搜索树,二叉查找树)

1.判断给定的二叉树是否为二叉排序树&#xff0c;如果是返回1&#xff0c;不是返回0。 思想&#xff1a; 二叉树是左子树<根<右子树。中序遍历是递增有序&#xff0c;可以通过比较当前结点与前驱关系来进行判断。 代码&#xff1a; //pre为全局变量&#xff0c;保存当…

数学与生活

多学科交叉 信号处理 小波 经济 政策 计算机 统计 信号处理与市场分析 经济与数据分析 政策与统计 过去的数学家没有一个是纯粹的数学家&#xff1b;生活中各方面工程的&#xff0c;物理的&#xff0c;天文&#xff0c;地理的&#xff0c;赌博&#xff0c;政治的&#xff1b…

5-25 JQuery

jQuery简介 jQuery是什么 jQuery基本语法 测试jQuery <head> <meta charset"utf-8"> <title>无标题文档</title><script type"text/javascript" src"jquery-3.5.1.js"></script><script type"tex…

FastAdmin Apache下设置伪静态

FastAdmin Apache下设置伪静态 一、引言 FastAdmin 是一个基于ThinkPHP和Bootstrap框架开发的快速后台开发框架&#xff0c;它以其简洁、高效、易于扩展的特点&#xff0c;广受开发者的喜爱。在部署FastAdmin项目时&#xff0c;为了提高访问速度和用户体验&#xff0c;我们通…

Redis介绍及整合Spring

目录 Redis介绍 Spring与Redis集成 Redis介绍 Redis是内存数据库&#xff0c;Key-value型NOSQL数据库&#xff0c;项目上经常将一些不经常变化并且反复查询的数据放入Redis缓存&#xff0c;由于数据放在内存中&#xff0c;所以查询、维护的速度远远快于硬盘方式操作数据&#…