34

Does C treat hexadecimal constants (e.g. 0x23FE) as signed or unsigned integers?

wovano
  • 4,543
  • 5
  • 22
  • 49
Amr Bekhit
  • 4,613
  • 8
  • 34
  • 56
  • Possible duplicate of [Type of integer literals not int by default?](https://stackoverflow.com/questions/8108642/type-of-integer-literals-not-int-by-default) – phuclv Sep 13 '18 at 02:09
  • Related: [Why are decimal and hexadecimal integer literals treated differently?](https://stackoverflow.com/q/36560143) quotes the C99 rationale for why there's a difference in type between decimal and hex constants for the same value (hex can pick an unsigned type). – Peter Cordes Aug 31 '23 at 23:36

3 Answers3

32

The number itself is always interpreted as a non-negative number. Hexadecimal constants don't have a sign or any inherent way to express a negative number. The type of the constant is the first one of these which can represent their value:

int
unsigned int
long int
unsigned long int
long long int
unsigned long long int
CB Bailey
  • 755,051
  • 104
  • 632
  • 656
  • 2
    Note that as a consequence, 0x8000 may be either signed or unsigned depending on whether sizeof(int) is 2 or 4. Yuck! Just append `u` if you really need `unsigned`. – anatolyg Jan 19 '11 at 17:15
  • 2
    @anatolyg: I'm not sure what you mean by "yuck". It will always be positive and it will always convert to the correct value if assigned or promoted to another type where the value is still in range which seems like fairly sensible and desirable behaviour to me. – CB Bailey Jan 19 '11 at 17:18
  • 2
    @anatolyg: But `0x8000` isn't negative. Either it can fit in an `int`, in which case `0x8000 > 0x7000` is done as a comparison of `int`, otherwise `0x8000` is an `unsigned` and `0x7000` is promoted to `unsigned` (no change of value) and the comparison is a comparison of `unsigned`. Either way the result is true. – CB Bailey Jan 19 '11 at 20:35
  • 2
    Decimal and octal constants don't have a sign either - if you write `-1`, you're writing a unary `-` followed by a decimal constant `1`. In @anatolyg's example, `if (MYSIZE > -1)` could produce surprising results, since the `-1` may or may not be promoted to unsigned. – caf Jan 20 '11 at 00:20
  • 1
    @caf: `0x8000 > -1` is a much better example of where care definitely is needed. – CB Bailey Jan 20 '11 at 07:50
  • 1
    @CBBailey: Regarding "Hexadecimal constants don't have a sign or any inherent way to express a negative number" - Can you provide a reliable source/reference which mentions/documents this? Does that mean that Hexadecimals number system doesn't support negative numbers? – Cheshar Jul 18 '18 at 15:32
  • 1
    It's not true that hex literals don't have a sign. They do. You can qualify `0x23FEU` if you want unsigned, but `0x23FE` is signed. – Alex Apr 02 '20 at 12:09
  • 1
    @Alex, what the answer tries to say is that there is no sign (i.e. no "minus") in `0x23FE`. The literal `0x23FE` is always interpreted as a non-negative integer number. However, when using it in an expression, the _type_ of the expression may become signed or unsigned (which by the way does not mean it becomes negative). Maybe the second sentence of this answer should be changed to "Hexadecimal _literals_ do not have a sign..." to make this more clear. – wovano May 10 '23 at 13:37
15

It treats them as int literals(basically, as signed int!). To write an unsigned literal just add u at the end:

0x23FEu
Khaled Alshaya
  • 94,250
  • 39
  • 176
  • 234
  • 5
    I don't think that you can leave that statement as such. E.g provided that the width of `int` is 32 bit the value `0x8000` is `unsigned` (namely `INT_MAX + 1`) and not `signed` (and `INT_MIN`). – Jens Gustedt Jan 19 '11 at 16:40
  • 3
    @JensGustedt: Presumably you mean that if the width of `int` is **16** bit then `0x8000` will be `unsigned`? – CB Bailey Jan 19 '11 at 16:43
  • 1
    @Charles, probably. Counting bits myself never was my strength :) – Jens Gustedt Jan 19 '11 at 16:56
  • 2
    @Alex, no. A hexadecimal value is `int` as long as the value fits into `int` and for larger values it is `unsigned`, then `long`, then `unsigned long` etc. See Section 6.4.4.1 of the C standard. Just as the accepted answer states. – Jens Gustedt Apr 02 '20 at 13:26
4

According to cppreference, the type of the hexadecimal literal is the first type in the following list in which the value can fit.

int
unsigned int
long int
unsigned long int
long long int(since C99)
unsigned long long int(since C99) 

So it depends on how big your number is. If your number is smaller than INT_MAX, then it is of type int. If your number is greater than INT_MAX but smaller than UINT_MAX, it is of type unsigned int, and so forth.

Since 0x23FE is smaller than INT_MAX(which is 0x7FFF or greater), it is of type int.

If you want it to be unsigned, add a u at the end of the number: 0x23FEu.

Searene
  • 25,920
  • 39
  • 129
  • 186
  • This answer appears to pertain to C++, not C. It may be correct, but I think the reference link should be updated to point to the C reference, not the C++ reference. – Resigned June 2023 Sep 13 '18 at 00:25
  • 3
    @RadonRosborough I've updated the answer to use the C reference and checked the other parts of it to make sure the answer was all about C. Thanks for pointing it out. – Searene Sep 13 '18 at 00:49