#c #visual-c #visual-studio-2015 #x86-64
# #c #visual-c #visual-studio-2015 #x86-64
Вопрос:
Это функция, которую я (хочу) использовать для декодирования чисел из буферов без знака 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? Я полагаю, что это не так. Но тогда как мне это исправить?
Ответ №1:
Нет, ты не можешь игнорировать это. Операнд buffer[i]
имеет тип unsigned char
, который, вероятно, повышен до int
(а если нет int
, то unsigned int
). Если 56 больше или равно разрядности int
, то сдвиг равен UB.
Вам нужно записать static_cast<unsigned long long>(buffer[0]) << 56
и так далее, чтобы операнд был не менее 64 бит задолго до сдвига.
Комментарии:
1. я полагаю, что в данном случае статические слепки предположительно лучше, чем обычные.
2. @blipman17 Это вопрос мнения, но многие люди рекомендуют избегать приведений в стиле C на C , в то время как мало кто найдет какие-либо недостатки в использовании приведения в стиле C , даже если они предпочитают приведение в стиле C.
3. не могу заставить это работать с вашими предложенными правками. Тот же результат.
Ответ №2:
При использовании в выражении unsigned char
значения повышаются до int
s . Очевидно, что попытка сдвинуть an int
на 56 бит будет не очень продуктивной.
Вы должны быть более явными:
unsigned long long number= ((unsigned long long)buffer[0] << 56);
number|= ((unsigned long long)buffer[1] << 48);
….. и так далее. Вы должны принудительно unsigned long long
выполнить преобразование до того, как произойдет операция сдвига.
Комментарии:
1. Продвижение чисел при сдвиге битов. Что вы думаете об этом поведении? Когда это было бы полезно?
2. Это было бы полезно, когда вам нужно получить правильный результат.
3. Да, но почему бы автоматически не привести его к типу, с которым он будет взаимодействовать? в этом случае беззнаковый long long? «просто» преобразовать его в (неподписанный) int звучит для меня как ошибка.
4. Потому что он не «взаимодействует» с an
unsigned long long
. Другим значением, используемым<<
оператором, является anint
.
Ответ №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;
}
пример вывода ассемблером неподписанной версии 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
Комментарии:
1. Это звучит интересно наверняка. Я не уверен, что полностью понимаю вас как ученика c , но я посмотрю на эффективную разницу для себя позже в тот же день.
Ответ №4:
измените код следующим образом:
//unsigned long long number= (buffer[0] << 56);
unsigned long long number= ((buffer[0] << 31) << 25);