How scanf Parses User Input into Memory in C
The scanf function in the C standard library reads
character sequences from the standard input stream (stdin),
interprets them according to specified conversion specifiers, and writes
the converted values directly into memory buffers via memory addresses
provided by the caller. This article explains the internal mechanics of
scanf, detailing how it retrieves data from the input
buffer, matches control strings, handles whitespace, performs data
conversion, and manipulates target memory locations.
The Input Stream and Buffering
When a user types input in a terminal and presses "Enter", the
operating system delivers these keystrokes as a sequence of ASCII
characters to the standard input stream (stdin). The C
runtime maintains an underlying file buffer for stdin. The
scanf function does not read directly from keystroke
hardware events; instead, it consumes characters sequentially from this
memory stream using an internal stream pointer.
Format String Processing and Whitespace Handling
The first argument to scanf is a format control string
containing format specifiers (such as %d, %f,
or %s) and optional literal characters. scanf
traverses this format string character by character:
- Whitespace characters: A space, tab, or newline
inside the format string causes
scanfto read and discard all contiguous whitespace characters in the input stream until it encounters the first non-whitespace character. - Specifier default whitespace skipping: Most
conversion specifiers (including
%d,%f, and%s) automatically skip leading whitespace in thestdinbuffer before reading actual data. Specifiers like%cand the scanset%[...]are exceptions and consume whatever character comes next, including spaces or newlines. - Literal matching: If the format string contains
literal characters,
scanfreads characters from the input stream to verify an exact match. If the characters do not match, parsing halts immediately.
Parsing and Data Conversion
Once scanf encounters a conversion specifier and
positions the input stream past any ignored whitespace, it begins the
lexing process:
- Character collection: It consumes characters from
stdinone at a time as long as they fit the expected character set for that specific type. For instance, for%d, it accepts an optional sign (+or-) followed by decimal digits (0–9). For%f, it reads digits, an optional decimal point, and optional exponential notations (eorE). - Delimiter termination: Reading stops as soon as a
character violates the grammar of the specifier or reaches a defined
field width. The non-matching character is not discarded; it is pushed
back onto the stream buffer via an internal
ungetcmechanism, leaving it available for subsequent input operations. - Binary encoding: Once the string representation is
collected,
scanfconverts the ASCII sequence into the target architecture's native binary representation. An integer string like"1042"is converted to two's-complement binary, while a floating-point string is parsed into IEEE 754 standard binary format.
Writing Directly to Memory Buffers
The arguments following the format string must be valid memory
addresses (pointers). Once scanf completes the
conversion:
- Type casting and assignment: The internal routines
cast the target address to the appropriate pointer type (e.g.,
int*,double*, orchar*). - Dereferencing:
scanfdereferences the pointer and writes the parsed binary value into the memory segment starting at that address. - String handling: When parsing strings with
%s,scanfreads non-whitespace characters and copies their raw bytes sequentially into the target buffer, automatically appending a null terminator byte (\0) at the end. Without an explicit field width limiter (e.g.,%63s),scanfcontinues writing until whitespace is reached, which causes buffer overflows if the input exceeds the allocated array capacity.
Return Values and Parsing Failures
The function tracks the number of successful assignments and returns
this count as an integer. If a conversion fails (e.g., text is supplied
where %d expects an integer), scanf leaves the
unparsed characters in stdin, abandons further processing
of the format string, and returns the count of variables successfully
assigned up to that failure point. If the stream encounters an
end-of-file condition before any successful match occurs, it returns
EOF.