How Do I Store and Display Data with Variables and I/O?
printf, scanf, format specifiers, and why C makes you do what Java did automatically
After this lesson, you will be able to:
- Use
printfwith format specifiers (%d,%f,%.2f,%c,%s) to produce formatted output - Use field width, alignment, and zero-padding in
printfformat strings - Use
scanfwith the&operator to read typed values from user input - Check
scanf’s return value to detect invalid input - Clear the input buffer to prevent the newline buffer problem between
scanfcalls - Use explicit type casting to avoid integer division truncation
Why Can’t I Just println?
In Java:
int age = 20;
System.out.println("I am " + age + " years old.");
Java automatically converts age to a string and concatenates. Easy. Now try the C equivalent:
int age = 20;
printf("I am %d years old.\n", age);
That %d is a format specifier — you must explicitly tell printf “an integer goes here.” If you use the wrong one, you don’t get a compile error — you get garbage output or a crash. Welcome to C.
System.out.println("Age: " + age). Why doesn't printf("Age: " + age) work in C?+ operator on strings does pointer arithmetic, not concatenation. C's printf uses format specifiers (%d, %f, %s) to embed values into output. This is more explicit but gives you precise control over formatting.
Output, Input, and Everything Between
printf: Formatted Output
printf (print formatted) takes a format string and values to substitute:
printf("format string", value1, value2, ...);
Essential format specifiers:
| Specifier | Type | Example | Output |
|---|---|---|---|
%d |
int |
printf("%d", 42) |
42 |
%f |
double |
printf("%f", 3.14) |
3.140000 |
%.2f |
double (2 decimals) |
printf("%.2f", 3.14) |
3.14 |
%c |
char |
printf("%c", 'A') |
A |
%s |
string (char*) |
printf("%s", "hello") |
hello |
%ld |
long |
printf("%ld", 123456L) |
123456 |
%x |
int (hex) |
printf("%x", 255) |
ff |
%% |
literal % |
printf("100%%") |
100% |
Common Pitfall: The format specifier MUST match the type of the value.
printf("%d", 3.14)doesn’t print3— it prints garbage because it interprets the double’s bytes as an integer. The compiler won’t stop you (without-Wall).
printf("Score: %08.2f\n", 3.14) output?%08.2f means: total width 8, 2 decimal places, zero-padded. The value 3.14 takes 4 characters (3.14), so the remaining 4 positions are filled with zeros. Option D would be the output of %f with no width or precision specified (default is 6 decimal places).
Escape Sequences
| Sequence | Meaning |
|---|---|
\n |
Newline |
\t |
Tab |
\\ |
Literal backslash |
\" |
Literal quote |
\0 |
Null terminator (end of string) |
Unlike Java’s println, printf does NOT automatically add a newline. You must include \n yourself.
Field Width and Alignment
Control how values are formatted:
printf("%10d\n", 42); // 42 (right-aligned, width 10)
printf("%-10d\n", 42); // 42 (left-aligned, width 10)
printf("%08d\n", 42); // 00000042 (zero-padded)
printf("%8.2f\n", 3.14159); // 3.14 (width 8, 2 decimals)
This is essential for building aligned tables in your lab output.
scanf: Reading Input
scanf reads formatted input from the keyboard:
int age;
printf("Enter your age: ");
scanf("%d", &age);
Key Insight: Notice the
&beforeage? That’s the address-of operator.scanfneeds to know where in memory to store the value, so you pass the variable’s address, not the variable itself. Forgetting&is one of the most common C bugs — and it often causes a crash.
scanf format specifiers:
| Specifier | Type | Note |
|---|---|---|
%d |
int |
|
%f |
float |
NOT double! |
%lf |
double |
l for “long float” |
%c |
char |
|
%s |
string | Stops at whitespace |
Common Pitfall:
scanfuses%lffordouble, butprintfuses%f. Yes, they’re different. This catches everyone at least once. When reading adouble:scanf("%lf", &x). When printing adouble:printf("%f", x).
scanf("%d", age) instead of scanf("%d", &age)?&, scanf receives the current value of age (which could be garbage if uninitialized) and tries to write to that address in memory. This is undefined behavior — usually a segmentation fault (crash). The code compiles because C doesn't enforce pointer type safety strictly, but -Wall will warn you.
The Temperature Converter
Let’s build a complete program — a Fahrenheit to Celsius converter:
#include <stdio.h>
int main(void)
{
double fahrenheit;
double celsius;
printf("Enter temperature in Fahrenheit: ");
scanf("%lf", &fahrenheit);
celsius = (fahrenheit - 32.0) * 5.0 / 9.0;
printf("%.1f°F = %.1f°C\n", fahrenheit, celsius);
return 0;
}
Compile and run:
gcc -Wall -o tempconv tempconv.c
./tempconv
Enter temperature in Fahrenheit: 212
212.0°F = 100.0°C
Notice that both variables are declared at the top of the function, before any executable statements. Modern C (C99+) lets you declare variables anywhere, but Dr. Steiner’s convention is top-of-block declarations — declare everything at the top of each {...} block. This makes it easy to see at a glance what variables a function uses.
From Java: In Java, you’d use
Scanner scanner = new Scanner(System.in)andscanner.nextDouble(). In C,scanf("%lf", &fahrenheit)does the same thing — but you must specify the format and pass the address. There’s noScannerobject, no.nextDouble()method. It’s more manual, but it’s closer to how the hardware actually works. Fun fact: Java’sSystem.out.printf()uses the same format specifiers as C’sprintf— Java borrowed the syntax directly.
Checking scanf’s Return Value
scanf returns the number of items successfully read:
int result = scanf("%d", &age);
if (result != 1)
{
printf("Invalid input!\n");
return 1;
}
If the user types “hello” when you expect a number, scanf returns 0 (no items read). Always check the return value in production code.
The Newline Buffer Problem
This is one of the trickiest bugs for beginners:
int age;
char initial;
printf("Enter age: ");
scanf("%d", &age);
printf("Enter initial: ");
scanf("%c", &initial); // BUG: reads the leftover newline!
When you type 20 and press Enter, scanf("%d") reads 20 but leaves the \n in the input buffer. The next scanf("%c") immediately reads that \n instead of waiting for your input.
The fix:
scanf("%d", &age);
while (fgetc(stdin) != '\n') {} // Clear the input buffer
scanf("%c", &initial); // Now this works correctly
Common Pitfall: When mixing
scanfcalls for different types, the newline from pressing Enter stays in the input buffer. Usewhile (fgetc(stdin) != '\n') {}after reading numeric values to clear the buffer before reading characters.
Integer Division
One more gotcha from Java carries over but hits harder in C:
Predict: What do you think
averageequals after this code runs? Work it out on paper before reading the answer.
int sum = 7;
int count = 2;
double average = sum / count; // average = 3.0, NOT 3.5!
Both operands are int, so C performs integer division (truncating the decimal). Fix with a cast:
double average = (double)sum / count; // average = 3.5
The Trick: When computing an average or any division that should produce a decimal result, cast the numerator to
doublebefore dividing:(double)sum / count. Casting either operand promotes the whole expression to floating-point.
Why does this matter?
The printf/scanf format string system is how C handles all text output and input. There’s no toString() method, no string concatenation with +, no automatic type conversion. If you can’t write a correct format string, you can’t display results or read user input — and that means you can’t complete any lab in this course.
scanf("%d", &age) and the user types hello. What happens?scanf can't parse "hello" as an integer, so it reads nothing, returns 0, and leaves the input in the buffer. The variable age keeps whatever value it had before (garbage if uninitialized). This is why you should check scanf's return value: if (scanf("%d", &age) != 1). The input stays in the buffer, which can cause infinite loops if you retry without clearing it.
Quick Check: What's wrong with scanf("%f", &temperature) when temperature is a double?
%f in scanf reads a float, not a double. For double, you must use %lf. This mismatch writes 4 bytes into an 8-byte variable, causing incorrect values. (Confusingly, printf uses %f for both float and double.)
Quick Check: Why does scanf need & before the variable?
scanf needs to store a value into the variable’s memory location. The & (address-of) operator gives scanf the memory address where it should write the value. Without &, scanf receives the variable’s value (which it interprets as a memory address) — usually causing a crash.
Quick Check: What does 7 / 2 evaluate to in C? How about 7.0 / 2?
7 / 2 evaluates to 3 (integer division, truncated). 7.0 / 2 evaluates to 3.5 because one operand is a double, promoting the division to floating-point.
You Control the Format
In Java, System.out.println handles formatting automatically. In C, you control every detail: how many decimal places, how wide the columns, whether to pad with zeros. That’s more work, but it’s also more power — essential for producing professional-looking lab output.
Next lesson: operators and expressions. You’ll learn arithmetic, comparison, logical, and bitwise operators — plus the infamous = vs. == trap that catches every C beginner.
Sanity Check: Before moving on, make sure you can: (1) write a program that reads a
doublewithscanfand prints it withprintf, (2) explain why&is needed inscanf, and (3) fix the newline buffer problem. These three skills are prerequisites for every lab from here on.
Big Picture: In Java, I/O is handled by objects (
Scanner,System.out). In C, it’s handled by functions with format strings. This is your first taste of C’s philosophy: explicit control over everything, automatic help with nothing. The format string system also connects to security — format string vulnerabilities are a real class of exploits you’ll encounter in cybersecurity courses.