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) |
);
}
我对使用b1
和b2
进行的转换操作感到困惑。将char b1
移动8或大于8会使值为零吗?
什么是更好的策略,首先是转移然后转换或首先转移然后转移。我想要一个应该是BBAA00的返回值。我做对了吗?
如果我错了..第二个代码是否正确?
答案 0 :(得分:4)
处理8次移位和16次移位的不对称性很奇怪,但只要sizeof(int) >= 2
(假设为CHAR_BIT == 8
)就无害。掩蔽有点奇怪;目的可能是确保在更大的类型上完成移位,这是保证的,因为操作数被转换为int
。但是,分配回b0
,b1
和b2
会撤消所做的任何好处; 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;
}
您的第二个代码片段几乎是正确的,但返回表达式是错误的。它是:
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 << 8
是b1
时,人们似乎认为unsigned char
是未定义的行为,所以让我引用标准:
ISO / IEC 9899:2011§6.5.7转移运算符
¶3对每个操作数执行整数提升。结果的类型是提升的左操作数的类型。如果右操作数的值为负或大于或等于提升的左操作数的宽度,则行为未定义。
¶4
E1 << 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) ;
}