Which initializer is appropriate for an int64_t?
Asked Answered
M

3

14

I like to initialize my variables to some "dummy" value and have started to use int64_t and uint64_t. So far, it looks like there are at least three ways I could initialize an int64_t to a particular value (and with slight changes for the unsigned equivalent):

int64_t method_one   = 0;
int64_t method_two   = 0LL;
int64_t method_three = INT64_C(0);

I use GCC and target OS X and Linux. I'd like to pick a method that aims for ease of portability and clarity — but correctness, above all. Am I overthinking this, or is there a "best" or "most recommended" approach for initializing this variable type, for any particular value I throw at it (which is within its bounds, of course)?

Microgram answered 7/11, 2012 at 17:39 Comment(5)
have you looked at the assembly ? does any of those actually make any difference ?Bashful
No, I have not looked at the assembly. Wouldn't this be case-specific, though?Microgram
Yes it would, but the compiler will most likely be following some standard, I was asking out of curiosity anyway.Bashful
@mux: Correct me if I am wrong, the actual bit-representation between (signed) 0 and and unsigned(0); isn't it the same?Sidneysidoma
@AdelQodmani if it's two's complement, then yes, but I meant to check if the asm would be generated to zero the whole 64bits or not.Bashful
E
9
int64_t method_one   = 0;

...is perfectly reasonable. C99 (see e.g. draft here; yes, I know it's not the most recent standard any more, but it's the one that introduced the int<N>_t types) says that:

  • the 0 has type int (§6.4.4.1 para.5);
  • the type of the expression is int64_t (§6.5.16 para.3);
  • the type of the right-hand side will be converted to the type of the expression (§6.5.16.1 para.2);
  • this conversion will not change the value (§6.3.1.3 para.1).

So there's nothing wrong with that at all, and the lack of additional clutter makes it the most readable of the options when initialising to 0 or anything else in the range of an int.

int64_t method_two   = 0LL;

int64_t is not guaranteed to be the same as long long; however, this should in fact work portably for any signed 64-bit value as well (and similarly ULL for unsigned 64-bit values): long long (and unsigned long long) should be at least 64 bits in a C99-compliant implementation (§5.2.4.2.1), so LL (and ULL) should always be safe for initialising 64-bit values.

int64_t method_three = INT64_C(0);

This is arguably a better option for values which may be outside the range of an int, as it expresses the intent more clearly: INT64_C(n) will expand to something appropriate for any n in (at least) a 64-bit range (see §7.18 in general, and particularly §7.8.4.1).


In practice, I might well use any of the above, depending on context. For example:

uint64_t counter = 0;

(Why add unnecessary clutter?)

uint64_t some_bit = 1ULL << 40;

(1 << 40 simply won't work unless int is unusually wide; and UINT64_C(1) << 40 seems less readable to me here.)

uint64_t some_mask = UINT64_C(0xFF00FF00FF00FF00);

(In this case, explicitly calling out the value as a 64-bit constant seems more readable to me than writing 0xFF00FF00FF00FF00ULL.)

Ethelind answered 8/11, 2012 at 0:39 Comment(1)
Thanks, this is a much more detailed answer. I am now using C99 (in order to get int64_t support) and I seem to like the use of INT64_C() over the other two options, if only because it is so explicit, while the first doesn't work with literals outside the range of an int, and the second seems to imply a reliance on long long (and its unsigned equivalent) being exactly 64 bits wide, which is an assumption I would prefer to avoid. This brings my concerns and thoughts into clarity. Thanks again.Microgram
C
7

Personnally, I would use the third, which is the most portable way to achieve this.

#include <stdint.h>

int64_t method_three  = INT64_C(0);
uint64_t method_three = UINT64_C(0);

Anyway, I don't think it's a very important thing.

Carroll answered 7/11, 2012 at 17:51 Comment(2)
+1 for this. The whole point of the INT64_C and UINT64_C casting macros is to be portable. Some might try to argue that it isn't important for 64-bit values (they'll always be long long int!), but nothing says that int64_t has to be a long long int.Oxpecker
Question: What about int64_t method_four{}?Cooperstein
B
0

According to the ANSI C standard, the suffix for a long long int and unsigned long long int is LL and ULL respectively:

octal or hexadecimal suffixed by ll or LL long long int, unsigned long long int decimal, octal, or hexadecimal suffixed by both u or U, and ll or LL unsigned long long int

If you know that int64_t is defined as:

typedef signed long long int int64_t

Then method two is most definitely the correct one:

int64_t method_two   = 0LL;
uint64_t method_two   = 0ULL;

Edit:

Keeping in mind the portability issues, and the fact that it's not guaranteed to be defined as long long, then it would be better to use the third method:

INT64_C()
UINT64_C()
Bashful answered 7/11, 2012 at 17:58 Comment(2)
There are no guarantees that int64_t is long long. If there were, we wouldn't need the typedef.Mendelssohn
I remember when (1980s) a big number was 16 bits. I like the uint64_t etc. as defined in stdint.h. What's a long or long long historically changes over time so I avoid them. You can always do a quick sizeof() check if you have to use them.Ebbie

© 2022 - 2024 — McMap. All rights reserved.