移位 unsigned char 和 unsigned long long 长期出错
Posted
技术标签:
【中文标题】移位 unsigned char 和 unsigned long long 长期出错【英文标题】:bitshifting unsigned char and unsigned long long gone wrong 【发布时间】:2016-10-13 21:54:24 【问题描述】:这是我(想要)用来解码 unsigned char[] 缓冲区中的数字以用于网络的函数。
inline unsigned long long getULongLongLongInt(const unsigned char* buffer)
unsigned long long number= (buffer[0] << 56);
number|= (buffer[1] << 48);
number|= (buffer[2] << 40);
number|= (buffer[3] << 32);
number|= (buffer[4] << 24);
number|= (buffer[5] << 16);
number|= (buffer[6] << 8);
number|= buffer[7];
return number;
对于最高位移位,我收到警告 C4293 '
【问题讨论】:
【参考方案1】:不,你不能忽视它。操作数buffer[i]
的类型为unsigned char
,它可能被提升为int
(如果不是int
,则为unsigned int
)。如果56大于等于int
的位宽,则移位为UB。
你需要写static_cast<unsigned long long>(buffer[0]) << 56
等等,所以操作数在移位之前至少有64位长。
【讨论】:
我想,在这种情况下,静态转换应该比常规转换更好。 @blipman17 这是一个见仁见智的问题,但许多人建议在 C++ 中避免使用 C 风格的强制转换,而即使他们更喜欢 C 风格的强制转换,也很少有人会发现使用 C++ 风格的强制转换有任何问题. 无法使其适用于您建议的修改。结果相同。【参考方案2】:在表达式中使用时,unsigned char
值将提升为 int
s。尝试将 int
移动 56 位显然不会很有成效。
你必须更明确:
unsigned long long number= ((unsigned long long)buffer[0] << 56);
number|= ((unsigned long long)buffer[1] << 48);
... 等等。在移位操作发生之前,您必须强制转换为 unsigned long long
。
【讨论】:
在移位时推广数字。对这种行为有什么想法?什么时候有用? 当你需要得到正确的结果时会很有用。 是的,但为什么不自动将其转换为将与之交互的类型呢?在这种情况下无符号长长? “只是”将其转换为(无符号)int 对我来说听起来像是一个错误。 因为它不与unsigned long long
“交互”。 <<
运算符使用的另一个值是 int
。【参考方案3】:
我认为最好明确说明意图并让编译器的优化器发挥其魔力:
#include <cstdint>
#include <utility>
template<class Unsigned,
std::enable_if_t<std::is_unsigned<Unsigned>::value>* = nullptr
>
inline Unsigned decode_int_msb(const unsigned char* buffer)
//
// some helpful types and constants
//
using type = Unsigned;
static constexpr auto bytes = sizeof(type);
static constexpr auto bits = bytes * 8;
//
// a helpful local function
//
auto byte = [buffer](auto i)
return type(buffer[i]) << bits - ((i+1) * 8);
;
//
// simplified algorithm here
//
type acc = 0;
for(std::size_t i = 0 ; i < bytes ; ++i)
acc += byte(i);
return acc;
int main(int argc, char** argv)
// force expansion of some templates
auto x = decode_int_msb<unsigned long long>(reinterpret_cast<const unsigned char*>(argv[0]));
auto y = decode_int_msb<unsigned long>(reinterpret_cast<const unsigned char*>(argv[0]));
auto z = decode_int_msb<unsigned short>(reinterpret_cast<const unsigned char*>(argv[0]));
return x + y + z;
unsigned long long 版本的示例汇编器输出(未内联时):
unsigned long decode_int_msb<unsigned long, (void*)0>(unsigned char const*):
movzx ecx, BYTE PTR [rdi+1]
movzx edx, BYTE PTR [rdi+2]
movzx eax, BYTE PTR [rdi+3]
mov rsi, rcx
sal rdx, 40
sal rsi, 48
sal rax, 32
lea rcx, [rsi+rdx]
lea rdx, [rcx+rax]
movzx eax, BYTE PTR [rdi]
sal rax, 56
add rax, rdx
movzx edx, BYTE PTR [rdi+4]
sal rdx, 24
add rdx, rax
movzx eax, BYTE PTR [rdi+5]
sal rax, 16
add rdx, rax
movzx eax, BYTE PTR [rdi+6]
sal rax, 8
add rax, rdx
movzx edx, BYTE PTR [rdi+7]
add rax, rdx
ret
【讨论】:
这听起来确实很有趣。作为一名 C++ 学徒,我不确定我是否完全理解你,但我会在当天晚些时候为自己看看有效的区别。【参考方案4】:像这样更改代码:
//unsigned long long number= (buffer[0] << 56);
unsigned long long number= ((buffer[0] << 31) << 25);
【讨论】:
以上是关于移位 unsigned char 和 unsigned long long 长期出错的主要内容,如果未能解决你的问题,请参考以下文章
unsigned char 和 signed char 区别