-
Notifications
You must be signed in to change notification settings - Fork 0
printf Format String — Terminology
printf("blabla1 %d\n", d);
// |<------------>| — string literal (C syntax)
// |<---------->| — format string (printf semantics)
// ^^^^^^^ — literal text
// ^^ — conversion specifier
// ^^ — escape sequence| 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 byprintf. In everyday usage both terms are used interchangeably for the whole quoted string — that is sloppy but universally understood.
| 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 %
|
% |
| 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
|
| Flag | Meaning | Example |
|---|---|---|
- |
Left-align | "%-10s" |
+ |
Always show sign |
"%+d" → +42
|
0 |
Zero-pad |
"%05d" → 00042
|
# |
Alternate form |
"%#x" → 0xff
|
| 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
| 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 |
.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 1418All 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 |
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) argumentThis catches mismatches like log_msg(__FILE__, __LINE__, "%d", "string") at compile time.