c++ - предупреждение: количество сдвигов вправо ›= ширина типа на 32-битной машине

У меня есть следующая функция:

void func(unsigned long v)
{
  char max_byte = 0xFF;
  char buffer[8];

  buffer[0] = static_cast<char>((v)       & max_byte);
  buffer[1] = static_cast<char>((v >> 8)  & max_byte);
  buffer[2] = static_cast<char>((v >> 16) & max_byte);
  buffer[3] = static_cast<char>((v >> 24) & max_byte);
  buffer[4] = static_cast<char>((v >> 32) & max_byte);
  buffer[5] = static_cast<char>((v >> 40) & max_byte);
  buffer[6] = static_cast<char>((v >> 48) & max_byte);
  buffer[7] = static_cast<char>((v >> 56) & max_byte);
}

которая принимает аргумент unsigned long и вставляет его 8 байтов в буфер char (не пытайтесь понять почему. Это краткая версия значимой функции).

Этот код хорошо компилируется на 64-битной, но на 32-битной я получаю следующее предупреждение:

warning: right shift count >= width of type

ссылаясь на строки:

  buffer[4] = static_cast<char>((v >> 32) & max_byte);
  buffer[5] = static_cast<char>((v >> 40) & max_byte);
  buffer[6] = static_cast<char>((v >> 48) & max_byte);
  buffer[7] = static_cast<char>((v >> 56) & max_byte);

Я думаю, что понимаю предупреждение, но я не уверен, что мне делать, чтобы иметь возможность плавно скомпилировать его и на 32-битной версии.


person idanshmu    schedule 09.05.2014    source источник
comment
Да, это неопределенное поведение для выполнения сдвига с шириной, превышающей или равной числу битов.   -  person Shafik Yaghmour    schedule 09.05.2014
comment
Какова длина unsigned long в 32-битной системе?   -  person Codor    schedule 09.05.2014
comment
@ShafikYaghmour Я это понимаю, но каково решение. unsigned long имеет длину 64 бита, и мне нужно правильно его обработать.   -  person idanshmu    schedule 09.05.2014
comment
Поговорим об операторах сдвига ‹‹, ›› - viva64.com/en/b/0142< /а>   -  person    schedule 09.05.2014


Ответы (4)


unsigned long гарантированно имеет только 32 бита. См. здесь. Вам нужно использовать unsigned long long, чтобы гарантировать 64 бита.

Еще лучше было бы использовать целое число фиксированной ширины, то есть uint64_t. Они определены в заголовке <cstdint> (или <stdint.h>).

person Danvil    schedule 09.05.2014
comment
также в более ранних версиях C++ - только если ваша библиотека/компилятор имеет нестандартные расширения. Они не были стандартными до C++11. - person Mike Seymour; 09.05.2014

Используйте целочисленные типы фиксированной ширины. В этом случае вам нужно std::uint64_t.

person ecatmur    schedule 09.05.2014
comment
Или, по крайней мере, попробуйте, реализация не требует их предоставления :) Но да, это решение. - person David Rodríguez - dribeas; 09.05.2014

При написании кода, который зависит от целочисленного размера, вам действительно нужно использовать <stdint.h>.

#include <stdint.h>

void func(uint64_t v)
{
  static const uint8_t max_byte = 0xFF; // Let the compiler hardcode this constant.
  uint8_t buffer[8];

  buffer[0] = static_cast<uint8_t>((v)       & max_byte);
  buffer[1] = static_cast<uint8_t>((v >> 8)  & max_byte);
  buffer[2] = static_cast<uint8_t>((v >> 16) & max_byte);
  buffer[3] = static_cast<uint8_t>((v >> 24) & max_byte);
  buffer[4] = static_cast<uint8_t>((v >> 32) & max_byte);
  buffer[5] = static_cast<uint8_t>((v >> 40) & max_byte);
  buffer[6] = static_cast<uint8_t>((v >> 48) & max_byte);
  buffer[7] = static_cast<uint8_t>((v >> 56) & max_byte);
}
person Mike DeSimone    schedule 09.05.2014
comment
Сегодня не мой день. Модем потерял связь, когда я пытался опубликовать сообщение. ›_‹ - person Mike DeSimone; 09.05.2014

Я могу ошибаться в предположении, что ни один из ответов не соответствует сути этого вопроса, поэтому здесь.

При компиляции в виде 64-битного двоичного файла long определяется как 64-битное значение (или 8 байтов), тогда как 32-битный двоичный файл a long соответствует типу int, который составляет 32 бита или 4 байта.

Есть несколько решений проблемы:
1. переопределить параметр на long long или int64, как предлагается в других ответах.
2. добавить определение препроцессора, чтобы заблокировать ошибочные битовые операции. Например...

#ifdef __LP64__
buffer[4] = static_cast<uint8_t>((v >> 32) & max_byte);
buffer[5] = static_cast<uint8_t>((v >> 40) & max_byte);
buffer[6] = static_cast<uint8_t>((v >> 48) & max_byte);
buffer[7] = static_cast<uint8_t>((v >> 56) & max_byte);
#endif


Это гарантирует, что тип long обрабатывается в соответствии с архитектурой процессора, а не означает, что тип long всегда должен быть 64-битным.
3. Использование объединения приведет к тому же конечному результату для предоставленного кода.

void func(unsigned long v)
{
    union {
        unsigned long long  ival;
        unsigned char       cval[8];
    } a;

    a.ival = v;
}

конечно, если вы используете С++, вы можете хранить любой базовый тип данных аналогичным образом, изменив приведенное выше следующим образом:

template<class I>
void func(I v) {
    union {
        unsigned long long  ival;
        unsigned char       cval[8];
        I                   val;
    } a;

    a.val = v;
}
person Gary Barnes    schedule 26.06.2014