Reputation: 527
When declaring or using in an artithmetic expression an integer
constant expression in C of a type that’s defined in
stdint.h,
for instance uint64_t
, one could cast the integer to the desired
type (uint64_t)x
, or use a Macro for Integer Constant
Expressions
such as UINT64_C(x)
(where x
is an integer constant expression).
I’m more enclined to use the macro, however I’m wondering in what cases the two approaches are equivalent, differ, and what could go wrong. More precisely: is there a case where using one would lead to a bug, but not with the other?
Thanks!
Upvotes: 2
Views: 228
Reputation: 2792
Keep it simple and make everything clear and obvious to the reader. I.e. avoid the preprocessor as much as possible, and only introduce a cast where absolutely necessary.
Upvotes: -1
Reputation: 141574
An esoteric but possible case: If the system has no 64-bit type (e.g. 32-bit int
, 128-bit long
), then (uint64_t)1
will fail to compile, whereas UINT64_C(1)
will give something of the smallest unsigned integer type larger than 64 bits.
The macro forms are likely to expand to suffixes rather than casts. But I can't think of any other situation where a conforming program would behave differently (other than the syntax precedence issue of course).
If the program is non-conforming then there are various possibilities, e.g. UINT64_C(-1)
is undefined behaviour no diagnostic required, as is UINT8_C(256)
. The macro argument must be an unsuffixed integer constant that is in range for the target type.
Upvotes: 2
Reputation: 180201
More precisely: is there a case where using one would lead to a bug, but not with the other?
Yes, there are such cases, though they are rather contrived. Unary operators such as cast operators have high precedence, but all the postfix operators have higher. Of those, the indexing operator, []
, can be applied to an integer constant when the expression inside is a pointer to a complete type. Thus, given this declaration in scope:
int a[4] = { 1, 2, 3, 4 };
... the expression (uint64_t) 1[a]
evaluates to a uint64_t
with value 2, whereas the expression UINT64_C(1)[a]
evaluates to an int
with value 2. The type difference can cause different behavior to manifest. That can arise from different implicit conversion behavior, which is generally a subtle effect, or if these are used as control expressions for a generic selection then the overall expression can evaluate to wildly different things depending on which variation you use.
However, I think there is no practical difference if you put the cast expression in parentheses: ((uint64_t) 1)
.
Upvotes: 3