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)?

Alex Reynolds
  • 95,983
  • 54
  • 240
  • 345

3 Answers3

8
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.)

Matthew Slattery
  • 45,290
  • 8
  • 103
  • 119
  • 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. – Alex Reynolds Nov 08 '12 at 00:47
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.

md5
  • 23,373
  • 3
  • 44
  • 93
  • +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`. – tomlogic Nov 07 '12 at 18:20
  • Question: What about `int64_t method_four{}`? – starriet Jun 30 '23 at 08:04
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()
iabdalkader
  • 17,009
  • 4
  • 47
  • 74
  • 4
    There are no guarantees that `int64_t` is `long long`. If there were, we wouldn't need the typedef. – Bo Persson Nov 07 '12 at 18:04
  • 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. – Alan Corey Mar 19 '18 at 16:33