移位 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&lt;unsigned long long&gt;(buffer[0]) &lt;&lt; 56等等,所以操作数在移位之前至少有64位长。

【讨论】:

我想,在这种情况下,静态转换应该比常规转换更好。 @blipman17 这是一个见仁见智的问题,但许多人建议在 C++ 中避免使用 C 风格的强制转换,而即使他们更喜欢 C 风格的强制转换,也很少有人会发现使用 C++ 风格的强制转换有任何问题. 无法使其适用于您建议的修改。结果相同。【参考方案2】:

在表达式中使用时,unsigned char 值将提升为 ints。尝试将 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“交互”。 &lt;&lt; 运算符使用的另一个值是 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 长期出错的主要内容,如果未能解决你的问题,请参考以下文章

C语言的补码表示和unsigned及signed的的转换

unsigned char 和 signed char

unsigned和signed char

unsigned char 和 signed char 区别

unsigned char 与 char 有啥却别?何时适用

什么是unsigned char;;?