Question

How to Print unsigned long with printf in C

cprintflong-integerunsignedformat-specifiers

Question

I have trouble remembering how to print an unsigned long value in C using printf.

Suppose unsigned_foo is declared as an unsigned long. I tried several format strings:

printf("%lu\n", unsigned_foo);
printf("%du\n", unsigned_foo);
printf("%ud\n", unsigned_foo);
printf("%ll\n", unsigned_foo);
printf("%ld\n", unsigned_foo);
printf("%dl\n", unsigned_foo);

Instead of printing the expected unsigned value, some of these print a negative number. What is the correct way to print an unsigned long in C, and why do the other format specifiers behave incorrectly?

Short Answer

By the end of this page, you will know the correct printf format specifier for unsigned long in C, understand why mismatched format specifiers can print incorrect values, and learn how to choose the right specifier for related integer types.

Concept

In C, printf does not automatically know the type of each value you pass. It relies entirely on the format string to interpret the bytes correctly.

For an unsigned long, the correct format specifier is:

%lu
  • % starts a format specifier
  • l means long
  • u means unsigned decimal integer

So this is correct:

unsigned long unsigned_foo = 123456UL;
printf("%lu\n", unsigned_foo);

If you use the wrong specifier, printf interprets the argument as the wrong type. That causes undefined behavior, which often shows up as strange output such as negative numbers or garbage values.

For example:

  • %ld means long int (signed long)
  • %u means unsigned int
  • %d means int
  • %llu means unsigned long long

Even if some of these types look similar, they are not interchangeable in printf.

This matters in real programming because formatted output is used everywhere:

  • debugging values
  • logging
  • printing counters, file sizes, IDs, and timestamps
  • displaying results in command-line tools

A small mismatch in a format string can make debugging harder or lead to incorrect output.

Mental Model

Think of printf like a machine reading labeled boxes on a conveyor belt.

Each value you pass is a box containing bytes. The format string tells printf which label to use when opening each box.

If the box contains an unsigned long, the label must match:

  • unsigned → use u
  • long → use l

So the correct label is %lu.

If you attach the wrong label, printf opens the box incorrectly. It may treat an unsigned value as signed, or expect a different size. That is why wrong format specifiers can print nonsense or negative values.

Take Quiz

Syntax and Examples

Core syntax

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

Use %lu when value has type unsigned long.

Basic example

#include <stdio.h>

int main(void) {
    unsigned long count = 4000000000UL;
    printf("count = %lu\n", count);
    return 0;
}

Output might be:

count = 4000000000

Why %ld is different

#include <stdio.h>

int main(void) {
    unsigned long value = 4000000000UL;

    printf("Correct: %lu\n", value);
    printf("Wrong:   %ld\n", value);

    return 0;
}

%lu prints the value as an unsigned long.

%ld tells printf to interpret the same bytes as a signed long. That mismatch causes undefined behavior and may print a negative number.

Printing with text around it

unsigned long size = 1024UL;
printf("File size: %lu bytes\n", size);

Related integer examples

unsigned int a = 10;
unsigned long b = 20UL;
unsigned long long c = 30ULL;

printf("%u\n", a);    // unsigned int
printf("%lu\n", b);   // unsigned long
printf("%llu\n", c);  // unsigned long long

Step by Step Execution

Consider this program:

#include <stdio.h>

int main(void) {
    unsigned long value = 42UL;
    printf("Value: %lu\n", value);
    return 0;
}

Step by step

  1. unsigned long value = 42UL;

    • A variable named value is created.
    • Its type is unsigned long.
    • The suffix UL makes the literal an unsigned long.
  2. printf("Value: %lu\n", value);

    • printf reads the format string.
    • It sees ordinary text: Value:
    • Then it sees %lu
    • %lu means: read the next argument as an unsigned long and print it in decimal form.
    • The next argument is value, which is indeed an unsigned long.
    • printf prints 42.
    • \n prints a newline.
  3. return 0;

    • The program ends successfully.

What if you use %ld instead?

printf("Value: %ld\n", value);

Now printf is told to read the argument as a signed long. But the argument is actually unsigned long. The types do not match, so the behavior is undefined.

You might see:

  • the same number
  • a negative number
  • a completely incorrect number

You cannot rely on any of these results.

Real World Use Cases

Logging counters and totals

unsigned long request_count = 150000UL;
printf("Requests handled: %lu\n", request_count);

Printing file sizes or byte counts

unsigned long bytes = 2048UL;
printf("Downloaded: %lu bytes\n", bytes);

Reporting loop progress

unsigned long i;
for (i = 0; i < 3; i++) {
    printf("Step %lu\n", i);
}

Debugging values from system or library code

Some APIs return values stored in larger integer types. When printing them for debugging, the format must match the actual type exactly.

Embedded and systems programming

In low-level C programs, exact integer sizes and signedness matter. Printing with the right specifier helps verify values correctly during testing and troubleshooting.

Take Quiz

Real Codebase Usage

In real projects, developers usually do more than just write printf("%lu", x).

1. Print values with clear labels

printf("timeout_ms=%lu\n", timeout_ms);

This makes logs easier to read.

2. Use the exact matching specifier

Developers check the actual variable type before printing:

unsigned long total;
printf("%lu\n", total);

Not:

printf("%u\n", total);   // wrong if total is unsigned long

3. Use warnings to catch mistakes

Compilers can detect many printf mismatches.

gcc -Wall -Wextra -Wformat program.c

These warnings are extremely helpful in real codebases.

4. Use <inttypes.h> for fixed-width integer types

If a codebase uses types like uint32_t or uint64_t, developers often prefer macros such as PRIu64 instead of guessing the right format string.

#include <inttypes.h>
#include <stdio.h>

uint64_t id = 123;
printf("%

Common Mistakes

1. Using the right letters in the wrong order

This is wrong:

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

The correct order is length modifier first, then conversion specifier:

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

2. Using %d or %u for an unsigned long

Broken example:

unsigned long value = 5000000000UL;
printf("%u\n", value);   // wrong

%u is for unsigned int, not unsigned long.

Use:

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

3. Using %ld because long looks close enough

Broken example:

unsigned long value = 4000000000UL;
printf("%ld\n", value);  // wrong

%ld is signed long, not unsigned long.

4. Forgetting the semicolon

One of the original examples is missing semicolons:

printf("%lu\n", unsigned_foo)

It should be:

printf("%lu\n", unsigned_foo);

5. Assuming wrong output is always just a signedness issue

A format mismatch is not just a small display problem. In C, it is undefined behavior. That means the language does not guarantee what happens.

6. Ignoring compiler warnings

If your compiler says the format expects one type and you passed another, fix it immediately. Those warnings often point directly to the bug.

Comparisons

TypeCorrect printf specifierMeaning
int%dsigned decimal integer
unsigned int%uunsigned decimal integer
long%ldsigned long decimal integer
unsigned long%luunsigned long decimal integer
long long%lldsigned long long decimal integer
unsigned long long%lluunsigned long long decimal integer

%lu vs %u

SpecifierExpected typeUse when
%uunsigned intthe variable is exactly unsigned int
%luunsigned longthe variable is exactly unsigned long

%lu vs %ld

SpecifierSigned or unsignedExpected type
%ldsignedlong
%luunsignedunsigned long

Even if both use long, the signedness still matters.

Plain integer types vs fixed-width integer types

If you use fixed-width types such as uint64_t, prefer <inttypes.h> macros when portability matters:

#include <inttypes.h>
#include <stdio.h>

uint64_t n = 100;
printf("%
Take Quiz

Cheat Sheet

// unsigned long
printf("%lu\n", value);

Quick rules

  • unsigned long → %lu
  • long → %ld
  • unsigned int → %u
  • int → %d
  • unsigned long long → %llu
  • long long → %lld

Pattern

% + length modifier + type

Examples:

  • l = long
  • ll = long long
  • d = signed decimal
  • u = unsigned decimal

Common valid examples

unsigned long a = 10UL;
long b = -10L;
unsigned long long c = 99ULL;

printf("%lu\n", a);
printf("%ld\n", b);
printf("%llu\n", c);

Common invalid examples

printf("%ul\n", a);   // wrong order
printf("%u\n", a);    // wrong type
printf("%ld\n", a);   // signed vs unsigned mismatch

Best practices

  • Match the format string to the exact type.
  • Compile with -Wall -Wextra -Wformat.
  • For uint32_t, uint64_t, and similar types, consider <inttypes.h> macros.

Important edge case

A wrong printf specifier is not just "slightly wrong". It causes undefined behavior.

FAQ

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

Use:

%lu

Why does %ld sometimes print a negative number?

Because %ld tells printf to treat the value as a signed long. If the actual value is unsigned long, the types do not match.

Is %ul correct for unsigned long?

No. The correct order is %lu, not %ul.

Can I use %u for unsigned long if the value is small?

No. The format specifier must match the type, not just the value.

Why do some wrong format specifiers seem to work?

Sometimes the memory layout makes the output look correct by accident. But it is still undefined behavior and cannot be trusted.

How can I catch printf format mistakes?

Compile with warnings enabled:

gcc -Wall -Wextra -Wformat file.c

What should I use for uint64_t?

Prefer <inttypes.h> macros such as PRIu64 for portable code.

Do I need UL on the numeric literal?

Not always, but it helps make the literal type explicit when assigning to an unsigned long.

unsigned long x = 100UL;
Take Quiz

Mini Project

Description

Build a small C program that prints several integer variables using printf. The purpose is to practice matching each variable type with the correct format specifier and to see how readable diagnostic output is written in real programs.

Goal

Create a program that correctly prints int, unsigned int, long, unsigned long, and unsigned long long values using the proper printf specifiers.

Requirements

  • Declare at least five integer variables of different C integer types.
  • Print each variable on its own line with a descriptive label.
  • Use the correct printf format specifier for every variable.
  • Include at least one unsigned long example.
  • Compile cleanly with format warnings enabled.
Take Quiz

Keep learning

Related questions

View more