I have the following warning generated by a printf()
:
warning: format '%llu' expects argument of type 'long long unsigned int', but argument 7 has type 'uint64_t' {aka 'long unsigned int'} [-Wformat=]
Although I want to create a warning-free code, I think that the compiler is way too over-zealous. Because:
long unsigned
is unsigned and has 64 bits (it is there, in the text of the message)long long unsigned
is also unsigned, and cannot be shorter than 64 bits (C standard, in comparison to unsigned long
), and also cannot have more than 64 bits (hardware limitation, not enough bits in the processor registers).So for any practical purpose, long unsigned
and long long unsigned
are actually the same thing, right?
Even more, while long unsigned
can have 32bits on some platforms (e.g., Widows), long long unsigned
is highly unlikely to have another number of bits (than 64). long long
was created especially to support 64 bits.
So what is going on? How should I proceed? What should I understand?
Side question:
Let's assume that the following is true:
typedef long unsigned int uint64_t;
And let's assume that on some platform, long unsigned
becomes 32 bits, and in that case, long long unsigned
is not anymore the same as long unsigned
. But then uint64_t
is not 64 bits anymore - which means that the definition of uint64_t
will have to change to remain 64 bits. So in the end, long long unsigned
and uint64_t
will still be the same size and sign-ness.
So is my thinking correct, that the compiler should not have given me that message at all? Because it is not applicable, no matter how things are, or will be?
I currently use Codeblocks with Cygwin C compiler under Windows - in order to have long unsigned
of 64 bits, instead of 32.
The command line for compiling (copy-paste):
gcc.exe -Wshadow -Winit-self -Wredundant-decls -Wcast-align -Wundef -Wfloat-equal -Winline -Wunreachable-code -Wmissing-declarations -Wswitch-enum -Wswitch-default -Wmain -pedantic -Wextra -Wall -m64 -O0 -c Z:/_progs/_progsData/cb/myprog.c -o obj/Debug/myprog.o
The warning underscores a portability issue. Using %llu
for a uint64_t
would have undefined behavior on architectures where uint64_t
is an unsigned long
and unsigned long long
would have 128 bits, which would make sense if 128-bit arithmetics are supported.
This would not happen on legacy systems where long
is still 32 bits, but the C Standard does support this possibility.
You can fix the code in different ways:
use the PRIu64
macro defined in <inttypes.h>
(quite ugly and convoluted, but this is the correct solution):
int print_uint64(uint64_t val) {
return printf("%" PRIu64, val); // a bit cryptic but correct
}
cast the values as (unsigned long long)
(ugly and verbose too):
int print_uint64(uint64_t val) {
return printf("%llu", (unsigned long long)val);
}
use %lu
... but this would make your code non portable to legacy architectures where long
still has just 32 bits.
use your own type for 64-bit ints typedef unsigned long long u64;
and you can use %llu
on all platforms where long long
has 64 bits, and worry about portability to 128 bit systems later:
#include <limits.h>
#include <stdint.h>
#include <stdio.h>
#if ULLONG_MAX == UINT64_MAX
typedef unsigned long long u64;
typedef long long i64;
#else
#error "this platform does not have 64-bit long long types"
#endif
int print_u64(u64 val) {
return printf("%llu", val);
}