3
votes

http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf 7.20.4 introduces Macro integer constants with:

1 The following function-like macros expand to integer constants suitable for initializing objects that have integer types corresponding to types defined in <stdint.h>. Each macro name corresponds to a similar type name in 7.20.1.2 or 7.20.1.5.

I don't quite understand this paragraph. The macros basically slap the appropriate suffix onto an unsuffixed number as in:

UINT64_C(0x123) => 0x123ULL

But if I wanted to initializes an uint64_t, I would just do:

uint64_t x = 0x123; 

and I wouldn't bother with the suffix at all.

Why would I need these macros in initializations?

2
I think these macros were introduced just to duct tape over the language defect which is that there are no integer literal suffix for the stdint.h types. Kind of like the format specifiers for printf in inttypes.h.Lundin

2 Answers

3
votes

this UINT64_C(0x123) macro creates an immediate unsigned long long number so it can be used in variable argument functions for instance or intermediate computations without the need for casting into the uint64_t type, where it is important that this particular data type is used.

Example:

printf("%llu\n",UINT64_C(0x123));

is correct

printf("%llu\n",0x123);

is incorrect and is UB because data size isn't correct, and printf cannot know that.

when you do uint64_t x = 0x123;, there's an assignment and an implicit cast, so no need to do this (and printf("%llu\n",x); is correct)

Another usage is in intermediate computations, as illustrated below:

uint32_t a = 0xFFFFFFFF;
uint64_t x = a + UINT64_C(0xFFFFFFFF);

won't overflow whereas

x = a + 0xFFFFFFFF;

will overflow because intermediate result is stored in a uint32_t

As a conclusion, the main functional difference between UINT64_C(SOME_CONSTANT) and (uint64_t)SOME_CONSTANT is that if the value overflows, you'll get a warning in the first case for sure, and a "conversion" in the other case (and maybe a warning but that depends on the compiler).

0
votes

With u64 you tell that you want 64-bit integer. Some platforms provide it by defining unsigned long long, others by unsigned long. If your values are to interoperate with functions that need this info (e.g. printf and pals), you should deal with this indirection in order to have flexible code. In your example (uint64_t ex = 0x123;) type has this information, no need to explicitly call the macro. But I assume you need all the 64-bit, if it exceeds int values, you should have the UL / ULL postfices. Example:

unsigned ex = 0x100000000U;
// warning: large integer implicitly truncated to unsigned type [-Woverflow]