Skip to content

printf Format String — Terminology

MarekBykowski edited this page Jun 23, 2026 · 1 revision

Anatomy

printf("blabla1 %d\n", d);
//     |<------------>| — string literal (C syntax)
//      |<---------->|  — format string (printf semantics)
//      ^^^^^^^          — literal text
//             ^^        — conversion specifier
//               ^^      — escape sequence

Terms

Term Example Domain Description
string literal "blabla1 %d\n" (with quotes) C language / syntax hardcoded value in source, lives in .rodata
format string blabla1 %d\n (content/meaning) printf convention template interpreted by printf at runtime
literal text blabla1 printf convention plain text, printed as-is
conversion specifier %d printf convention placeholder for a value (% introducer + d specifier character)
escape sequence \n C language / syntax special character encoded as backslash sequence

Precise usage: "blabla1 %d\n" is a string literal that is used as a format string by printf. In everyday usage both terms are used interchangeably for the whole quoted string — that is sloppy but universally understood.


Conversion Specifiers

Specifier Type Example output
%d / %i Signed decimal int -42
%u Unsigned decimal int 42
%o Unsigned octal 10
%x / %X Unsigned hex lower/upper ff / FF
%f Decimal float 3.140000
%e / %E Scientific notation 3.140000e+00
%g / %G Shorter of %f / %e 3.14
%c Character A
%s String hello
%p Pointer address 0x7ffd...
%% Literal % %

Length Modifiers

Modifier Type
hh char / unsigned char
h short / unsigned short
l long / unsigned long
ll long long / unsigned long long
z size_t / ssize_t
t ptrdiff_t
j intmax_t / uintmax_t

Flags

Flag Meaning Example
- Left-align "%-10s"
+ Always show sign "%+d"+42
0 Zero-pad "%05d"00042
# Alternate form "%#x"0xff

String Literal Storage

Declaration Section Note
printf("hello") .rodata no variable, read-only
const char *s = "hello" .rodata pointer on stack, string in .rodata
char s[] = "hello" .rodata + stack .rodata holds original, stack holds copy
static char s[] = "hello" .rodata + .data .rodata holds original, .data holds copy

Writing to a string literal is undefined behavior:

char *s = "hello";
s[0] = 'H';       // UB — segfault, .rodata is read-only

char s[] = "hello";
s[0] = 'H';       // OK — copy on stack, writable

Memory Sections — Full Picture

What Section Note
String literals "hello" .rodata read-only, shared across all uses
Integer/float literals 42, 3.14, 'A' .text embedded as immediate in instruction encoding
Global / static, uninitialized .bss zeroed by loader, not stored in binary
Global / static, initialized to 0 .bss compiler treats same as uninitialized
Global / static, initialized non-zero .data stored in binary
Local variable stack not zeroed — garbage unless explicitly set
malloc / heap heap always explicit, never holds literals

Why .bss Exists

.bss does not store actual bytes in the binary — only a size. The OS/loader zeroes the memory at startup. This keeps the binary small even when you have large zero-initialized buffers.

size a.out
#    text    data     bss     dec
#    1234      56     128    1418

Zero-Initialization Rule (C11 §6.7.9)

All variables with static storage duration are zero-initialized by the C standard — even without explicit = 0.

int g;              // .bss — zero (guaranteed)
int *p;             // .bss — NULL
float f;            // .bss — 0.0
static int x;       // .bss — zero (guaranteed)

void foo() {
    int y;          // stack — GARBAGE (undefined!)
}
Storage class Zeroed?
Global yes — guaranteed by standard
Static local yes — guaranteed by standard
Local / auto no — undefined, garbage

GCC/Clang Format String Attribute

When wrapping printf in a custom logger, annotate with __attribute__((format)) so the compiler type-checks the format string against variadic args:

static inline void log_msg(const char *file, int line, const char *fmt, ...)
    __attribute__((format(printf, 3, 4)));
//                              ^  ^
//                              |  +-- index of first variadic argument
//                              +----- index of fmt (format string) argument

This catches mismatches like log_msg(__FILE__, __LINE__, "%d", "string") at compile time.

Clone this wiki locally