Variables, Data Types and printf scanf in C: C Learning Guide, Part 2
A variable is a named storage box: its name is how you reach it, its type decides what it can hold, and its value is what sits inside right now. Once a program can keep values in variables, read fresh values from the keyboard with scanf and print results with printf, it stops being a fixed script and starts answering new inputs.
This is Part 2 of the C programming guide for beginners. Part 1 turned words into a tested program whose values were fixed in the source code. This chapter keeps the same packing problem and teaches it to ask: how many parts arrived today, and what is a full box worth? You need only Part 1's vocabulary: main, printf and const.
1. Variables in C: a named, typed box
A variable is a named place in the computer's memory whose content can change while the program runs. Think of a labelled parts bin on the lab bench: the label is the name, the kind of part the bin accepts is the type, and the parts inside right now are the value.
| Part | Example | Plain meaning |
|---|---|---|
| Name | items | How your code refers to the box |
| Type | int | What kind of value fits: whole numbers here |
| Value | 23 | What is stored right now; it can be replaced |
A declaration creates the box. Read this line word by word:
int items = 23; - int is the type keyword for whole numbers such as -3, 0 or 23. items is the name you chose. = 23 is the initializer: the starting value placed in the box. Here = means "store", not mathematical equality. The semicolon ends the statement, like a full stop ends a sentence.
An assignment replaces the value later. This program has no sample keyboard input:
#include <stdio.h>
int main(void)
{
int items = 23;
printf("Items at start: %d\n", items);
items = 29;
printf("Items after delivery: %d\n", items);
return 0;
}
Verified output
Items at start: 23 Items after delivery: 29
items = 29; does not create a second box. It opens the existing box named items, throws away 23 and stores 29. The two printf calls prove it: the same name printed different values at different times.
Naming rules. A name can use letters, digits and the underscore _, but it cannot start with a digit. Names are case-sensitive: items and Items are two different boxes. And a name cannot be a keyword, one of the words the language owns, such as int or return:
int return = 5; /* error: return is a keyword, not an allowed name */
Choose names that say what the value means. items helps a reader; x7 makes every later line harder.
2. Data types in C: choose a type that matches the value
A type is the contract for a box: what values it can hold and what you may do with them. This chapter uses four:
| Type | Holds | Example values | Use it for |
|---|---|---|---|
int | Whole numbers, positive and negative | 0, 23, -7 | Counts: parts, boxes, people |
double | Decimal numbers, about 15 significant digits | 12.50, 0.33, -2.75 | Measurements and money-like quantities: prices, averages |
char | One character, stored as a small integer code | 'A', '7', '+' | Single symbols; real text needs arrays, later |
_Bool | Only 0 (false) or 1 (true) | 0, 1 | Yes/no facts; used heavily from Part 3 |
Memory is measured in bytes: one byte is the amount of memory that holds one char. How many bytes does each type need? The language sets minimums, not exact sizes, so the honest answer is: measure it. The sizeof operator reports the size of a type or value in bytes on the platform running the program:
#include <stdio.h>
int main(void)
{
printf("char: %zu byte\n", sizeof(char));
printf("int: %zu bytes\n", sizeof(int));
printf("double: %zu bytes\n", sizeof(double));
return 0;
}
Verified output
char: 1 byte int: 4 bytes double: 8 bytes
These numbers are this runner's answer, not a law: an int must be at least 16 bits, and on most modern desktops and phones it is 4 bytes. %zu is the printf conversion for the value sizeof produces, whose type is called size_t, an unsigned whole-number type made for sizes. The details of size_t are not needed yet; only remember that sizeof's answer prints with %zu.
Wrong turn: overflow. Every integer type has a largest value. For a 4-byte int it is 2,147,483,647, written as the constant INT_MAX from the header limits.h. Adding 1 to the largest int does not give a bigger number: the result is undefined, which means the language makes no promise at all. Counts that can grow without bound need a plan, not just a bigger box; this chapter simply stays far from the limits.
3. Integer division drops the fraction
Part 1 used 23 / 6 = 3 because both operands are int: division between two integers answers "how many whole times", and the remainder is simply dropped, never rounded. This matters the moment you want an average or a price. Watch the same values give two different answers:
#include <stdio.h>
int main(void)
{
int total_minutes = 317;
int people = 6;
printf("Integer share: %d\n", total_minutes / people);
printf("Real share: %.2f\n", (double)total_minutes / people);
return 0;
}
Verified output
Integer share: 52 Real share: 52.83
The first printf divides int by int: 6 goes into 317 exactly 52 whole times (52 x 6 = 312, and 5 minutes remain). The second converts one operand to double first, so the division happens in decimal form and keeps the fraction.
(double)total_minutes is a cast: a request to convert a value to another type before it is used. Placement decides everything:
| Expression | Result | Why |
|---|---|---|
317 / 6 | 52 | int / int: whole times, fraction dropped |
317 % 6 | 5 | remainder after the whole division |
(double)317 / 6 | 52.8333... | conversion happens first, so decimal division runs |
(double)(317 / 6) | 52.0 | int division runs first and drops .8333; converting afterwards cannot restore it |
The last row is the classic exam trap: casting the finished answer cannot recover a fraction that was already thrown away.
4. Read values with scanf in C
scanf is printf's mirror: it reads characters from standard input (the keyboard, by default), converts them according to a format string, and stores the results in your variables. This is the line that turns the fixed packing program into a tool:
#include <stdio.h>
int main(void)
{
int items;
int per_box;
printf("Items and box capacity: ");
scanf("%d %d", &items, &per_box);
printf("Full boxes: %d\n", items / per_box);
printf("Leftover items: %d\n", items % per_box);
return 0;
}
Sample input: 23 6
Verified output
Items and box capacity: Full boxes: 3 Leftover items: 5
Line by line:
| Line | What it does | Why it is written this way |
|---|---|---|
int items; | Declares the box without a starting value | The value is unknown until the user types it |
printf("Items and box capacity: "); | Shows what to type | A prompt; it has no \n, so the answer appears on the same line, as the verified output shows |
scanf("%d %d", &items, &per_box); | Reads two whole numbers separated by spaces or newlines and stores them | %d matches an int; the order in the format string matches the order of the boxes |
printf("Full boxes: %d\n", items / per_box); | Computes and prints | Exactly the Part 1 formulas, now on fresh input |
The & operator gives the address of a variable: the exact location of its box in memory. scanf must know where to put the value it read, so every variable after the format string is written with &. printf only reads values, so it never needs &.
Wrong turn: the missing &.
scanf("%d", items); /* wrong: scanf needs the address, written &items */
Without &, scanf receives a number instead of a location and will try to write to a meaningless place: undefined behavior, which often ends in a crash. The compiler can warn about this; the warnings in this guide's builds are treated as errors, so this line cannot pass unnoticed.
Two practical notes. Whitespace - spaces, tabs or newlines - between the two numbers is all the same to %d, so typing the values on two lines also works. And scanf returns how many values it actually stored: if the user types text instead of a number, scanf fails, stores nothing new, and returns a smaller count. Part 3 will use that count to refuse bad input.
5. printf in C: conversions that match the type
printf already knows %d from Part 1. The conversions this chapter adds:
| Conversion | Works with | Prints / reads |
|---|---|---|
%d | int | Whole number in base 10 |
%f | double in printf | Decimal number, six digits after the point by default |
%.2f | double in printf | Decimal number rounded to two digits after the point |
%c | char | One character |
%zu | size_t | The answer of sizeof |
%lf | double in scanf | Reads a decimal number into a double |
The asymmetry in the last row is a real exam question: printf's %f already expects a double, while scanf needs %lf ("long float", the old name for double) to store into a double. Now the packing problem learns about money. A full box sells for a price with two decimal places:
#include <stdio.h>
int main(void)
{
int items;
int per_box;
double price_per_box;
printf("Items, capacity and price per box: ");
scanf("%d %d %lf", &items, &per_box, &price_per_box);
int full_boxes = items / per_box;
double revenue = full_boxes * price_per_box;
printf("Full boxes: %d\n", full_boxes);
printf("Revenue: %.2f\n", revenue);
return 0;
}
Sample input: 23 6 12.50
Verified output
Items, capacity and price per box: Full boxes: 3 Revenue: 37.50
One new detail does quiet work here: full_boxes * price_per_box multiplies an int by a double. C cannot multiply two different types directly, so it converts the int to double first and the result is a double. This automatic widening is called the usual arithmetic conversions; it is the same rule that made 5 / 2.0 equal 2.5. Printing 37.50 with %.2f then rounds only the display, never the stored value.
6. Wrong turns and meaningful checks
Wrong turn: reading a variable that was never given a value.
#include <stdio.h>
int main(void)
{
int left;
printf("%d\n", left); /* left was never given a value */
return 0;
}
Unlike Part 1's const boxes, int left; declares storage but fills it with nothing. Printing it is undefined behavior: some builds print leftover garbage, some print 0, and changing an unrelated line can change the answer. The compiler warns; this guide's build settings turn that warning into an error, because a value that was never set cannot be reasoned about.
Meaningful checks. A test is only meaningful when the expected answer is decided before running. These inputs for the packing reader were chosen to touch every branch of the arithmetic, then executed:
| Input | Expected full boxes | Expected leftover | What it checks |
|---|---|---|---|
| 23 6 | 3 | 5 | ordinary case with a remainder |
| 24 6 | 4 | 0 | exact division, no leftover |
| 5 6 | 0 | 5 | fewer items than one box holds |
All three matched when executed. The pattern generalizes: test the ordinary case, the boundary where a remainder becomes 0, and the boundary below one full unit.
7. C programs for practice with solutions
P1 (predict). Without running it, state what these four lines print:
int a = 5;
int b = 2;
printf("%d\n", a / b);
printf("%.1f\n", (double)a / b);
P2 (repair). This program should read a count and a price, then print the total. It has three faults. Find them before reading the answers:
#include <stdio.h>
int main(void)
{
int count;
double price;
printf("Count and price: ");
scanf("%d %lf", count, price);
printf("Total: %d\n", count * price);
return 0;
}
P3 (write). The lab buys sealed boxes. Write a program that reads the number of boxes, the capacity of each box and the price per box (a decimal number), then prints the total items, the total cost to two decimal places, and the average cost per item to two decimal places.
Checked answers and reasoning
P1. The first printf prints 2: both operands are int, so 5 / 2 keeps only the whole part. The second prints 2.5: the cast converts a to double before the division, and %.1f displays one digit after the point. If you predicted 2.5 and 2.5, reread section 3.
P2. Fault 1: scanf("%d %lf", count, price) is missing & on both variables, so scanf receives values instead of addresses. Fault 2: %d in printf expects an int, but count * price is a double; the conversion must be %.2f or %f. Fault 3 is the quiet one: the program gives no prompt, so the user stares at a blank screen with no idea what to type - a design fault, not a language error. The repaired program:
#include <stdio.h>
int main(void)
{
int count;
double price;
printf("Count and price: ");
scanf("%d %lf", &count, &price);
printf("Total: %.2f\n", count * price);
return 0;
}
Sample input: 4 9.99
Verified output
Count and price: Total: 39.96
Transfer answer: a complete program
P3. One correct version:
#include <stdio.h>
int main(void)
{
int boxes;
int per_box;
double price_per_box;
printf("Boxes, capacity and price per box: ");
scanf("%d %d %lf", &boxes, &per_box, &price_per_box);
int total_items = boxes * per_box;
double total_cost = boxes * price_per_box;
double cost_per_item = total_cost / total_items;
printf("Total items: %d\n", total_items);
printf("Total cost: %.2f\n", total_cost);
printf("Cost per item: %.2f\n", cost_per_item);
return 0;
}
Sample input: 5 6 12.50
Verified output
Boxes, capacity and price per box: Total items: 30 Total cost: 62.50 Cost per item: 2.08
The order of operations matters: total_items must exist before cost_per_item uses it, and dividing by total_items works here only because the input had at least one box. Part 3's decisions will guard that hole. Check P3 against a second executed input: 3 boxes of 4 at 9.99 gives 12 items, 29.97 total and 2.50 per item.
What was checked, and what comes next
Every runnable example in this chapter was compiled with GCC in C17 mode with warnings treated as errors, then executed; the outputs shown are the actual outputs, and the extra checks in section 6 and P3 were executed too. Two honest limits: sizeof answers describe this runner, not every computer, and nothing here handles bad input yet - that needs decisions.
Programming in C learning route
Part 3 adds decisions with if and else, so the packing tool can refuse a zero capacity or a missing number instead of trusting the typist. The route then continues through loops, functions, arrays and strings, pointers, structures and files, and a small project, linked from the series index as chapters publish. For extra programs on variables and input, use the C lab file; for rapid revision, the Exam Notes.
Sources and scope
- cppreference: declarations for declaration, initialization and naming rules.
- cppreference: arithmetic types for int, double, char, _Bool and their ranges.
- cppreference: implicit conversions for the usual arithmetic conversions.
- cppreference: scanf for format matching, the address argument and the return count.
- cppreference: sizeof for sizes, bytes and size_t.
The lab story, practice questions, wrong turns and explanations are original. Language rules were checked against the references above; sample behavior was checked by execution on this runner. The notes use C17-compatible examples in a hosted environment and are learning material, not certification of any tool or a guarantee of passing any exam.
Comments
Post a Comment