Question

How to Print int64_t Portably in C Using inttypes.h

cstdint

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:

  • PRId64 for signed int64_t
  • PRIu64 for unsigned uint64_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."

Take Quiz

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:

  1. #include <stdint.h> defines the type int64_t.
  2. #include <inttypes.h> defines the macro PRId64.
  3. int64_t count = 42; stores a 64-bit signed integer.
  4. The compiler sees:
    "count = %" PRId64 "\n"
    
  5. It expands PRId64 to the correct platform-specific format part, such as lld or ld.
  6. Adjacent string literals are merged into one string.
  7. printf receives a format string that correctly matches the underlying type of count.
  8. 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.

Take Quiz

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_t is not guaranteed to be long long
  • it may be long on some platforms

Use this instead:

printf("%" PRId64 "\n", x);

Mistake 2: Using Windows-specific %I64d

printf("%I64d\n", x);

Why it is a problem:

  • %I64d is 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

ApproachExamplePortable?Notes
Platform-specific specifier%I64dNoCommon on older Windows compilers
Hard-coded long long specifier%lldSometimesWorks only if int64_t is actually long long
Hard-coded long specifier%ldSometimesWorks only if int64_t is actually long
inttypes.h macro"%" PRId64YesRecommended portable solution

int64_t vs long long

TypeMeaning
int64_tExactly 64-bit signed integer, if available
long longA standard C integer type, but not the portable name for fixed-width formatting

stdint.h vs inttypes.h

HeaderPurpose
stdint.hDefines fixed-width integer types like int64_t
inttypes.hDefines format macros like PRId64, PRIu64

Use both together when you need fixed-width integers and portable printing.

Take Quiz

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

TypeDecimal macro
int8_tPRId8
int16_tPRId16
int32_tPRId32
int64_tPRId64
uint8_tPRIu8
uint16_tPRIu16
uint32_tPRIu32
uint64_tPRIu64

Pattern to remember

printf("%" PRId64 "\n", value);

Rules

  • Use stdint.h for the type.
  • Use inttypes.h for the print macro.
  • Do not assume int64_t is always long long.
  • Do not use %I64d if you want portable code.
  • Match signed types with PRId... and unsigned types with PRIu....

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.

Take Quiz

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, and stdio.h
  • Declare at least one int64_t and one uint64_t variable
  • Print both values using the correct format macros
  • Add one additional fixed-width integer type such as int32_t or uint32_t
  • Ensure the program uses portable format strings only
Take Quiz

Keep learning

Related questions

View more