C语言移位操作遇到的问题和解决办法

        最近在调试一个Camera ISP OTP校准的问题,在开发过程中,要将2个字节的数据组合成16bit的数据。一开始我以为只要是一大块内存,我们告诉它类型,那么编译器就会自动分配对应的内容。例如:下面图1中连续的内容,加入这里我把uint8_t temp[4] = {0x20,0x33,0x44,0x55},现在我想把它放到一个结构类型的数据中,假如这里数据结构是:

struct test{
   uint16_t   a;
   uint16_t   b;
}

这里我们有struct test  *arm = (struct test *)test;这样一行代码,大家猜猜arm中对应的a,b域分别是多少。一开始我以为就是arm->a = 0x2033,arm->b = 0x4455;

                                                                                                                                                    

                                                                                            实际存放状态                                                                                             目的状态

      经过实际代码验证,由于存在大小端问题,它会将低地址的数据放到高位,高地址的数据放到低位。如下试验代码

试验代码1

#include<stdio.h>
unsigned char temp[] = {0x20,0x33,0x44,0x55};
struct test{
   unsigned short a;
   unsigned short b;
};

int main(){
   struct test * arm = (struct test *)temp;
   printf("A:0x%x\n",arm->a);
   printf("B:0x%x\n",arm->b);
}
打印结果:看到结果的瞬间,大家都明白了吧。

A:0x3320
B:0x5544
实验代码2

实验2重要研究移位操作对赋值的影响,如下面代码标识出的。

#include<stdio.h>

typedef struct {
	unsigned short a;
	unsigned short b;
	unsigned short c;
}data_test;

unsigned char temp[6]={0x11,0x22,0x33,0x44,0x55,0x66};

void test(unsigned char *in){
	data_test dta;
	unsigned char *in_data = in;
	dta.a = (*in_data & 0xff) <<8  + (*(in_data+1)&0xff);     //---------------------
	dta.b = (*(in_data+2) & 0xff) <<8  + (*(in_data+3)&0xff); //---------------------
	dta.c = (*(in_data+4) & 0xff) <<8  + (*(in_data+5)&0xff); //---------------------

	printf("armwind,0x%x\n",dta.a);
	printf("armwind,0x%x\n",dta.b);
	printf("armwind,0x%x\n",dta.c);

	int i;
	for(i=0;i<6;i++){
		printf("hehe:0x%x\n",*(in_data +i));
	}
}
int main(){
	test(temp);
	printf("char:%d\n",sizeof(unsigned char));
	printf("short:%d\n",sizeof(unsigned short));
}
运行结果:

armwind,0x4400
armwind,0x3000
armwind,0x4000
hehe:0x11
hehe:0x22
hehe:0x33
hehe:0x44
hehe:0x55
hehe:0x66
char:1
short:2

上面的代码很简单,就是有3行非常不解,字面上看没什么错误,但为什么打出来的结果和我们预期的,有这么大差异呢。为此我将代码反汇编了,在汇编代码中我找到了原因。请看汇编代码片段。

	dta.a = (*in_data & 0xff) <<8  + (*(in_data+1)&0xff);     
	dta.b = (*(in_data+2) & 0xff) <<8  + (*(in_data+3)&0xff); 
	dta.c = (*(in_data+4) & 0xff) <<8  + (*(in_data+5)&0xff); 
汇编代码片段:

void test(unsigned char *in){
  4004f4:	55                   	push   %rbp
  4004f5:	48 89 e5             	mov    %rsp,%rbp
  4004f8:	53                   	push   %rbx
  4004f9:	48 83 ec 38          	sub    $0x38,%rsp
  4004fd:	48 89 7d c8          	mov    %rdi,-0x38(%rbp) //这里申请临时变量堆栈,用来
	data_test dta;
	unsigned char *in_data = in;
  400501:	48 8b 45 c8          	mov    -0x38(%rbp),%rax 
  400505:	48 89 45 d8          	mov    %rax,-0x28(%rbp) //*in_data = in;
	dta.a = (*in_data & 0xff) <<8  + (*(in_data+1)&0xff);
  400509:	48 8b 45 d8          	mov    -0x28(%rbp),%rax     //取到in_data指针
  40050d:	0f b6 00             	movzbl (%rax),%eax             //拿到*in_data值
  400510:	0f b6 d0             	movzbl %al,%edx                  //;(*in_data)&0xff  取低8位 --------------------------------------->前面一半计算的结果在edx中。
  400513:	48 8b 45 d8          	mov    -0x28(%rbp),%rax    //开始(*(in_data+1)&0xff),这是拿到in_data指针。
  400517:	48 83 c0 01          	add    $0x1,%rax                   //in_data 进行+1操作
  40051b:	0f b6 00             	movzbl (%rax),%eax             //;(*(in_data +1))
  40051e:	0f b6 c0             	movzbl %al,%eax                  //(*(in_data+1)&0xff)计算的结果取低8位,放到eax,高位补0.
  400521:	83 c0 08             	add    $0x8,%eax                   //;这里直接就是将eax+8了,这里可以移位的数量,之前eax存放的是高8位数据------------>这个地方就出现问题了。不应该直接加eax。
  400524:	89 d3                	mov    %edx,%ebx                //;ebx中存放的是高8位数据。
  400526:	89 c1                	mov    %eax,%ecx                 //
  400528:	d3 e3                	shl    %cl,%ebx                      //;高8位数据逻辑左移动cl个寄存器。
  40052a:	 89 d8                	mov    %ebx,%eax
  40052c:	66 89 45 e0          	mov    %ax,-0x20(%rbp)
	dta.b = (*(in_data+2) & 0xff) <<8  + (*(in_data+3)&0xff);
  400530:	48 8b 45 d8          	mov    -0x28(%rbp),%rax
  400534:	48 83 c0 02          	add    $0x2,%rax
  400538:	0f b6 00             	movzbl (%rax),%eax
  40053b:	0f b6 d0             	movzbl %al,%edx
  40053e:	48 8b 45 d8          	mov    -0x28(%rbp),%rax
  400542:	48 83 c0 03          	add    $0x3,%rax 
  400546:	0f b6 00             	movzbl (%rax),%eax
  400549:	0f b6 c0             	movzbl %al,%eax
  40054c:	83 c0 08             	add    $0x8,%eax //同理,这里 (*(in_data+3)&0xff) + 8操作
  40054f:	89 d3                	mov    %edx,%ebx
  400551:	89 c1                	mov    %eax,%ecx
  400553:	d3 e3                	shl    %cl,%ebx
  400555:	89 d8                	mov    %ebx,%eax
  400557:	66 89 45 e2          	mov    %ax,-0x1e(%rbp)
	dta.c = (*(in_data+4) & 0xff) <<8  + (*(in_data+5)&0xff);
  40055b:	48 8b 45 d8          	mov    -0x28(%rbp),%rax
  40055f:	48 83 c0 04          	add    $0x4,%rax
  400563:	0f b6 00             	movzbl (%rax),%eax
  400566:	0f b6 d0             	movzbl %al,%edx
  400569:	48 8b 45 d8          	mov    -0x28(%rbp),%rax
  40056d:	48 83 c0 05          	add    $0x5,%rax
  400571:	0f b6 00             	movzbl (%rax),%eax
  400574:	0f b6 c0             	movzbl %al,%eax
  400577:	83 c0 08             	add    $0x8,%eax //同理,这里 (*(in_data+5)&0xff) + 8操作,这是无效的。
  40057a:	89 d3                	mov    %edx,%ebx
  40057c:	89 c1                	mov    %eax,%ecx
  40057e:	d3 e3                	shl    %cl,%ebx
  400580:	89 d8                	mov    %ebx,%eax
  400582:	66 89 45 e4          	mov    %ax,-0x1c(%rbp)
...................
总结:以后在将高八位,低八位进行拼接的时候,最好使用一个中间变量,要不然编译器会直接舍弃调低8位,导致运算失效。切记,切记!!!!

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值