Search code examples
cbit-manipulation64-bitimplicit-conversionbitmask

Safe low 32 bits masking of uint64_t


Assume the following code:

uint64_t g_global_var;

....
....

void foo(void)
{
    uint64_t local_32bit_low = g_global_var & 0xFFFFFFFF;
    ....
}

With the current toolchain, this code works as expected, local_32bit_low indeed contains the low 32 bits of g_global_var.

I wonder if it is guaranteed by the standard C that this code will always work as expected? My concern is that the compiler may treat 0xFFFFFFFF as integer value of -1 and when promoting to uint64_t it would become 0xFFFFFFFFFFFFFFFF.

P.S.

I know that to be on the safe side it is better to use 0xFFFFFFFFULL in this case. The point is that I saw it in a legacy code and I wonder if it worth to be fixed or not.


Solution

  • There is no problem. The integer constant 0xFFFFFFFF has the type that is able to store the value as is.

    According to the C Standard (6.4.4.1 Integer constants)

    5 The type of an integer constant is the first of the corresponding list in which its value can be represented

    So this value is stored as a positive value.

    If the type unsigned int is a 32-bit integer type then the constant will have the type unsigned int.

    Otherwise it will have one of the types that can store the value.

    long int
    unsigned long int
    long long int
    unsigned long long int 
    

    Due to the usual arithmetic conversions in the expression

    g_global_var & 0xFFFFFFFF;
    

    it is promoted like

    0x00000000FFFFFFFF
    

    Pay attention to that in C there is no negative integer constants. For example an expression like

    -10
    

    consists of two sub-expressions: the primary expression 10 and the sub-expression with the unary operator - -19 that coincides with the full expression.