【内存分配】04.大小端,字节对齐
【嵌入式八股】一、语言篇(本专栏)https://www.nowcoder.com/creation/manager/columnDetail/mwQPeM
【嵌入式八股】二、计算机基础篇https://www.nowcoder.com/creation/manager/columnDetail/Mg5Lym
【嵌入式八股】三、硬件篇https://www.nowcoder.com/creation/manager/columnDetail/MRVDlM
【嵌入式八股】四、嵌入式Linux篇https://www.nowcoder.com/creation/manager/columnDetail/MQ2bb0
大小端
60.大小端基础
内存中的大小端模式
大小端是指在多字节的数据类型中,不同字节的存储顺序。大端是指高位字节存储在低地址,而小端是指低位字节存储在低地址。
int a = 0x12345678; //(gdb) p/t a
//(gdb) x &a 查看内存值
char *b =(unsigned char *)&a; //通过地址 强转为char型,让指针 指向字节地址
printf("%x %x %x %x",*(b+3),*(b+2),*(b+1),*b);
例如:32bit的数字0x12345678
- 小端存储:字数据的低字节存储在低地址中
- 大端存储:字数据的高字节存储在低地址中
通信系统中的大小端模式 如要通过串口发送一个0x12345678给接收方,但是因为串口本身限制,只能以字节为单位来发送,所以需要发4次;接收方分4次接收,内容分别是:0x12、0x34、0x56、0x78接收方接收到这4个字节之后需要去重组得到0x12345678(而不是得到0x78563412)
所以在通信双方需要有一个默契,就是:先发/先接的是高位还是低位?这就是通信中的大小端问题。
- 一般来说是:先发低字节叫小端;先发高字节就叫大端。实际操作中,在通信协议里面会去定义大小端,明确告诉你先发的是低字节还是高字节(看接口协议里的各种协议)。
在通信协议中,大小端是非常重要的,大家使用别人定义的通信协议还是自己要去定义通信协议,一定都要注意标明通信协议中大小端的问题。
61.大小端各自的优点是什么?
不同大小端的优点如下:
大端:
- 易于人类阅读和理解。在阅读16进制数时,大端存储的字节顺序与我们自然阅读的顺序是一致的。
- 网络字节序使用的是大端,因此在进行网络通信时,使用大端存储可以避免字节序的转换(Socket编程中)。
小端:
- 小端在内存中的布局与寄存器的读写顺序一致,因此在进行位运算和数据处理时,可以更快地访问数据。
- 小端与x86架构兼容,因此在x86架构下,使用小端存储可以直接访问存储在内存中的数据,无需进行转换,提高了性能。
62.如何用代码判断大小端存储?
了解了大小端存储的方式,如何在代码中进行判断呢?下面介绍两种判断方式:
方式一:使用强制类型转换
#include <iostream>
using namespace std;
int main()
{
int a = 0x1234;
//由于int和char的长度不同,借助int型转换成char型,只会留下低地址的部分
char c = (char)(a);
if (c == 0x12)
cout << "big endian" << endl;
else if(c == 0x34)
cout << "little endian" << endl;
}
方式二:巧用union联合体
#include <iostream>
using namespace std;
//union联合体的重叠式存储,endian联合体占用内存的空间为每个成员字节长度的最大值
union endian
{
int a;
char ch;
};
int main()
{
endian value;
value.a = 0x1234;
//a和ch共用4字节的内存空间
if (value.ch == 0x12)
cout << "big endian"<<endl;
else if (value.ch == 0x34)
cout << "little endian"<<endl;
}
63.传输协议发送接收两边大小端不一致怎么办
当发送和接收两端的大小端(即字节序)不一致时,需要进行字节序转换,以确保数据在传输过程中能够正确解析。
字节序指的是数据在内存中存储的顺序。有两种常见的字节序:大端序(Big Endian)和小端序(Little Endian)。在大端序中,高位字节(最高有效字节)存储在低位地址,而低位字节(最低有效字节)存储在高位地址。而在小端序中,高位字节存储在高位地址,低位字节存储在低位地址。
下面是一种常见的处理方法,用于在大小端不一致的情况下进行字节序转换:
- 首先,将要发送或接收的数据按照一种确定的字节序(通常选择大端序或小端序)进行打包或解包。这意味着将数据从内部表示形式转换为字节流或从字节流转换回内部表示形式。
- 在发送端,如果发送方和接收方的字节序不一致,发送方需要将数据从自己的字节序转换为接收方的字节序。这可以通过将数据从发送方的字节序转换为网络字节序(通常是大端序)来实现。可以使用相关的字节序转换函数或手动处理字节序转换。
- 在接收端,接收方需要将接收到的数据从网络字节序转换为自己的字节序。这可以通过将数据从网络字节序转换为接收方的字节序(可能是大端序或小端序)来实现。同样,可以使用相关的字节序转换函数或手动处理字节序转换。
64.大小端转化:对一个输入的整型数进行大小端存储模式转化
位域大小端转换方法有哪些? - 知乎 (zhihu.com)
大小端转化
**思路:**大小端转化就是将一个整型数的低字节放到高字节,高字节放到低字节,跟前面的位翻转类似,只不过这里的单位是字节,因此需要将位翻转中的&0x01改为&0xFF,<< bit改为size * 8,>>= 1改为 >> 8。
int endian_convert(int input)
{
int result = 0;
int size = sizeof(input);
while(size--)
{
result |= ((input & 0xFF) << (size * 8));
input >>= 8;
}
return result;
}
65.指针偏移 + 大小端
- 例1
#include <stdio.h>
int main()
{
int a = 0x12345678;
int b = 0x0A0B0C0D;
// 局部变量 会顺序分配地址:
//(gdb) x &a
// 0x7fffffffdf78: 0x12345678
//(gdb) x &b
// 0x7fffffffdf7c: 0x0a0b0c0d
//因是小端 故内存为
//内存: 高 0x0A0B0C0D 0x12345678 低
unsigned char *c=( unsigned char *)&a; //小端 a 地址指向低字节 78
printf("%x ",*(c+1)); // 地址加1后取内容 为0x56
printf("%x ",*(int*)(c+1)); //强转为int后再取内容 为0x0D123456
return 0;
}
- 例2
//问:下题输出信息是?
int a[5]={1,2,3,4,5};
int *ptr=(int *)(&a+1);
//&a:取数组的地址。
//&a+1:下一个对象的地址(对象是 数组类型,长度单位 是 4*5,偏移1个数组的大小),
//指针的偏移: 是以它指向的对象的数据类型的长度为偏移的单位
// 指针的类型就像人的身份,你是以个人,还是老板的身份
// 不同的身份,你的贷款额度不一样
//(int *): 把指向数组类型的地址,强转为指向int型的地址
//int 型是4 ,double型是8 数组型是数组长度。
printf("%d,%d",*(a+1),*(ptr-1));
//a: 数组名,是数组首个元素的地址,指向的数据类型是元素的类型 int
//a+1: 下一元素的地址,即a[1] (对象是 int类型,长度单位是 4)
//*(a+1): a地址偏移1个单位(int 是4),再取里面内容(int型), 是2
//ptr: 是指向int型的指针
//ptr-1: 地址减1个单位(int型是4)
//*(ptr-1): 取里面内容(int型) 是5
- 例3:指针偏移 + 类型转换 + 大小端
//问:下面程序,变量a的内存地址为:0x0034fc50 问printf输出值是多少
int main()
{
int a[5] = { 1, 2, 3, 4, 5 };
int *p1
剩余60%内容,订阅专栏后可继续查看/也可单篇购买
查阅整理上千份嵌入式面经,将相关资料汇集于此,主要包括: 0.简历面试 1.语言篇【本专栏】 2.计算机基础 3.硬件篇 4.嵌入式Linux (建议PC端查看)