I'm trying to write strict ISO C89-compliant code. Since long long
is not standard and is often implemented as compiler extensions before C99, the compiler should warn me it when I use it. However, when using either gcc or clang, there's no warning when int64_t
is used and expands to long long
(with 32-bit compilation -m32
). Is there any way to make the compiler warn me?
For example:
/* test.c */
#include <stdint.h>
#include <stdio.h>
int main(void) {
printf("The size of an int64_t is %u.\n", (unsigned)sizeof(int64_t));
return 0;
}
Compiled with clang or gcc:
clang-17 -m32 -std=c89 -Wall -Wextra -Werror -pedantic -pedantic-errors test.c
# or
x86_64-pc-linux-gnu-gcc-13.2.1 -m32 -std=c89 -Wall -Wextra -Werror -pedantic -pedantic-errors test.c
They issue no warning or error, even if the int64_t
is actually a typedef to long long int
, since clang-17 -m32 -std=c89 -E -dD -Wall -Wextra -Werror -pedantic -pedantic-errors test.c
gives me:
...
#define __INT64_TYPE__ long long int
#define __INT64_FMTd__ "lld"
#define __INT64_FMTi__ "lli"
#define __INT64_C_SUFFIX__ LL
...
and in /path/to/clang/17/include/stdint.h
there's
...
#ifdef __INT64_TYPE__
# ifndef __int8_t_defined /* glibc sys/types.h also defines int64_t*/
typedef __INT64_TYPE__ int64_t;
# endif /* __int8_t_defined */
typedef __UINT64_TYPE__ uint64_t;
# undef __int_least64_t
...
But if I replace int64_t
with long long
or __INT64_TYPE__
in the above test.c
the compiler will complain about it. So why there's a behavioral difference between long long
and uint64_t
?
The most relevant question on SO seems to be this and this, but their answers don't seem to explain why there's no warning when using -m32
. (In -m64
mode uint64_t
is not a problem since it expands to long
, which is in C89 standard).
-- EDIT --
Eric's answer explained nicely why and how compilers treat system headers and user headers differently. For those who might be interested, in addition to creating your own sets of headers, I found one way to make the compiler also check C-standard compliance for system headers:
clang -v <args>...
to generate the command that clang is actually running;-internal-isystem
with -I
;In file included from test.c:2:
/usr/lib/clang/17/include/stdint.h:10:1: error: // comments are not allowed in this language [-Werror,-Wcomment]
10 | // AIX system headers need stdint.h to be re-enterable while _STD_TYPES_T
| ^
/usr/lib/clang/17/include/stdint.h:52:3: error: #include_next is a language extension [-Werror,-Wgnu-include-next]
52 | # include_next <stdint.h>
| ^
2 errors generated.
meaning that the standard library is not C89-compliant, which is the expected behavior.
The most relevant question on SO seems to be this and this, but their answers don't seem to explain why there's no warning when using
-m32
.
GCC would issue a warning on the typedef
(rather than where the defined type is used), but GCC has built-in behavior to suppress warnings in system headers. So does Clang.
A potential workaround is to make your own versions of system headers that suppress the undesired type definitions. For each standard header, make a file with this template:
#define int64_t DoNotUseint64_t
#define uint64_t DoNotUseuint64_t
#include <absolute path to actual system header>
#undef uint64_t
#undef int64_t
Put those files in a directory, and compile with -isystem ThatDirectory
.
(Note you must do this for each standard header, because standard headers are allowed to include and define others even if the C standard does not explicitly say so. E.g., on my platform, including <stdio.h>
defines int64_t.)
This will result in no typedef
being made for int64_t
(it will be made for DoNotUseint64_t
instead), so your program will get a compiler error if it attempts to use int64_t
. There is some chance these redefinitions will break something in the standard headers, but you will have to try it and see.
As Lundin notes, you could use #pragma GCC poison int64_t
to flag uses of int64_t
and other types. Some consideration of this versus the #define
method above:
#pragma
after all standard headers are included. (A problem arises if these inclusions are anywhere other than the beginning of the source file, as you need to poison the identifiers for the user source code and not for the standard headers.)int64_t
and other such identifiers provided it defines them itself, since these were ordinary identifiers in C 1990. If that is an issue for you, then the #define
method above allows that to work, whereas the poison
method does not.