将char值移动24位

时间:2015-04-10 06:17:13

标签: c bit-shift

static unsigned int read24(unsigned char *ptr)
{
  unsigned char b0;
  unsigned char b1;
  unsigned int  b2;

  b0 = *ptr++;     //b0 = 00            
  b1 = *ptr++;     //b1 = AA
  b2 = *ptr;       //b2 = BB

  b0 = (b0 & 0x000000ff) ; 
  b1 = (b1 << 8) & 0x0000ff00);
  b2 = (b2 & 0x000000ff) << 16;

  return (b0 | b1 | b2) ;  
} 

代码2

static unsigned int read24(unsigned char *ptr)
{
  unsigned char b0;
  unsigned char b1;
  unsigned char b2;

  b0 = *ptr++;
  b1 = *ptr++;
  b2 = *ptr;

  return (
       ((b0 & 0x000000ff)) |
       ((b1 & 0x000000ff) << 8 |
       ((b2 << 16) & 0x00ff0000) |
     );
}

我对使用b1b2进行的转换操作感到困惑。将char b1移动8或大于8会使值为零吗? 什么是更好的策略,首先是转移然后转换或首先转移然后转移。我想要一个应该是BBAA00的返回值。我做对了吗?

如果我错了..第二个代码是否正确?

2 个答案:

答案 0 :(得分:4)

处理8次移位和16次移位的不对称性很奇怪,但只要sizeof(int) >= 2(假设为CHAR_BIT == 8)就无害。掩蔽有点奇怪;目的可能是确保在更大的类型上完成移位,这是保证的,因为操作数被转换为int。但是,分配回b0b1b2会撤消所做的任何好处; b0保持不变,其他两个归零。

代码应该更像是这样:

static unsigned int read24(unsigned char *ptr)
{
    unsigned char b0;
    unsigned char b1;
    unsigned int  b2;  // assert(sizeof(b2) >= 4);

    b0 = *ptr++;     //b0 = 00            
    b1 = *ptr++;     //b1 = AA
    b2 = *ptr;       //b2 = BB

    b2 <<= 16;
    b2  |= (b1 << 8) & 0xFF00;  // Mask only necessary if sizeof(int) == 2
    b2  |= b0;

    return  b2;  
}

完全可以写得更紧凑:

static unsigned int read24(unsigned char *ptr)
{
    unsigned char b0 = *ptr++;
    unsigned char b1 = *ptr++;
    unsigned int  b2 = *ptr;    // assert(sizeof(b2) >= 4);

    return (b2 << 16) | (b1 << 8) | b0;
}

代码2

您的第二个代码片段几乎是正确的,但返回表达式是错误的。它是:

return (
     ((b0 & 0x000000ff)) |
     ((b1 & 0x000000ff) << 8 |     // Missing )
     ((b2 << 16) & 0x00ff0000) |   // Extraneous |
   );

应该是:

static unsigned int read24(unsigned char *ptr)
{
    unsigned char b0 = *ptr++;
    unsigned char b1 = *ptr++;
    unsigned char b2 = *ptr;

    return (
        ((b0 & 0x00FF)) |
        ((b1 & 0x00FF) << 8) |
        ((b2 << 16) & 0x00FF0000));
}

返回表达式中有一个缺少的括号和一个无关的|。如果沿着这条路走下去,我会用:

static unsigned int read24(unsigned char *ptr)
{
    unsigned char b0 = *ptr++;
    unsigned char b1 = *ptr++;
    unsigned char b2 = *ptr;

    assert(sizeof(int) >= 4);

    return (
        ((b0 <<  0) & 0x0000FF) |
        ((b1 <<  8) & 0x00FF00) |       // 0xFF00 instead of 0x00FF!
        ((b2 << 16) & 0xFF0000));
}

这保留了对称性。当然,即使没有打开优化,<< 0也会优化为无操作。

b1 << 8未定义的行为?

由于当b1 << 8b1时,人们似乎认为unsigned char是未定义的行为,所以让我引用标准:

  

ISO / IEC 9899:2011§6.5.7转移运算符

     

¶3对每个操作数执行整数提升。结果的类型是提升的左操作数的类型。如果右操作数的值为负或大于或等于提升的左操作数的宽度,则行为未定义。

     

¶4E1 << E2的结果是E1左移E2位位置;腾出的位充满了   零。如果E1具有无符号类型,则结果的值为E1 × 2 E2 ,减少模数   比结果类型中可表示的最大值多一个。如果E1已签名   类型和非负值,E1 × 2 E2 在结果类型中可表示,那么   结果价值;否则,行为未定义。

因此,unsigned char会在转变发生之前升级为int(因此我对sizeof(int) >= 2发表评论)。如果sizeof(int) == 2,则在b1中左移一个0x80..0xFF范围内的值会导致未定义的行为(UB)。您必须决定风险是否可以接受,或者在转移之前将b1强制转换为unsigned

(unsigned)b1 << 8

在原始代码中:

b1 = (b1 << 8) & 0x0000ff00);

(不编译:它应该是b1 = (b1 << 8) & 0x0000ff00;!),是造成损害的分配(将结果保留为零),而不是转移。

答案 1 :(得分:0)

在位移位期间,操作数被提升为int。所以8位左移工作正常。但是当我们从char将其分配回int时,它会被截断为1个字节(char的大小为1个字节,因此如果您将移位8位,则该值将变为0 )。因此,请将b1的数据类型更改为int

代码1:没有作业

static unsigned int read24(unsigned char *ptr)
{
unsigned char b0;
unsigned char b1;
unsigned int b2;

b0 = *ptr++;     //b0 = 00            
b1 = *ptr++;     //b1 = AA
b2 = *ptr;       //b2 = BB

return (b0 | (b1<<8) | (b2<<16)) ;  
} 

代码2:带作业

static unsigned int read24(unsigned char *ptr)
{
unsigned char b0;
unsigned int b1;
unsigned int b2;

b0 = *ptr++;     //b0 = 00            
b1 = *ptr++;     //b1 = AA
b2 = *ptr;       //b2 = BB

b0 = (b0 & 0x000000ff) ; 
b1 = (b1 << 8) & 0x0000ff00);
b2 = (b2 & 0x000000ff) << 16;

return (b0 | b1 | b2) ;  


}