3

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.

Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335
Alex Lop.
  • 6,810
  • 1
  • 26
  • 45
  • 1
    _My concern is that the compiler may treat 0xFFFFFFFF as integer value of -1_ , why would the compiler do that? – David Ranieri Aug 21 '19 at 14:40
  • @DavidRanieri Because constant numbers by default have type of `int` and in 2th complementary, 0xFFFFFFFF is -1 (for `int`s) – Alex Lop. Aug 21 '19 at 14:42
  • @DavidRanieri because technically all integer constants are signed (with caveats) unless suffixed with a `u` – Mgetz Aug 21 '19 at 14:43
  • @Mgetz Not true. All *decimal* integer constants are signed unless otherwise specified with a suffix, but *octal* or *hexadecimal* constants can be unsigned. In this particular case, it would be unsigned, as per the C standard included in Vlad's answer. – Christian Gibbons Aug 21 '19 at 14:46
  • Technically, there is no such thing as a negative integer constant. – Ian Abbott Aug 21 '19 at 14:53
  • @ChristianGibbons those are the caveats, but that's implementation dependent and not guaranteed in all cases. However the core of it is that in almost all cases it's not worth worrying about because of how all integer constants, even decimals, are positive by default and negation is applied afterwards. – Mgetz Aug 21 '19 at 14:57
  • Just cast it to `unsigned` if you're worried about it. – S.S. Anne Aug 21 '19 at 15:03
  • @Mgetz The issue I have is that this question is specifically about hexadecimal representation, so sweeping that under the rug as a "caveat" isn't very helpful and potentially misleading as it pertains to this question. I will agree, though, that the specific type isn't as important to the OP's issue so much as the fact that integer constants are always positive. – Christian Gibbons Aug 21 '19 at 15:16

4 Answers4

8

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.

Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335
  • 2
    It could have type `int` if `int` is more than 32 bits wide. – Ian Abbott Aug 21 '19 at 14:50
  • @IanAbbott The size of int can not be greater than the size of unsigned int.:) – Vlad from Moscow Aug 21 '19 at 14:58
  • True, but who says size 4 means 32 bits? – Ian Abbott Aug 21 '19 at 15:02
  • @IanAbbott We do not deep into details about that some implementations can have padding bits.:) – Vlad from Moscow Aug 21 '19 at 15:06
  • I wasn't referring to the padding bits. :) E.g. you could have a 9-bit `char` and a 36-bit `int`. – Ian Abbott Aug 21 '19 at 15:09
  • Hmm, I want to agree with @IanAbbott, who is technically correct (best kind of correct) that if CHAR_BIT is not 8, then it could still be an `int`, even if `unsigned int` has sizeof 4. But on the other hand, if `CHAR_BIT` is 9, then you wouldn't have `uint64_t` available, which is the type of `g_global_var`, therefore it can be surmised that `CHAR_BIT` is not, in fact, 9. – Christian Gibbons Aug 21 '19 at 15:22
  • @ChristianGibbons That's a good point, but my point would still apply if `CHAR_BIT` is 16 and `unsigned int` is 64 bits wide. – Ian Abbott Aug 21 '19 at 15:25
  • @IanAbbott Agreed. That is why I restricted my comment to the example of `CHAR_BIT` being 9 rather than specifying that `CHAR_BIT` must therefore be 8 making it a non-factor. – Christian Gibbons Aug 21 '19 at 15:37
  • @ChristianGibbons Understood. :) – Ian Abbott Aug 21 '19 at 15:44
3

0xffffffff is not -1, ever. It may convert to -1 if you cast or coerce (e.g. by assignment) it to a signed 32-bit type, but integer literals in C always have their mathematical value unless they overflow.

For decimal literals, the type is the narrowest signed type that can represent the value. For hex literals, unsigned types are used before going up to the next wider signed type. So, in the common case where int is 32-bit, 0xffffffff would have type unsigned int. If you wrote it as decimal, it would have type long (if long is 64-bit) or long long (if long is only 32-bit).

R.. GitHub STOP HELPING ICE
  • 208,859
  • 35
  • 376
  • 711
1

The type of an unsuffixed hexadecimal or octal constant is the first of the following list in which its value can be represented:

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

(For unsuffixed decimal constants, remove the unsigned types from the above list.)

The hexadecimal constant 0xFFFFFFFF can definitely be represented by unsigned long int, so its type will be the first of int, unsigned int, long int or unsigned long int that can represent its value.

Note that although 0xFFFFFFFF > 0 always evaluates to 1 (true), it is possible for 0xFFFFFFFF > -1 to evaluate to either 0 (false) or 1 (true) on different implementations. So you need to be careful when comparing integer constants with each other or with other objects of integer type.

Ian Abbott
  • 15,083
  • 19
  • 33
0

Others have answered the question, just a recomendation, next time (if you are under C11) you can check the type of the expression by yourself using _Generic

#include <stdio.h>
#include <stdint.h>

#define print_type(x) _Generic((x), \
    int64_t:  puts("int64_t"),      \
    uint64_t: puts("uint64_t"),     \
    default:  puts("unknown")       \
)

uint64_t g_global_var;

int main(void)
{
    print_type(g_global_var & 0xFFFFFFFF);
    return 0;
}

The ouput is

uint64_t
David Ranieri
  • 39,972
  • 7
  • 52
  • 94