Question
I want to print an int64_t value in C without compiler warnings and in a portable way.
I currently have code like this:
#include <stdio.h>
#include <stdint.h>
int main(void) {
int64_t my_int = 999999999999999999;
printf("This is my_int: %I64d\n", my_int);
return 0;
}
On some systems, I also tried:
printf("This is my_int: %lld\n", my_int);
but I still get a warning that the format specifier does not match the type of my_int.
I am compiling with GCC 4.2.1 on an Apple system.
Which format specifier should be used to print an int64_t value correctly and portably in C, without warnings?
Short Answer
By the end of this page, you will understand why int64_t should not be printed with hard-coded format specifiers like %lld or %I64d, and how to print it portably using the macros from inttypes.h, such as PRId64 and PRIu64. You will also see how this fits into real C codebases and how to avoid common printf format mismatches.
Concept
In C, fixed-width integer types such as int64_t and uint64_t are defined in stdint.h. They guarantee a width, such as exactly 64 bits, but they do not guarantee which base C type is used underneath.
For example, on one platform int64_t might be a long, while on another it might be a long long. Because printf requires the format string to match the actual underlying type, using a hard-coded specifier like %lld or %I64d is not fully portable.
That is why C provides inttypes.h. This header defines format macros specifically for fixed-width integer types:
PRId64for signedint64_tPRIu64for unsigneduint64_t- and many others for other integer widths
These macros expand to the correct format string for the current platform.
This matters because printf is a variadic function. Variadic functions do not know argument types automatically. They trust the format string. If the format string and argument type do not match, you can get:
- compiler warnings
- incorrect output
- undefined behavior
Using PRId64 and related macros makes your code portable, correct, and warning-free.
Mental Model
Think of int64_t as a box labeled "64-bit signed integer".
The label tells you the size of the value, but not the exact material the box is made from. On one machine, the box might really be a long; on another, a long long.
printf does not look at the label on the box. It only follows the instructions in the format string.
So if you say %lld, you are telling printf: "Expect a long long."
If the value is actually represented as a long on that platform, the instruction does not match the box.
PRId64 is like asking the compiler: "Please give me the right instruction for this platform."
Syntax and Examples
To print fixed-width integers portably, include both stdint.h and inttypes.h.
#include <stdio.h>
#include <stdint.h>
#include <inttypes.h>
Signed int64_t
#include <stdio.h>
#include <stdint.h>
#include <inttypes.h>
int main(void) {
int64_t my_int = 999999999999999999;
printf("This is my_int: %" PRId64 "\n", my_int);
return 0;
}
Unsigned uint64_t
#include <stdio.h>
#include <stdint.h>
#include <inttypes.h>
int main(void) {
uint64_t value = 999999999999999999ULL;
printf("This is value: %" PRIu64 "\n", value);
return 0;
}
Why the format looks unusual
This part:
"%" PRId64 "\n"
is just string literal concatenation in C. The compiler joins it into one format string.
If PRId64 expands to lld, the final result becomes:
"%lld\n"
If it expands to ld, then the final string becomes:
"%ld\n"
That is exactly what makes it portable.
Step by Step Execution
Consider this example:
#include <stdio.h>
#include <stdint.h>
#include <inttypes.h>
int main(void) {
int64_t count = 42;
printf("count = %" PRId64 "\n", count);
return 0;
}
Here is what happens step by step:
#include <stdint.h>defines the typeint64_t.#include <inttypes.h>defines the macroPRId64.int64_t count = 42;stores a 64-bit signed integer.- The compiler sees:
"count = %" PRId64 "\n" - It expands
PRId64to the correct platform-specific format part, such aslldorld. - Adjacent string literals are merged into one string.
printfreceives a format string that correctly matches the underlying type ofcount.- The program prints:
count = 42
Because the format matches the type, the compiler warning goes away.
Real World Use Cases
Portable fixed-width integer printing is common in real programs where exact integer sizes matter.
File sizes and byte counters
uint64_t bytes_processed = 1234567890ULL;
printf("Processed %" PRIu64 " bytes\n", bytes_processed);
Timestamps and IDs
int64_t event_id = 91234567890123;
printf("Event ID: %" PRId64 "\n", event_id);
Cross-platform libraries
If your code runs on Linux, macOS, and Windows, you should avoid platform-specific specifiers like %I64d.
Binary protocols and data formats
When reading or writing 64-bit values from network packets, database files, or custom binary formats, developers often use int64_t and uint64_t. Printing them correctly is important for debugging and logging.
Real Codebase Usage
In real C projects, developers usually follow these patterns:
Logging fixed-width values
fprintf(stderr, "offset=%" PRId64 "\n", offset);
This keeps logs portable across compilers and operating systems.
Validation and diagnostics
if (size < 0) {
fprintf(stderr, "Invalid size: %" PRId64 "\n", size);
return 1;
}
Configuration and parsing tools
Programs that parse configuration files or command-line arguments often store results in fixed-width types and print them back for debugging.
Wrapping format details in macros or helpers
Large codebases sometimes create helper functions for repeated logging:
void print_record_id(int64_t id) {
printf("record id=%" PRId64 "\n", id);
}
This reduces repeated format mistakes.
Error handling
When printing values during failures, using the correct format matters even more because debugging output must be trustworthy.
Common Mistakes
Mistake 1: Using %lld for every int64_t
int64_t x = 10;
printf("%lld\n", x); // may warn on some systems
Why it is a problem:
int64_tis not guaranteed to belong long- it may be
longon some platforms
Use this instead:
printf("%" PRId64 "\n", x);
Mistake 2: Using Windows-specific %I64d
printf("%I64d\n", x);
Why it is a problem:
%I64dis not standard C portable syntax- it may fail or warn on non-Windows compilers
Use PRId64 instead.
Mistake 3: Forgetting to include inttypes.h
#include <stdio.h>
#include <stdint.h>
printf("%" PRId64 "\n", x); // error: PRId64 not defined
Fix:
#include <inttypes.h>
Mistake 4: Mixing signed and unsigned macros
uint64_t u = 100;
printf("%" PRId64 "\n", u); // wrong macro for unsigned type
Use:
printf("%" PRIu64 "\n", u);
Mistake 5: Adding the macro inside the string manually
Broken:
printf("%PRId64\n", x);
Correct:
printf("%" PRId64 "\n", x);
The macro is not text inside the string. It is a preprocessor token that becomes part of the string through concatenation.
Comparisons
| Approach | Example | Portable? | Notes |
|---|---|---|---|
| Platform-specific specifier | %I64d | No | Common on older Windows compilers |
Hard-coded long long specifier | %lld | Sometimes | Works only if int64_t is actually long long |
Hard-coded long specifier | %ld | Sometimes | Works only if int64_t is actually long |
inttypes.h macro | "%" PRId64 | Yes | Recommended portable solution |
int64_t vs long long
| Type | Meaning |
|---|---|
int64_t | Exactly 64-bit signed integer, if available |
long long | A standard C integer type, but not the portable name for fixed-width formatting |
stdint.h vs inttypes.h
| Header | Purpose |
|---|---|
stdint.h | Defines fixed-width integer types like int64_t |
inttypes.h | Defines format macros like PRId64, PRIu64 |
Use both together when you need fixed-width integers and portable printing.
Cheat Sheet
#include <stdint.h>
#include <inttypes.h>
Print fixed-width integers
int64_t a;
uint64_t b;
printf("%" PRId64 "\n", a);
printf("%" PRIu64 "\n", b);
Common macros
| Type | Decimal macro |
|---|---|
int8_t | PRId8 |
int16_t | PRId16 |
int32_t | PRId32 |
int64_t | PRId64 |
uint8_t | PRIu8 |
uint16_t | PRIu16 |
uint32_t | PRIu32 |
uint64_t | PRIu64 |
Pattern to remember
printf("%" PRId64 "\n", value);
Rules
- Use
stdint.hfor the type. - Use
inttypes.hfor the print macro. - Do not assume
int64_tis alwayslong long. - Do not use
%I64dif you want portable code. - Match signed types with
PRId...and unsigned types withPRIu....
Edge case
int64_t exists only if the platform provides an exact 64-bit signed integer type. On most modern systems, it does.
FAQ
What is the correct portable way to print int64_t in C?
Use PRId64 from inttypes.h:
printf("%" PRId64 "\n", value);
Why does %lld sometimes produce a warning for int64_t?
Because int64_t is not guaranteed to be long long on every platform. It may be a different underlying type.
Do I need both stdint.h and inttypes.h?
Yes. stdint.h gives you int64_t, and inttypes.h gives you PRId64.
Can I use %I64d in portable C code?
No. %I64d is compiler- or platform-specific and should not be used for portable code.
How do I print uint64_t?
Use PRIu64:
printf("%" PRIu64 "\n", value);
Why is the format written as "%" PRId64 instead of %PRId64?
Because PRId64 is a macro, not part of the string text. C concatenates adjacent string literals after macro expansion.
Does this only apply to printf?
No. The same idea applies to other formatted output functions such as fprintf and sprintf.
Mini Project
Description
Build a small command-line program that prints several fixed-width integer values in a portable way. This project demonstrates how to combine stdint.h types with inttypes.h format macros for clean, warning-free output across platforms.
Goal
Create a C program that prints signed and unsigned fixed-width integers using the correct portable printf macros.
Requirements
- Include
stdint.h,inttypes.h, andstdio.h - Declare at least one
int64_tand oneuint64_tvariable - Print both values using the correct format macros
- Add one additional fixed-width integer type such as
int32_toruint32_t - Ensure the program uses portable format strings only
Keep learning
Related questions
2D Array Loop Order and Cache Performance in C
Learn why swapping nested loops changes 2D array performance in C, using row-major memory layout, cache locality, and practical benchmarks.
Array-to-Pointer Conversion in C and C++ Explained
Learn what array-to-pointer conversion means in C and C++, how array decay works, and how it differs from a pointer to an array.
Building More Fault-Tolerant Embedded C++ Applications for Radiation-Prone ARM Systems
Learn practical C++ and compile-time techniques to reduce soft-error damage in embedded ARM systems exposed to radiation.