标题大小端字节序(联合体/共用体)
以上bilibili教程(大小端存储 - 详细简介)(空间也有)
目录
简介大小端存储(空间视频详解):
a.大小端的定义大端模式(Big - Endian):是指高位字节存放在内存的低地址端,低位字节存放在内存的高地址端。例如一个32位的整数0x12345678,在大端模式下,内存中的存储顺序是:0x12在最低地址处,然后是0x34,接着是0x56,最后0x78在最高地址处。这种存储方式和我们人类阅读数字的习惯比较相似,从高位到低位。
b.小端模式(Little - Endian):与大端模式相反,高位字节存放在内存的高地址端,低位字节存放在内存的低地址端。还是以0x12345678为例,在小端模式下,内存中的存储顺序是:0x78在最低地址处,然后是0x56,接着是0x34,最后0x12在最高地址处。这种存储方式在计算机系统中也很常见,比如x86架构的处理器就是采用小端模式。
方法一(易,这样的写法在面试要求中,显然相差云泥之别):
#include<iostream>
using namespace std;
int main() {
int a = 1;//int类型,在内存中的布局
if (1 == *(char*)(&a))//重点标记:强制转换char*
{
cout << "小端存储" << endl;
}
else {
cout << "大端存储" << endl;
}
return 0;
}
强制类型转换 - char*:
-
字节序(Endianness):
- 小端序(Little-Endian):低位字节存储在内存的低地址端,高位字节存储在内存的高地址端。
- 大端序(Big-Endian):高位字节存储在内存的低地址端,低位字节存储在内存的高地址端。
-
int
类型的内存布局:- 假设
int
类型占用 4 个字节,整数1
在内存中的表示为00000001 00000000 00000000 00000000
(以 32 位为例)。 - 在小端序系统中,内存布局为:
- 地址 0:
01
(低位字节) - 地址 1:
00
- 地址 2:
00
- 地址 3:
00
- 地址 0:
- 在大端序系统中,内存布局为:
- 地址 0:
00
- 地址 1:
00
- 地址 2:
00
- 地址 3:
01
(高位字节)
- 地址 0:
- 假设
-
转换为
char*
:char
类型的大小为 1 字节,因此将int
类型的地址转换为char*
类型后,可以逐字节访问int
类型变量的内存。- 通过
*(char*)(&a)
,我们访问了int
类型变量a
的第一个字节(即内存中的低地址字节)。
-
检查字节序:
- 如果
*(char*)(&a)
的值为1
,说明int
类型变量a
的第一个字节(低地址字节)是01
,这表明系统是小端序。 - 如果
*(char*)(&a)
的值为0
,说明int
类型变量a
的第一个字节(低地址字节)是00
,这表明系统是大端序。
- 如果
方法二(函数形式,地址传递)
#include<iostream>
using namespace std;
int check_sys(const int* pa)
{
return *(char*)(pa);
}
int main()
{
int a = 1;
int ret = check_sys(&a);
if (ret == 1) {
cout << "小端存储" << endl;
}
else {
cout << "大端存储" << endl;
}
return 0;
}
难度增加:
#include <iostream>
using namespace std;
unsigned char check_sys(const int* pa)
{
return *reinterpret_cast<const unsigned char*>(pa);
}
int main() {
int a;
cout << "请输入一个整数: ";
cin >> a;
unsigned char ret = check_sys(&a);
// 检查输入值的最低字节是否等于返回的字节
if (ret == (a & 0xFF)) //按位操作
{
cout << "小端存储" << endl;
}
else
{
cout << "大端存储" << endl;
}
return 0;
}
-
当 a 为随机输入值时:
- 判断大小端的方法需要稍作调整:关键在于检查 a 的最低有效字节(LSB)是否为 1。
如果 a 的值是 1,那么在小端存储中,最低地址的字节将是 1,而在大端存储中,最低地址的字节将是 0。
但是,如果 a 是一个随机值,我们需要检查 a 的最低有效字节是否为 1,而不是直接检查 a 是否等于 1。 - 检查最低有效字节:
a. a & 0xFF:这将提取 a 的最低有效字节。例如,如果 a 是 0xABCD1234,那么 a & 0xFF 的结果是 0x34。
b. check_sys(&a):调用 check_sys 函数,返回 a 的最低地址字节的值。
如果 check_sys(&a) 的返回值等于 a & 0xFF,则说明 a 的最低有效字节存储在最低地址,即小端存储。否则,说明 a 的最低有效字节存储在高地址,即大端存储。
- 判断大小端的方法需要稍作调整:关键在于检查 a 的最低有效字节(LSB)是否为 1。
-
运行示例
:假设用户输入 0xABCD1234:
a.小端存储:最低地址的字节是 0x34,a & 0xFF 也是 0x34,因此输出 "小端存储"。
b.大端存储:最低地址的字节是 0xAB,a & 0xFF 是 0x34,因此输出 "大端存储"。 -
按位操作的优点:
它通过直接操作数据的二进制位来实现各种功能。按位操作的优点主要体现在以下几个方面:
a. 效率高:
硬件层面:按位操作直接在硬件层面进行,通常比其他高级操作(如算术运算、逻辑运算等)更快。这是因为按位操作直接作用于寄存器中的位,不需要复杂的指令序列。
编译器优化:现代编译器通常会对按位操作进行高度优化,确保它们在运行时非常高效。
b. 内存使用高效:
紧凑的数据表示:按位操作可以将多个布尔值或标志位存储在一个整数中,从而节省内存。例如,一个 int 类型的变量可以存储 32 个布尔标志位。
减少内存访问:通过按位操作,可以在单个内存访问中处理多个标志位,减少内存访问次数,提高性能。
c. 控制硬件:
低级硬件操作:按位操作常用于低级硬件编程,如嵌入式系统、驱动程序开发等。通过直接操作寄存器的位,可以精确控制硬件设备的状态。
位掩码和位操作:可以使用位掩码(Bitmask)和位操作来设置、清除、翻转和检查特定的位,这对于硬件寄存器的控制非常有用。
d. 算法优化:
位运算技巧:按位操作可以用于实现一些高效的算法和数据结构,如位图(Bitmap)、布隆过滤器(Bloom Filter)等。快速幂运算:按位操作可以用于实现快速幂运算(Exponentiation by Squaring),在计算大数的幂时非常高效。
e. 数据压缩:
信息编码:按位操作可以用于数据压缩和编码,通过将多个小的数据值合并到一个整数中,减少存储空间。位字段:在结构体中使用位字段(Bit Fields)可以精确控制每个字段的位数,从而节省内存。
f. 加密和哈希:
位混合:按位操作常用于加密算法和哈希函数中,通过位混合(Bit Mixing)和位置换(Bit Permutation)来提高算法的安全性和均匀性。生成随机数:按位操作可以用于生成伪随机数,通过位操作生成具有特定统计特性的随机数序列。
g. 错误检测和纠正:
奇偶校验:按位操作可以用于生成和检查奇偶校验位,用于检测数据传输中的错误。CRC校验:循环冗余校验(CRC)算法中大量使用按位操作,用于检测数据的完整性。
h. 代码简洁性:
简洁的表达:按位操作可以将复杂的逻辑操作简化为几行代码,使代码更简洁、更易读。减少条件语句:通过按位操作,可以减少条件语句的使用,使代码更高效。
巧用联合体,解决百度面试题 - 大小端字节序问题:
(满分联合体,恰到好处):
#include<iostream>
using namespace std;
int check_sys()
{
/*
代码机理:
函数 check_sys:
在 check_sys 函数中,创建了一个 union Un 类型的对象 u。
将 u.i 设置为输入的整数 a。
返回 u.c,即 u.i 的第一个字节的值。
*/
union Un
/*
union Un 定义了一个联合体,包含两个成员:char c 和 int i。
这两个成员共享同一块内存。
*/
{
char c;
int i;
}u;//创建对象u
u.i = 1;
return u.c;
}
int main() {
int a = 1;
int ret = check_sys();
if (1 == ret) {
cout << "小端存储" << endl;
}
else {
cout << "大端存储" << endl;
}
return 0;
}
#include<iostream>
using namespace std;
int check_sys(int* pa)
{
union Un//(当然此处也可省略Un),形参匿名联合体类型
{
char c;
int i;
}u;
u.i = *pa; //使用传入的地址解引用得到所需值
return u.c;
}
int main() {
int a = 1;
cin >> a;
int ret = check_sys(&a);
if (ret == (a & 0xFF))//按位操作
{
cout << "小端存储" << endl;
}
else {
cout << "大端存储" << endl;
}
return 0;
}
使用联合体的优点:
使用联合体(union
)来检测系统的字节序(大小端)是一种非常巧妙且高效的方法。联合体的优点在于它的多个成员共享同一块内存,这意味着对任何一个成员的写操作都会影响到其他成员。这种特性使得联合体成为检测字节序的理想工具。
-
内存共享:
- 联合体的所有成员共享同一块内存。
- 这意味着当你修改其中一个成员时,其他成员的值也会相应改变。这使得联合体非常适合用于检查内存布局,如字节序检测。
-
简洁高效:
- 使用联合体可以非常简洁地实现字节序检测,无需复杂的指针操作或类型转换。
- 代码更易读、易懂。
-
类型安全:
- 虽然联合体允许多个不同类型的成员共享内存,但访问联合体的成员时,类型是明确的。
- 这避免了直接指针操作可能带来的类型安全问题。
-
无需外部输入:
- 检测字节序时,通常只需要一个固定的值。
- 联合体内部就可以完成所有操作,无需从外部获取输入。
-
总结
使用联合体来检测系统的字节序是一种非常简洁、高效且类型安全的方法。联合体的内存共享特性使得它非常适合用于这种类型的检测,无需复杂的指针操作或类型转换。通过上述示例,你可以看到联合体在字节序检测中的强大功能和简洁性。