C语言获取一个整型的各个字节的方法和字节内各位的倒置

下面是一篇好文:C语言 | 获取一个整数各个字节的两种方法

题目:获取0x12345678各个字节。

方法一:结构&联合

#include <stdio.h>
 
typedef unsigned int  uint32_t;
typedef unsigned char uint8_t;
 
union bit32_data
{
    uint32_t data;
    struct 
    {
        uint8_t byte0;
        uint8_t byte1;
        uint8_t byte2;
        uint8_t byte3;
    }byte;
};
 
int main(void)
{
    union bit32_data num;
    
    num.data = 0x12345678;
 
    printf("byte0 = 0x%x\n", num.byte.byte0);
    printf("byte1 = 0x%x\n", num.byte.byte1);
    printf("byte2 = 0x%x\n", num.byte.byte2);
    printf("byte3 = 0x%x\n", num.byte.byte3);
 
    return 0;
}
————————————————
版权声明:本文为CSDN博主「嵌入式大杂烩」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/zhengnianli/article/details/111189379

运行结果:

https://raw.githubusercontent.com/xkyvvv/blogpic/main/pic1/image-20210717003231662.png

这种方法的缺点是:会受到大小端的影响。只有在明确大小端的情况下可稍微用一用。以上是小端模式下的运行结果。

结构&联合法不仅仅是在获取数据各个字节有用,在数据协议、数据位拆分等方面很实用,之后再做更详细的分享。大小端推荐文章:《CPU 的大小端模式?》

方法二:移位操作

#include <stdio.h>
 
#define GET_LOW_BYTE0(x) ((x >>  0) & 0x000000ff) /* 获取第0个字节 */
#define GET_LOW_BYTE1(x) ((x >>  8) & 0x000000ff) /* 获取第1个字节 */
#define GET_LOW_BYTE2(x) ((x >> 16) & 0x000000ff) /* 获取第2个字节 */
#define GET_LOW_BYTE3(x) ((x >> 24) & 0x000000ff) /* 获取第3个字节 */
 
int main(void)
{
 unsigned int a = 0x12345678;
 
 printf("byte0 = 0x%x\n", GET_LOW_BYTE0(a));
 printf("byte1 = 0x%x\n", GET_LOW_BYTE1(a));
 printf("byte2 = 0x%x\n", GET_LOW_BYTE2(a));
 printf("byte3 = 0x%x\n", GET_LOW_BYTE3(a));
 return 0;
}

运行结果:

image-20210717003333718

这也是获取数据各个字节的最常用也最有效的方法。这种类似的位操作在嵌入式方面应用得很多,比如在LCD操作中表示像素颜色值、FLASH的操作等方面都有用到。位操作推荐文章:《C语言、嵌入式位操作精华技巧大汇总》

可以使用下面的方法来获取一个整型的各个字节

#define GET_LOW_BYTE0(x) ((x >>  0) & 0x000000ff) /* 获取第0个字节 */
#define GET_LOW_BYTE1(x) ((x >>  8) & 0x000000ff) /* 获取第1个字节 */
#define GET_LOW_BYTE2(x) ((x >> 16) & 0x000000ff) /* 获取第2个字节 */
#define GET_LOW_BYTE3(x) ((x >> 24) & 0x000000ff) /* 获取第3个字节 */

那么在获取了一个整型的各个字节之后,如果我们想要对其中的一个字节进行位的旋转

一个字节内的位翻转既是

LSB(11001110)MSB 位翻转之后 LSB(01110011)MSB

先看一段代码

#include <stdio.h>

void printf_bin_8(unsigned char num)
{
    int k;
    unsigned char *p = (unsigned char*)&num;

    for (int k = 7; k >= 0; k--) //处理8个位
    {
        if (*p & (1 << k))
            printf("1");
        else
            printf("0");
    }
    printf("\r\n");
}

int main() {
	int a = 99;
	int b = 0b01100011;
	
	printf_bin_8(b<<8);//00000000
	printf_bin_8(b<<7);//10000000
	printf_bin_8(b<<6);//11000000
	printf_bin_8(b<<5);//01100000
	printf_bin_8(b<<4);//00110000
	printf_bin_8(b<<3);//00011000
	printf_bin_8(b<<2);//10001100
	printf_bin_8(b<<1);//11000110
	printf_bin_8(b<<0);//01100011
	
	printf_bin_8(b>>8);//00000000
	printf_bin_8(b>>7);//00000000
	printf_bin_8(b>>6);//00000001
	printf_bin_8(b>>5);//00000011
	printf_bin_8(b>>4);//00000110
	printf_bin_8(b>>3);//00001100
	printf_bin_8(b>>2);//00011000
	printf_bin_8(b>>1);//00110001
	printf_bin_8(b>>0);//01100011
}

https://raw.githubusercontent.com/xkyvvv/blogpic/main/pic1/image-20210717005409803.png

在我之前的一篇文章中C语言位操作详解(全网最全)

高低位交换

对一个字节数据,逐个交换其高低位,例如11010001,经过0-7,1-6,2-5,3-4对应位的交换,变成10001011 。

对于该问题,我们最先想到的是对原字节通过移位操作来逐位处理,使用另一个变量来存储交换后的结果。这种解决方案处理起来思路清晰,编写代码应该不难。

unsigned char shift_fun1(unsigned char data)
{
	unsigned char i;
	unsigned char tmp=0x00;
	for(i=0;i<8;i++)
	{
		tmp=((data>>i)&0x01)|tmp;
		if(i<7)
			tmp=tmp<<1;
	}
	printf("  after shift fun1 data=%x \n",tmp);
	return tmp;
}

上述代码实现起来不难,而且效率还是比较高的。但是还有比这更简洁的解决方法,在嵌入式开发中遇到交换字节位的问题时通常使用蝶式交换法和查表法来实现。查表法顾名思义即将一些值存到内存中,需要计算时查表即可,但是也会占用额外的存储空间。这里主要再介绍一下蝶式交换法。

所谓的蝶式交换是这样的:

data=(data<<4)|(data>>4);
data=((data<<2)&0xcc)|((data>>2)&0x33);
data=((data<<1)&0xaa)|((data>>1)&0x55);

我们可以做一下执行演算:
假设原始位序列为 0 1 0 1 1 0 0 1

data=(data<<4)|(data>>4);之后序列为 1 0 0 1 0 1 0 1

data=((data<<2)&0xcc)|((data>>2)&0x33); 之后序列为 0 1 1 0 0 1 0 1
data=((data<<1)&0xaa)|((data>>1)&0x55); 之后序列为 1 0 0 1 1 0 1 0

更抽象的来说 原始位为 1 2 3 4 5 6 7 8

data=(data<<4)|(data>>4); 之后位序为 5 6 7 8 1 2 3 4
data=((data<<2)&0xcc)|((data>>2)&0x33); 之后位序为 7 8 5 6 3 4 1 2

data=((data<<1)&0xaa)|((data>>1)&0x55); 之后位序为 8 7 6 5 4 3 2 1

由此完成了整个位的逆序转换,下面是具体的实现代码:

unsigned char shift_fun2(unsigned char data)
{
data=(data<<4)|(data>>4);
data=((data<<2)&0xcc)|((data>>2)&0x33);
data=((data<<1)&0xaa)|((data>>1)&0x55);
printf(" after shift fun2 data=%x \n",data);
return data;
}
因此我们可以用下面的代码来测试

#include <stdio.h>

void printf_bin_8(unsigned char num)
{
    int k;
    unsigned char *p = (unsigned char*)&num;

    for (int k = 7; k >= 0; k--) //处理8个位
    {
        if (*p & (1 << k))
            printf("1");
        else
            printf("0");
    }
    printf("\r\n");
}

unsigned char shift_fun1(unsigned char data)
{
	unsigned char i;
	unsigned char tmp=0x00;
	for(i=0;i<8;i++)
	{
		tmp=((data>>i)&0x01)|tmp;![在这里插入图片描述](https://raw.githubusercontent.com/xkyvvv/blogpic/main/pic1/image-20210717014245993.png#pic_center)

		if(i<7)
			tmp=tmp<<1;
	}
	return tmp;
}

int main() {
	int a = 99;
	int b = 0b01100011;

	printf_bin_8(shift_fun1(b));
}

在这里插入图片描述

可以看到,字节内的位翻转成功了。


最后我们将上面的获取一个整型的字节和翻转一个字节内的位结合起来。

#include <stdio.h>

#define GET_LOW_BYTE0(x) ((x >>  0) & 0x000000ff) /* 获取第0个字节 */
#define GET_LOW_BYTE1(x) ((x >>  8) & 0x000000ff) /* 获取第1个字节 */
#define GET_LOW_BYTE2(x) ((x >> 16) & 0x000000ff) /* 获取第2个字节 */
#define GET_LOW_BYTE3(x) ((x >> 24) & 0x000000ff) /* 获取第3个字节 */

void printf_bin_8(unsigned char num)
{
    int k;
    unsigned char *p = (unsigned char*)&num;

    for (int k = 7; k >= 0; k--) //处理8个位
    {
        if (*p & (1 << k))
            printf("1");
        else
            printf("0");
    }
    printf("\r\n");
}

unsigned char shift_fun1(unsigned char data)
{
	unsigned char i;
	unsigned char tmp=0x00;
	for(i=0;i<8;i++)
	{
		tmp=((data>>i)&0x01)|tmp;
		if(i<7)
			tmp=tmp<<1;
	}
	return tmp;
}

int main() {
	int a = 0b11111111111111111111111101100011;
	unsigned char b = GET_LOW_BYTE0(a);
	printf_bin_8(shift_fun1(b));
}

https://raw.githubusercontent.com/xkyvvv/blogpic/main/pic1/image-20210717014809138.png

其变化过程如下

对0b11111111111111111111111101100011取其第一个字节,得到0b01100011,再对0b01100011字节内的每一位翻转得到0b11000110

  • 5
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小熊coder

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值