bitshifting unsigned char 和 unsigned long long 出错了
bitshifting unsigned char and unsigned long long gone wrong
这是我(想要)用来解码 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“<<”:移位计数为负数或太大,未定义的行为”四次,用于最高位移位;
这是一个我可以放心忽略的警告,因为编译器无法识别我使用的是无符号 64 位 int 吗?我想不是。但是我该如何解决这个问题?
在表达式中使用时,unsigned char
值被提升为 int
。显然,尝试将 int
移动 56 位的效率不是很高。
你必须更明确:
unsigned long long number= ((unsigned long long)buffer[0] << 56);
number|= ((unsigned long long)buffer[1] << 48);
...等等。在进行移位操作之前,您必须强制转换为 unsigned long long
。
不,你不能忽视它。操作数 buffer[i]
的类型为 unsigned char
,它可能被提升为 int
(如果不是 int
,则 unsigned int
)。如果56大于等于int
的位宽,则移位为UB
你需要写 static_cast<unsigned long long>(buffer[0]) << 56
等等,所以操作数至少要有 64 位长 before 移位。
我认为最好明确说明意图并让编译器的优化器发挥其魔力:
#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
像这样更改代码:
//unsigned long long number= (buffer[0] << 56);
unsigned long long number= ((buffer[0] << 31) << 25);
这是我(想要)用来解码 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“<<”:移位计数为负数或太大,未定义的行为”四次,用于最高位移位; 这是一个我可以放心忽略的警告,因为编译器无法识别我使用的是无符号 64 位 int 吗?我想不是。但是我该如何解决这个问题?
在表达式中使用时,unsigned char
值被提升为 int
。显然,尝试将 int
移动 56 位的效率不是很高。
你必须更明确:
unsigned long long number= ((unsigned long long)buffer[0] << 56);
number|= ((unsigned long long)buffer[1] << 48);
...等等。在进行移位操作之前,您必须强制转换为 unsigned long long
。
不,你不能忽视它。操作数 buffer[i]
的类型为 unsigned char
,它可能被提升为 int
(如果不是 int
,则 unsigned int
)。如果56大于等于int
的位宽,则移位为UB
你需要写 static_cast<unsigned long long>(buffer[0]) << 56
等等,所以操作数至少要有 64 位长 before 移位。
我认为最好明确说明意图并让编译器的优化器发挥其魔力:
#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
像这样更改代码:
//unsigned long long number= (buffer[0] << 56);
unsigned long long number= ((buffer[0] << 31) << 25);