大端小端和C实现大小端字节序的转化

大端小端

小端就是低位字节放在内存的低地址端,高位字节放在内存的高地址端。

大端就是高位字节放在内存的低地址端,低位字节放在内存的高地址端。

举一个例子,比如数字0x12 34 56 78(注意78才是低位字节)在内存中的表示形式为:

大端模式:

低地址 ——————>高地址

0x12 | 0x34 | 0x56 | 0x78

小端模式:

低地址 ——————>高地址(谐音,小弟弟,小低低,小端模式低位字节在低地址)

0x78 | 0x56 | 0x34 | 0x12

检测大端小端

借助联合体,我们可以检测 CPU 是大端模式还是小端模式,请看代码:

#include <stdio.h>
int main(void)
{
  union {int n;char ch;
  } data;
  data.n = 1;
  if (data.ch == 1) {printf("Little-endian\n");
  } else {printf("Big-endian\n");
  }
  return 0;
}

共用体的各个成员是共用一段内存的。

1 是数据的低位,如果 1 被存储在 data 的低字节,就是小端模式,这个时候 data.ch 的值也是 1。

如果 1 被存储在 data 的高字节,就是大端模式,这个时候 data.ch 的值就是 0。

一个例子

联合体的大小取决于最宽成员数据类型

typedef union {
  unsigned int a;
  unsigned int b;
} TEST_U1;

typedef union {
  unsigned int a;
  unsigned char b;
  unsigned short c;
} TEST_U2;

typedef struct {
  unsigned char a;
  unsigned char b;
  unsigned char c;
  unsigned char d;
} TEST_S3;

typedef union {
  unsigned int a;
  TEST_S3 b;
} TEST_U3;
// 小端模式
TEST_U1 u1;
TEST_U2 u2;
TEST_U3 u3;
u1.a = 0x12345678;
printf(“u1:a=%x,b=%x\r\n”, u1.a, u1.b);

在这里插入图片描述

u2.a = 0x12345678;
printf(“u2:b=%x,c=%x\r\n”, u2.b , u2.c);

在这里插入图片描述

u3.a = 0x12345678;
printf(“u3:ba=%x,bd=%x\r\n”, u3.b.a, u3.b.d);

在这里插入图片描述
联合体带冒号的说明:
在这里插入图片描述

ab.a本为1,但是只占1位,以%d输出时要转成int,扩展31位,int a所以有符号所以扩31个1,所以输出是-1

又一个例子

代码实例:

typedef struct {
  unsigned int data1 : 2;
  unsigned int data2 : 12;
  unsigned int data3 : 2
} TestData;

int main(void)
{
  TestData data;
  memset(&data, 0, sizeof(data));
  data.data1 = 0x7;
  data.data2 = 0x10;
  data.data3 = 0x1;
  unsigned int result = *(unsigned int *)&data;
  printf("value = 0x%.8x", result);
  return 0;
}

赋值语句data.data1 = 0x7 ,由于data1只占用2个bit(7=b111),因此这里溢出了,溢出的部分直接丢掉,等价于bit0和bit1都是1;

data.data2 =0x10 = b10000,即data2的第五个bit为1,其余bit为0,由于data2在整个结构体中占的bit是从第2bit开始到第14bit,这里等价于bit6为1其余为0

data.data3=0x1=b1,代表bit14为1

由于是小端结构,要从高地址往低地址读,result为int型的,共4个字节,

最高BYTE为bit31~bit24 =0,次高BYTE为bit23~bit16 = 0,之后的BYTE为bit15~bit8=b01000000=0x40 ,最低BYTE =b01000011=0x43

最后的结果则是0x4043
在这里插入图片描述
另一种读法如下:
在这里插入图片描述

C实现大小端字节序的转化

比如对32位int的转化,无非是以字节为单位对该32位int的4个字节进行首尾调换
而float可以根据与int共用联合体后,对联合体的int做转化。

#include <iostream>

using namespace std;

// 从低地址到高地址打印
void JdugeByteOrder(int *a)
{
    char *p = (char *)a;
    for (int i = 0; i < sizeof(int); i++) {
        int b = p[i];
        cout << hex << b << "\t";
    }
    cout << endl;
}

// 在原地址上进行对调来获取
void ExchangeByteOrder(int *pData)
{
    char *p = (char *)pData;
    for (int i = 0, j = sizeof(int) - 1; i < j; i++, j--) {
        int temp = p[i];
        p[i] = p[j];
        p[j] = temp;
    }
}

// 在新值地址上对调来获取 
int ExchangeByteOrder(int pData)
{
    char *p = (char *)&pData;
    for (int i = 0, j = sizeof(int) - 1; i < j; i++, j--) {
        int temp = p[i];
        p[i] = p[j];
        p[j] = temp;
    }
    return pData;
}

// 获取一个32位int类型转换后的新值
int swapInt32(int value)
{
	return ((value & 0x000000FF) << 24) |
			((value & 0x0000FF00) << 8) |
			((value & 0x00FF0000) >> 8) |
			((value & 0xFF000000) >> 24) ;
}

union intWithFloat {
	int m_i32;
	float m_f32;
};

// 获取一个32位float类型转换后的新值
float swapFloat32(float value)
{
    intWithFloat i;
    i.m_f32 = value;
    i.m_i32 = swapInt32(i.m_i32);
    return i.m_f32;
}

int main(void)
{
    int num = 0x12345678;
    //printf("%x\n", num);
    cout << hex << num << endl;
    printf("before ExchangeByteOrder, num:\n");
    JdugeByteOrder(&num);
    ExchangeByteOrder(&num);
    printf("after ExchangeByteOrder, num:\n");
    JdugeByteOrder(&num);
    printf("\n");
    
    num = 0x12345678;
    int res = ExchangeByteOrder(num);
    printf("res:\n");
    JdugeByteOrder(&res);
    printf("num:\n");
    JdugeByteOrder(&num);
    printf("\n");

    res = swapInt32(num);
    printf("res:\n");
    JdugeByteOrder(&res);

	return 0;
}
  • 1
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值