Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why must I cast a `uint8_t` to `uint64_t` *before* left-shifting it?

Tags:

c++

I just want to concatenate my uint8_t array to uint64_t. In fact, I solved my problem but need to understand the reason. Here is my code;

    uint8_t byte_array[5];

    byte_array[0] = 0x41;
    byte_array[1] = 0x42;
    byte_array[2] = 0x43;
    byte_array[3] = 0x44;
    byte_array[4] = 0x45;

    cout << "index 0: " << byte_array[0] << " index 1: " << byte_array[1] << " index 2: " << byte_array[2] << " index 3: " << byte_array[3] << " index 4: " << byte_array[4] << endl;

    /* This does not work */
    uint64_t reverse_of_value = (byte_array[0] & 0xff) | ((byte_array[1] & 0xff) << 8) | ((byte_array[2] & 0xff) << 16) | ((byte_array[3] & 0xff) << 24) | ((byte_array[4] & 0xff) << 32);

    cout << reverse_of_value << endl;

    /* this works fine */
    reverse_of_value = (uint64_t)(byte_array[0] & 0xff) | ((uint64_t)(byte_array[1] & 0xff) << 8) | ((uint64_t)(byte_array[2] & 0xff) << 16) | ((uint64_t)(byte_array[3] & 0xff) << 24) | ((uint64_t)(byte_array[4] & 0xff) << 32);

    cout << reverse_of_value << endl;

The first output will be "44434245" and second one will be "4544434241" that is what I want.

So as we see when I use casting each byte to uint64_t code works, however, if I do not use casting it gives me irrelevant result. Can anybody explain the reason?

like image 380
ergin Avatar asked Aug 26 '15 14:08

ergin


2 Answers

Left-shifting a uint8_t that many bits isn't necessarily going to work. The left-hand operand will be promoted to int, whose width you don't know. It could already be 64-bit, but it could be 32-bit or even 16-bit, in which case… where would the result go? There isn't enough room for it! It doesn't matter that your code later puts the result into a uint64_t: the expression is evaluated in isolation.

You've correctly fixed that in your second version, by converting to uint64_t before the left-shift takes place. In this situation, the expression will assuredly have the desired behaviour.

like image 170
Lightness Races in Orbit Avatar answered Jan 14 '23 21:01

Lightness Races in Orbit


Here is an example showing left-shift turning the char to 0. At least it does so on my machine, gcc 4.8.4, Ubuntu 14.04 LTS, x86_64.

#include <iostream>

using std::cout;

int main()
{
    unsigned char ch;

    ch = 0xFF;

    cout << "Char before shift: " << static_cast<int>(ch) << '\n';
    ch <<= 10;

    cout << "Char after shift: " << static_cast<int>(ch) << '\n';
}

Note also my comment to the original question above, on some platforms, the 0x45 shifted 32 bits actually ends up in the least significant byte of the 64-bit value.

like image 36
Erik Alapää Avatar answered Jan 14 '23 22:01

Erik Alapää