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

embedded/2024/10/18 23:29:14/

文章目录


前言

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


一、什么是大小端

大小端(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.ppmy.cn/embedded/124362.html

相关文章

我的第一个AI视频(通义万相)

背景需求&#xff1a; 今天试试通义万相的AI视频功能——免费5秒&#xff0c; 一、文生视频 关键词&#xff1a;一只可爱的小猫咪穿着厨师的服装正在厨房里烤鱼&#xff0c;流口水 大约生成了10分钟&#xff0c;视频是5秒、消耗灵感值5分 20241007星火讯飞视频猫厨师烤鱼 猫…

项目最后优化(十五)

一. wifi连接太慢 void Serial::wifi_Connect(int *flag,const QString &command) {// 创建 QProcess 对象QProcess *process new QProcess(this); // this 是 Serial 类的实例&#xff0c;继承自 QObject// 设置进程的可执行文件// process->setProgram(command);//…

view deign 和 vue2 合并单元格的方法

1.vue版本和view design 版本 {"vue": "^2.6.11","view-design": "^4.7.0", }2.Data中定义数据 spanArr: [], // 某一列下需要合并的行数 pos: 0// 索引// 注意点&#xff1a; 在获取列表前&#xff0c;需要重置 this.spanArr [] 注…

Mysql中的事务

目录&#xff1a; 一&#xff1a;事务的ACID特性及为什么要使用事务 二&#xff1a;如何使用事务 三&#xff1a;事务的隔离级别 一&#xff1a;事务的ACID特性及为什么要使用事务 &#xff1a; 1.事务的ACID特性&#xff1a; 事务的ACID特性指的是 Atomicity (原子性)&#…

【分布式微服务云原生】gRPC与Dubbo:分布式服务通信框架的双雄对决

摘要 在构建分布式系统时&#xff0c;选择合适的服务间通信框架至关重要。gRPC和Dubbo作为两个领先的框架&#xff0c;各自拥有独特的优势和应用场景。本文将深入比较这两个框架&#xff0c;探讨它们的定义、语言支持、接口定义、通信协议、服务治理以及应用场景。通过本文&…

选择网络安全模式启动Windows系统,解决PC无法连接网络问题

目录 1、电脑无法连接网络 2、发现C:\Windows\System32\drivers路径下的很多文件不见了 3、使用360安全卫士中的断网急救箱工具修复&#xff0c;也就解决不了问题 4、重启系统&#xff0c;以网络安全模式启动系统&#xff0c;修复系统网络模块&#xff0c;完美解决问题 5、…

MySql 索引

一.索引作用 索引&#xff1a;提高数据库的性能&#xff0c;索引是物美价廉的东西了。不用加内存&#xff0c;不用改程序&#xff0c;不用调sql&#xff0c;只要执行正确的 create index &#xff0c;查询速度就可能提高成百上千倍。但是天下没有免费的午餐&#xff0c;查询速度…

Pikachu-Unsafe Fileupload-服务端check

MIME MIME是Multipurpose Internet Mail Extensions &#xff08;多用途互联网邮件扩展类型&#xff09;的缩写&#xff0c;用来表示文件、文档、或字节流的性质和格式。是设定某种扩展名的文件用一种应用程序来打开的方式类型&#xff0c;当该扩展名文件被访问的时候&#xff…