Question

How to Print unsigned long long with printf in C

csyntaxprintfformat-specifierslong-long

Question

#include <stdio.h>

int main(void) {
    unsigned long long int num = 285212672;
    int normalInt = 5;

    printf(
        "My number is %d bytes wide and its value is %ul. A normal number is %d.\n",
        sizeof(num),
        num,
        normalInt
    );

    return 0;
}

This program prints unexpected output:

My number is 8 bytes wide and its value is 285212672l. A normal number is 0.

I suspect the problem is the printf() format used for the unsigned long long int. What is the correct way to print an unsigned long long int with printf(), and why does the current format string produce incorrect output?

Short Answer

By the end of this page, you will understand how printf() format specifiers work in C, why the type and format must match exactly, and how to correctly print an unsigned long long int using %llu. You will also learn why sizeof() should usually be printed with %zu, and how format mismatches can cause strange output.

Concept

In C, printf() is a formatted output function. It does not know the types of the arguments by itself. Instead, it relies entirely on the format string to know how to interpret each value.

That means this is crucial:

  • %d expects an int
  • %u expects an unsigned int
  • %ld expects a long
  • %llu expects an unsigned long long

If the format specifier does not match the actual argument type, the behavior is undefined. In practice, this often leads to:

  • wrong numbers being printed
  • extra characters appearing
  • later arguments being read incorrectly
  • platform-dependent bugs

In your example, %ul is not the correct specifier for unsigned long long. In printf(), the length modifier must come before the conversion letter. So:

  • %lu = unsigned long
  • %llu = unsigned long long

Writing %ul makes printf() read %u first and treat the l as a normal character in the string. That is why the output includes a trailing l.

It also causes the next argument to be interpreted incorrectly, which is why normalInt prints as 0 instead of 5.

The correct code is:

printf("My number is %zu bytes wide and its value is %llu. A normal number is %d.\n",
       sizeof(num), num, normalInt);

Why %zu for sizeof(num)?

  • sizeof returns a value of type size_t
  • the correct printf() specifier for size_t is %zu

This makes the code portable and correct.

Mental Model

Think of printf() like a person reading labels on boxes coming down a conveyor belt.

Each format specifier is a label telling printf() what kind of box to open:

  • %d = "this box contains an int"
  • %llu = "this box contains an unsigned long long"
  • %zu = "this box contains a size_t"

If you put the wrong label on a box, printf() opens it the wrong way and gets confused. Once that happens, the rest of the boxes may also be read incorrectly.

So the rule is simple:

  • the format string must describe the arguments exactly
  • if the label is wrong, the output can become nonsense
Take Quiz

Syntax and Examples

The correct syntax for printing an unsigned long long is:

printf("%llu", value);

Example:

#include <stdio.h>

int main(void) {
    unsigned long long int num = 285212672;
    printf("Value: %llu\n", num);
    return 0;
}

Output:

Value: 285212672

If you also want to print the size of the variable:

#include <stdio.h>

int main(void) {
    unsigned long long int num = 285212672;
    printf("Size: %zu bytes, Value: %llu\n", sizeof(num), num);
    return 0;
}

Output might be:

Size: 8 bytes, Value: 285212672

Why %ul is wrong

printf("%ul", num);

This is interpreted as:

  • %u → print an unsigned int
  • l → print the character l

So it does not mean unsigned long. The correct one is %lu.

Common integer format specifiers

int a = -10;
unsigned int b = 10;
long c = 1000;
unsigned long d = 1000;
long long e = 5000000000LL;
unsigned long long f = 5000000000ULL;

printf("%d\n", a);
printf("%u\n", b);
printf("%ld\n", c);
printf("%lu\n", d);
printf("%lld\n", e);
printf("%llu\n", f);

Step by Step Execution

Consider this corrected example:

#include <stdio.h>

int main(void) {
    unsigned long long num = 285212672;
    int normalInt = 5;

    printf("My number is %zu bytes wide and its value is %llu. A normal number is %d.\n",
           sizeof(num), num, normalInt);

    return 0;
}

Step by step

  1. num is created as an unsigned long long.
  2. normalInt is created as an int.
  3. sizeof(num) is evaluated.
    • Its type is size_t.
    • On many systems its value is 8.
  4. printf() reads the format string from left to right.
  5. %zu tells printf():
    • "read the next argument as size_t"
    • it prints 8
  6. %llu tells printf():
    • "read the next argument as unsigned long long"
    • it prints 285212672
  7. %d tells printf():
    • "read the next argument as int"
    • it prints 5
  8. The final output is correct.

What went wrong in the original code

Original format:

"My number is %d bytes wide and its value is %ul. A normal number is %d.\n"

printf() reads %ul as:

  • %u for an unsigned int
  • then literal l

But the argument is actually unsigned long long, not unsigned int.

That mismatch means printf() consumes the wrong amount or wrong interpretation of data. After that, the remaining arguments may no longer line up properly, so the final %d reads garbage or the wrong bytes.

Real World Use Cases

Using the correct printf() format specifier matters anywhere C code prints values for debugging, logging, reporting, or user output.

Common real uses

  • System programming: printing file sizes, memory sizes, counters, or IDs
  • Embedded programming: printing hardware counters and timestamps
  • Networking: logging packet counts or large sequence numbers
  • Data processing tools: printing large unsigned totals
  • Debugging: verifying variable values during development

Example: logging a large counter

unsigned long long totalRequests = 123456789ULL;
printf("Total requests: %llu\n", totalRequests);

Example: printing object size safely

char buffer[256];
printf("Buffer size: %zu\n", sizeof(buffer));

If the format is wrong, logs become unreliable, and debugging gets harder.

Take Quiz

Real Codebase Usage

In real projects, developers usually follow a few practical patterns when using printf().

1. Match every type exactly

This is the most important habit.

size_t len = 42;
unsigned long long count = 1000;

printf("len=%zu count=%llu\n", len, count);

2. Let compiler warnings help you

Compiling with warnings catches many format mistakes.

gcc -Wall -Wextra -Wformat program.c

These warnings are extremely useful in production code.

3. Use fixed-width integer macros when needed

When code uses types like uint64_t, many codebases prefer <inttypes.h> macros for portability.

#include <inttypes.h>
#include <stdint.h>

uint64_t id = 123;
printf("id=%" PRIu64 "\n", id);

This is common in portable libraries and cross-platform systems code.

4. Use logging wrappers carefully

Large codebases often wrap printf() in macros or logging functions. The same format rules still apply.

printf("user_id=%llu status=%d\n", userId, status);

5. Prefer explicit suffixes for large constants

unsigned long long maxCount = 5000000000ULL;

The ULL suffix makes the constant type clearer and avoids accidental type issues.

Common Mistakes

1. Using the wrong specifier order

Broken:

printf("%ul\n", num);

Correct:

printf("%lu\n", someUnsignedLong);
printf("%llu\n", num);

The length modifier comes before the final conversion letter.

2. Printing sizeof(...) with %d

Broken:

printf("%d\n", sizeof(num));

Better:

printf("%zu\n", sizeof(num));

sizeof returns size_t, not int.

3. Assuming wrong format strings are "close enough"

Broken:

unsigned long long n = 10;
printf("%u\n", n);

This is undefined behavior.

Correct:

printf("%llu\n", n);

4. Forgetting that undefined behavior can affect later arguments

Broken:

unsigned long long n = 10;
int x = 5;
printf("%u %d\n", n, x);

You might think only the first value is wrong, but later values can also become wrong.

5. Ignoring compiler warnings

If your compiler says the format and argument type do not match, fix it. Do not ignore it.

6. Confusing long and long long

These are different types.

  • %lu → unsigned long
  • %llu → unsigned long long

Do not assume they are interchangeable.

Comparisons

TypeCorrect printf() specifierNotes
int%dSigned integer
unsigned int%uUnsigned integer
long%ldSigned long
unsigned long%luUnsigned long
long long%lldSigned long long
unsigned long long%lluUnsigned long long
size_t%zuCommonly used with sizeof

%lu vs %llu

SpecifierExpected type
%luunsigned long
%lluunsigned long long

unsigned long long vs uint64_t

TypeWhen usedPrinting approach
unsigned long longGeneral C integer type%llu
uint64_tExact-width integer from <stdint.h>Often PRIu64 from <inttypes.h>

If you specifically use fixed-width types, the <inttypes.h> approach is often more portable.

Take Quiz

Cheat Sheet

// unsigned long long
unsigned long long x = 123ULL;
printf("%llu\n", x);

// long long
long long y = -123LL;
printf("%lld\n", y);

// unsigned long
unsigned long a = 10UL;
printf("%lu\n", a);

// size_t (for sizeof)
printf("%zu\n", sizeof(x));

Rules

  • printf() does not detect types automatically.
  • The format specifier must match the argument type exactly.
  • For unsigned long long, use %llu.
  • For sizeof(...), use %zu.
  • %ul is wrong; l is treated as a literal character.

Quick fixes for the original code

printf("My number is %zu bytes wide and its value is %llu. A normal number is %d.\n",
       sizeof(num), num, normalInt);

Helpful compiler flags

gcc -Wall -Wextra -Wformat file.c

Portable fixed-width printing

#include <stdint.h>
#include <inttypes.h>

uint64_t n = 42;
printf("%" PRIu64 "\n", n);

FAQ

What is the correct printf specifier for unsigned long long in C?

Use %llu.

Why does %ul print an extra l?

Because printf() reads %u as the conversion specifier and then treats l as a normal character.

Why did the next %d print the wrong value?

Because the previous format mismatch caused printf() to interpret the arguments incorrectly. This is undefined behavior.

Should I use %d for sizeof?

No. sizeof returns size_t, so %zu is the correct specifier.

What is the difference between %lu and %llu?

%lu is for unsigned long. %llu is for unsigned long long.

Can I print uint64_t with %llu?

Not always safely on every platform. The portable approach is to use <inttypes.h> and PRIu64.

How can I catch printf mistakes early?

Compile with warnings such as -Wall -Wextra -Wformat.

Is this a runtime bug or a syntax error?

The code compiles, but the output is wrong because of undefined behavior caused by a format mismatch.

Take Quiz

Mini Project

Description

Build a small C program that prints several integer types correctly. This project reinforces how printf() format specifiers must match the actual variable types, including int, unsigned int, unsigned long long, and size_t from sizeof.

Goal

Create a program that prints multiple numeric types using the correct printf() specifiers without warnings.

Requirements

  • Declare an int, an unsigned int, and an unsigned long long
  • Print each variable with the correct format specifier
  • Print the size of at least one variable using sizeof
  • Make sure the output is readable and labeled
  • Use main(void) and return 0
Take Quiz

Keep learning

Related questions

View more