Development

How to check the value of a variable in C

printf is the obvious answer and the one that misleads you. Format specifiers, why the debugger beats print statements, and how the optimiser makes variables disappear.

The quick answer is printf, and the trap is that the wrong format specifier prints a plausible wrong number rather than failing.

bash
#include <stdio.h>

int main(void) {
    int count = 42;
    double ratio = 0.75;
    char *name = "sensor";

    printf("count = %d\n", count);
    printf("ratio = %f\n", ratio);
    printf("name  = %s\n", name);
    return 0;
}

Format specifiers, and why mismatches are dangerous

C does not check that your specifier matches your argument. A mismatch is undefined behaviour — often garbage, sometimes a crash, occasionally a value that looks right until it does not.

  • %d — int; %u — unsigned int
  • %ld — long; %lld — long long
  • %zusize_t. Using %d here is a very common bug on 64-bit systems.
  • %f — double (a float is promoted to double, so %f is correct for both)
  • %p — pointer, cast to (void *)
  • %c — char; %s — a null-terminated string, never a single char
  • %x — hexadecimal, invaluable for flags and masks
bash
size_t len = strlen(buffer);
printf("len = %zu\n", len);            // correct
printf("len = %d\n", len);             // wrong, and may print nonsense

printf("ptr = %p\n", (void *)&count);  // the cast is required

Turn the warnings on and the compiler will catch these:

bash
gcc -Wall -Wextra -Wformat=2 -g -O0 program.c -o program

stdout is line-buffered to a terminal and fully buffered to a pipe or file. If the program crashes, buffered output is lost — so the print statement before the crash never appears, and you conclude the crash happened earlier than it did.

bash
fprintf(stderr, "reached parse loop, i = %d\n", i);   // unbuffered

This one change removes a whole class of misleading debugging sessions.

A debug macro that can be switched off

bash
#ifdef DEBUG
  #define DBG(fmt, ...) \
      fprintf(stderr, "%s:%d: " fmt "\n", __FILE__, __LINE__, ##__VA_ARGS__)
#else
  #define DBG(fmt, ...) ((void)0)
#endif

DBG("count = %d, ratio = %.3f", count, ratio);

Compile with -DDEBUG to enable it. The file and line come free, and the release build contains none of it.

The debugger is better than printing

Print statements require you to guess in advance which variable matters. A debugger lets you ask afterwards.

bash
gcc -g -O0 program.c -o program
gdb ./program
bash
(gdb) break parse_packet          # stop when this function is called
(gdb) run
(gdb) print count                 # inspect any variable in scope
(gdb) print *ptr                  # follow a pointer
(gdb) print arr[3]@5              # five elements starting at index 3
(gdb) watch total                 # stop whenever total changes
(gdb) backtrace                   # how did we get here
(gdb) finish                      # run to the end of this frame

watch is the one worth knowing. "This value is wrong and I don't know who wrote it" is answered directly by a watchpoint, and awkwardly by print statements.

When the variable has vanished

With optimisation enabled, gdb may report <optimized out>. The variable was kept in a register or eliminated entirely — the code is correct, but there is no storage to inspect. Build with -O0 -g while debugging.

The related trap: a bug that disappears at -O0 and returns at -O2 is usually undefined behaviour, not a compiler bug. Run it under a sanitizer before blaming the optimiser:

bash
gcc -g -O1 -fsanitize=address,undefined program.c -o program && ./program

That will name the line and the fault far faster than adding print statements around it.

Arslan ud Din Shafiq

Founder and lead editor of LearnCybers. Full-stack engineer with expertise in Linux systems, cybersecurity, cloud infrastructure and web development. Writing about practical technology since 2019.

Related reading

Newsletter

Get smarter about security

Practical guides, tooling notes and the developments actually worth your attention — delivered when there is something worth saying.

No spam. Unsubscribe in one click.