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

来源:互联网 发布:松下网络摄像机软件 编辑:程序博客网 时间:2024/06/14 15:26

        最近在调试一个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:0x3320B: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,0x4400armwind,0x3000armwind,0x4000hehe:0x11hehe:0x22hehe:0x33hehe:0x44hehe:0x55hehe:0x66char:1short: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位,导致运算失效。切记,切记!!!!

0 0
原创粉丝点击