From a Problem to a C Program: C Learning Guide, Part 1
Start with the problem, not the code. Decide what information you have and what answer you need. Write the steps, work through an example by hand, then write the C program. Run it and compare its answer with one you worked out separately.
This is Part 1 of the Programming in C learning guide, the first subject in our semester-by-semester learning track. Start here if you have never written a program. You need whole-number arithmetic, not previous C knowledge. Semester labels are a study route, not a claim that every institution teaches the same syllabus.
1. Turn vague words into a precise problem
A program is a set of instructions expressed in a programming language. Before choosing those instructions, decide what a correct answer means. "Write a packing program" is incomplete: are we counting completely filled boxes, boxes needed to carry everything, or items that remain unpacked? Those are different outputs.
Our problem: a lab has 23 small parts. A box holds 6 parts. Find the number of completely filled boxes and the number of parts left over. All boxes have the same capacity. We do not split a part.
| Contract item | Meaning in this lesson |
|---|---|
| Inputs | items = 23; per_box = 6 |
| Valid domain | Whole numbers: 0 through 30,000 items; 1 through 30,000 parts per box |
| Outputs | Full boxes and leftover items, both whole numbers |
| Postcondition | full_boxes × per_box + leftover = items; 0 ≤ leftover < per_box |
The allowed input range is a precondition: a rule the inputs must meet before we calculate. A postcondition is a rule the result must meet. The table above lists both. These small numbers fit C's int type, which stores whole numbers. The multiplication in our check also fits. Larger numbers need another range check.
Inputs are the values a program works with. They need not come from a keyboard. Here we put 23 and 6 directly in the code. That lets us focus on solving the problem before learning how to read and check typed input.
2. Write the algorithm before the syntax
An algorithm is a clear set of steps that finishes and solves a problem. It does not need C code. Here are the steps in plain English:
- Take the valid values items and per_box.
- Divide items by per_box and keep the whole-number quotient as full_boxes.
- Take the remainder as leftover.
- Display both values, with labels.
- Stop.
For nonnegative whole numbers, 23 = 3 × 6 + 5. Therefore three boxes are completely full and five parts remain. Four boxes would be needed to carry every part, but only three are full. Adding one automatically is wrong when there is no remainder: 24 parts fill exactly four boxes of six.
- 1. Specify23 parts, 6 per box; count full boxes and leftovers
- 2. CalculateQuotient 3; remainder 5
- 3. Check3 × 6 + 5 = 23; remainder is less than 6
A formal flowchart draws these steps with symbols. Use a start/stop symbol, an input/output symbol for reading or displaying values, and a rectangle for a calculation. Arrows show the order. A diamond shows a decision, such as "Is the box size greater than zero?" Our fixed inputs are already valid, so this version needs no decision.
Sequence means that steps happen in order. Selection means choosing a path using a condition; repetition means doing steps again. This chapter uses sequence only. Do not add a loop just because a problem asks for an algorithm.
3. Dry-run the values and prove the check
A dry run follows the steps manually, recording the value produced at each step. It is not executing the program on a computer. Use a trace table rather than guessing what a line will do.
| Step | Operation | Result | Reason |
|---|---|---|---|
| 1 | Set items | 23 | Given input |
| 2 | Set per_box | 6 | Given capacity |
| 3 | 23 / 6 using integer operands | full_boxes = 3 | Only 3 complete groups of 6 fit |
| 4 | 23 % 6 | leftover = 5 | 23 − 3 × 6 = 5 |
| 5 | Reconstruct the original count | 3 × 6 + 5 = 23 | Nothing lost or invented |
Both checks matter. The wrong answer full_boxes = 2, leftover = 11 still passes 2 × 6 + 11 = 23. But 11 parts could fill another box of 6. So we also check 0 ≤ leftover < capacity: the leftover count must be zero or more, but smaller than one full box. Only then do we have the right answer.
One hand-worked example does not prove the method works for every input. We also reason about the calculation and test values near the allowed limits. In C17, division between integers removes the fractional part toward zero. For our values, 23 / 6 gives 3, not 3.833. This packing example does not allow negative item counts.
4. Read your first complete C program
A compiler turns C source code into a program the computer can run. First, check that you can build and run this small program. It asks for no keyboard input:
#include <stdio.h>
int main(void)
{
printf("Plan, trace, test.\n");
return 0;
}
Verified output
Plan, trace, test.
#include <stdio.h> gives the compiler information about standard input/output functions, including printf. This line prints nothing. main is where this program starts. In int main(void), void means main takes no values from its caller. int means it returns a whole-number status when it finishes. The braces { } enclose its instructions.
printf displays the text inside double quotes. Here, standard output is the runner's output area. \n starts a new line; it does not display a backslash and an n. The semicolon ends the instruction. return 0; tells the system that the program finished successfully. It does not display a zero.
Save the text as main.c. With GCC installed, a local terminal command is gcc -std=c17 -Wall -Wextra -Wpedantic main.c -o main. On a typical Unix-like shell, run the result with ./main. On Windows, the output is commonly named main.exe; how you launch it depends on your shell. These terminal commands are not C statements and must not be pasted into the C source editor. A browser compiler normally combines build and run behind its Run button.
5. Translate the packing algorithm into C
Compare the code with the steps. The first two lines inside main set the inputs. The next two calculate the answers. The two printf calls display them. You do not need to type any input.
#include <stdio.h>
int main(void)
{
const int items = 23;
const int per_box = 6;
const int full_boxes = items / per_box;
const int leftover = items % per_box;
printf("Full boxes: %d\n", full_boxes);
printf("Leftover items: %d\n", leftover);
return 0;
}
Verified output
Full boxes: 3 Leftover items: 5
| C construct | Meaning here | Common trap |
|---|---|---|
| const int items = 23; | Declare an integer object and initialize it to 23; const prevents later assignment to it | Assuming = is an equality question |
| items / per_box | Integer quotient because both operands are integers | Expecting 3.833… |
| items % per_box | Integer remainder | Using / for both outputs |
| %d in a printf string | A conversion for the supplied int value | Confusing the format marker with the remainder operator outside the string |
A name such as full_boxes tells us what a value means. Giving a name its first value is called initialization. We use const because we do not change these values later. It does not mean "keyboard input" or "global constant". These four names can be used only inside this function body.
In printf("Full boxes: %d\n", full_boxes);, %d marks where to display a value of type int. The value comes from full_boxes, after the comma. To display a percent sign in the format text, write %%. Each format must match the value's type. A mismatch can make the program behave unpredictably, even if it seems to work once.
Limit of this version: never set per_box to zero. Dividing by zero or finding a remainder with a zero divisor has undefined behavior: C gives no reliable result. Our input rules forbid it. A later version will check the box size before dividing. This program does not check typed input, negative counts or numbers outside the stated range.
6. Build errors, wrong answers and meaningful tests
GCC builds a program in stages. Preprocessing handles lines such as #include. Compilation translates C into assembly instructions. Assembly turns those instructions into object code. Linking joins that code with the definitions it needs to make a runnable file. Running the file is a separate step. One GCC command can do all the build stages for you.
| Failure | Example | What to do |
|---|---|---|
| Build-time diagnostic | Missing semicolon after the printf call | Read the first diagnostic and inspect the preceding line as well |
| Link failure | A called function has no available definition | Check required source files and libraries, not the arithmetic trace |
| Wrong result | Computing leftover with items / per_box | The program can build, but the contract check fails |
| Invalid execution | Dividing by a zero capacity | Do not execute it; enforce a precondition or validate before division |
The tempting wrong line const int leftover = items / per_box; produces 3, not 5, for the sample. A compiler is allowed to accept that perfectly well-formed calculation. The arithmetic check exposes it: 3 × 6 + 3 = 21, not 23. A successful build says nothing about whether you solved the intended problem.
A useful test set checks different situations. For each row below, change only the starting values of items and per_box in the code, then run it again. Do not type the row into the input box.
| items | per_box | Full / leftover | Why it matters |
|---|---|---|---|
| 0 | 6 | 0 / 0 | No parts |
| 5 | 6 | 0 / 5 | Too few for one full box |
| 6 | 6 | 1 / 0 | Exactly one full box |
| 23 | 6 | 3 / 5 | Several boxes with a remainder |
| 24 | 6 | 4 / 0 | Exact multiple |
| 30000 | 1 | 30000 / 0 | Largest allowed item count, smallest capacity |
Predict each answer before running it. Check the output labels as well as the numbers. Zero full boxes is a valid result. A zero remainder means every part fits in a full box. Compiler warnings can help spot mistakes, but you still need a clear problem and an independently checked answer.
7. Practise the method, not just the typing
Try each question before opening the answer. State the inputs, the result you need and the allowed values. Then show your calculation and check, even when the arithmetic looks easy.
- Foundation: 41 parts, 8 per box. Find full boxes and leftovers. Would the full-box count also be the number of boxes needed to carry everything?
- Trace: 0 parts and capacity 9. Explain why both outputs are zero without treating zero items as an invalid capacity.
- Find the false check: a student claims 2 full boxes and 7 leftover parts for 19 parts at capacity 6. Reconstruction works. Is the result valid?
- Repair the reasoning: for 24 parts at capacity 6, a student always adds one to the quotient to count boxes needed. Explain the mistake.
- Transfer: convert 3,671 whole seconds into hours, minutes and seconds. Use 3,600 seconds per hour and 60 per minute. The input is a nonnegative int no greater than 30,000. Do not use floating point.
- Design: specify one invalid capacity and state where a future validating program must reject it. Do not execute undefined arithmetic to "see what happens".
Checked answers and reasoning
- 41 = 5 × 8 + 1, so 5 full boxes and 1 leftover part. Carrying everything needs 6 boxes, with the last only partly filled.
- 0 = 0 × 9 + 0, and 0 ≤ 0 < 9. There is a valid capacity and nothing to pack.
- No. 2 × 6 + 7 = 19, but 7 is not less than 6. The valid answer is 3 full boxes and 1 leftover part.
- The remainder is zero. Four boxes already carry everything; adding one gives an unnecessary fifth box. Add a partly filled box only when a remainder exists.
- One hour consumes 3,600 seconds, leaving 71. One minute consumes 60 of those, leaving 11 seconds. The check is 1 × 3,600 + 1 × 60 + 11 = 3,671, with minutes and seconds each from 0 through 59.
- A capacity of 0 is invalid. Reject it before either division or remainder. Deciding what error message to return belongs in that program’s contract.
Transfer answer: a complete program
The first remainder leaves the seconds after removing full hours. The second division and remainder split only that remainder, not the original total. Again, the sample input is fixed in the source.
#include <stdio.h>
int main(void)
{
const int total_seconds = 3671;
const int hours = total_seconds / 3600;
const int after_hours = total_seconds % 3600;
const int minutes = after_hours / 60;
const int seconds = after_hours % 60;
printf("Hours: %d\n", hours);
printf("Minutes: %d\n", minutes);
printf("Seconds: %d\n", seconds);
return 0;
}
Verified output
Hours: 1 Minutes: 1 Seconds: 11
For 59 seconds, the answer is 0 hours, 0 minutes and 59 seconds. For 60 seconds, it is 0 hours, 1 minute and 0 seconds. For 3,600 seconds, it is 1 hour, 0 minutes and 0 seconds. These tests catch a common mistake: displaying all the minutes instead of only those left after full hours.
What was checked, and what comes next
Checked October 16, 2026: all three programs were built as C17 with GCC 11.4.0. Compiler warnings were treated as errors. Extra runtime checks looked for undefined behavior, where C gives no reliable result. Outputs and test cases were also checked separately. These are small lessons with inputs written in the code. They are not ready-made packing software and cannot guarantee an exam result.
Before moving on, explain why leftovers must be fewer than the parts in one full box. Trace each line of the packing program. Tell C code apart from a terminal command. Then choose a test that catches a wrong answer. If you cannot do these yet, revisit the 19-part practice question.
Programming in C learning route
Part 1 is the starting chapter. Follow the Programming in C series index for the learning route and published chapters. The planned route continues through variables, types and input/output; decisions; loops; functions; arrays and strings; pointers; structures and files; and a small project. Later parts will be linked as they are published. Topics may be split when the explanation needs room; a chapter number is not a deadline or a syllabus guarantee.
For short revision rather than a first lesson, use the separate C Exam Notes. For additional complete programs, use the C lab file. Do not treat a copied lab answer as a substitute for specifying and tracing a problem.
Sources and scope
- cppreference: arithmetic operators for integer division, remainder, zero-divisor and overflow limits.
- cppreference: main function for hosted-program entry and return status.
- cppreference: formatted output for printf conversions and matching argument types.
- GCC manual: overall options for build stages and output selection.
The packing story, worked examples, practice and explanations are original. The references above support the C rules; we also ran the programs to check the results. The examples use C17 on a normal computer or browser compiler. Programming a small device without that environment may need a different setup.
Comments
Post a Comment