Assignment 3 — Functions, and one program in three files
Four small functions, each in its own file, then the same three functions moved into one program split across three files — and a Makefile that builds it.
Due Sep 16, 2026
Everything in A2 lived inside main. This week you take code out of main and give it a name, which is the move every language has and C spells slightly differently. Then you take those names out of the file altogether.
Two things here are genuinely new, and neither exists in Java or Python. The first is that C reads your file top to bottom, once, so a function has to be declared before it is called. The second is that gcc is not one program: a compiler runs, and then a linker runs, and they fail in different ways with different messages. Steps 1 and 7 are where you meet them.
Start a new directory for this assignment. Not the A2 one. Several files below have the same names as A2 files, and the last step builds everything in the directory at once.
You will hand in nine files: max.c, digits.c, grade.c, swap.c, numbers.h, numbers.c, main.c, Makefile and notes.txt.
Those three are what Tuesday’s class starts from — it opens with a program already split across files, and reads much better from a chair where that has happened once already.
The rest is practice at the same two ideas, and practice is the point: gcc main.c numbers.c -o numbers should be something you type without stopping to think by the end of the week.
The lab machines in Ben Franklin 103 are the guaranteed environment. Compile everything there before you submit, even if you wrote it elsewhere.
Submit early and submit again. Brightspace keeps the latest version, so upload what you have on Friday and replace it on Tuesday.
AI: the course AI policy applies in full. No AI-generated code, and no asking a chatbot to solve these before the deadline. The optional problems at the end say when that changes.
The shapes, for reference
Keep this next to you. [DIS] 16.4 — Functions is the reading that explains it, and it is due before Tuesday.
A definition is the function itself:
int max(int a, int b) {
if (a > b) {
return a;
}
return b;
}A prototype is the same first line with a semicolon instead of a body. It tells the compiler that a function by that name exists, what it takes and what it gives back — and nothing about how it works:
int max(int a, int b);A function that gives nothing back has the return type void, and no return statement at the end:
void print_row(int n) {
printf("%d\n", n);
}Every function must be declared before the line that calls it. You have two ways to do that, and both are normal C: put the whole definition above main, or put a prototype above main and the definition below it. Step 1 is you trying both.
The order to write the pieces in is the design recipe — signature and purpose first, then the examples with their answers worked out, and the body last. It walks through max, which is step 1 below, so read it before you start.
Examples are checked with assert, not read off the screen. assert(x) does nothing when x is true and stops the program when it is false. It needs #include <assert.h>, and because it is silent on success, main ends with a line saying so:
assert(max(6, 10) == 10);
printf("all examples passed\n");A failure names the expression and the line it is on:
max: max.c:12: main: Assertion `max(6, 10) == 10' failed.
Aborted (core dumped)
That is the program stopping on purpose, not crashing.
Part A — four functions, four files
max.c— and the error you are supposed to cause. Writeint max(int a, int b)returning the larger of the two, and amainthat checks it on three examples:#include <stdio.h> #include <assert.h> int main() { assert(max(6, 10) == 10); assert(max(11, 3) == 11); assert(max(4, 4) == 4); printf("all examples passed\n"); }Put both
#includelines in before you start. Forget the second one andgcccomplains thatassertis implicitly declared — which is the very error this step is about, on the wrong name, and it will send you looking in the wrong place.Do it in this order, and compile with
gcc -Wall max.c -o maxafter each:Definition below
mainfirst. This is the version that goes wrong. Copy the messagegccprinted intonotes.txt, exactly as it appeared. Then say whether it was a warning or an error — the word is in the message — and whether you got a workingmaxprogram out of it anyway.Now move the whole definition above
main. Compile again. What changed?Now move it back below
main, and add a prototype abovemaininstead. Compile again.
Two of those three work. In
notes.txt, say in one or two sentences what the compiler needed, given that in (a) the function was sitting right there in the file where you could see it.digits.c— your A2 program, with the work given a name. Writeint count_digits(int n)which takes a number and returns how many digits it has.mainreads a number withscanfand prints the count.The counting loop is A2’s — divide by 10 until you reach 0. Copy it in and put it inside the function. No
printfand noscanfinsidecount_digits: it takes a number and returns a number, and that is all it does.Run it on
90210,7and0. If0is wrong, it is wrong in the same way it was in A2.If you never got A2’s
digits.cworking, write it now. The loop isn = n / 10and a counter, running untilnis0.grade.c— a function that returns a letter. Writechar letter_grade(int score)which returns'A','B','C','D'or'F'. Same boundaries as A2: 90 and above is A, 80–89 is B, 70–79 is C, 60–69 is D, below 60 is F.One
if/else if/elsechain, as before. A character constant is in single quotes,'A', it compares with==like any other number, andprintfprints one with%c.mainchecks it on90,89,60and59:assert(letter_grade(90) == 'A'); assert(letter_grade(89) == 'B'); assert(letter_grade(60) == 'D'); assert(letter_grade(59) == 'F');Now add a fifth, well away from any boundary:
assert(letter_grade(20) == 'F');.Then read what
gcc -Wallsays aboutgrade.c. Innotes.txt: did the fifth assertion pass, and didgccsay anything? If your chain has no finalelse, the assertion can pass anyway and the warning is the only thing that tells you. That is A2’s argument one step worse: a test passing is not the same as being right.swap.c— the one that does not work. Writevoid swap(int a, int b)whose body swapsaandbusing a third variable. Inmain:int x = 3; int y = 8; printf("before: x = %d, y = %d\n", x, y); swap(x, y); printf("after: x = %d, y = %d\n", x, y); assert(x == 8); assert(y == 3);Compile it and run it.
gcc -Wallsays nothing at all about this program — it compiles clean, and the only thing that objects is the assertion, at run time.In
notes.txt: what did the two lines print, what did you expect, and which assertion failed? Then, in two or three sentences, your best guess at whatswapactually swapped. There is a name for this and the reading has it; you do not need the name to describe what happened.Do not try to fix it. It cannot be fixed with anything you have been shown yet, and the tool that fixes it is week 9.
Part B — one program, three files
Now the three functions from steps 1–3 move into a program of their own. Nothing new is being computed. What changes is where the code lives.
Make the header and the source file.
numbers.hholds the three prototypes and nothing else:
// Include guard: magic that stops this file being pasted in twice. // Name it after the file: numbers.h -> NUMBERS_H #ifndef NUMBERS_H #define NUMBERS_H int max(int a, int b); int count_digits(int n); char letter_grade(int score); #endifCopy the comment too. The name has to differ from every other header’s or the guards start silencing each other, and deriving it from the file name is what guarantees that — so the next header you write is not a second
NUMBERS_H.numbers.cholds the three definitions — copied from steps 1, 2 and 3 — and nomain. Its first line is#include "numbers.h". Note the quotes: angle brackets mean a header that came with the system, quotes mean a file of mine, in this directory.main.cholds onlymain, and starts with both#include <stdio.h>and#include "numbers.h". It calls all three functions and prints the results. What it prints is up to you; make it obvious from the output which function produced which line.
Build it with:
gcc -Wall main.c numbers.c -o numbersDone when
./numbersruns and you can see all three functions in its output.Find out what
#includeactually did.#includeis not Java’simportand it is not Python’s. It is a paste: the contents of the named file are dropped into your file, in place of that line, before the compiler sees any of it. You can watch this happen:gcc -E main.c > main.i-Estops after the pasting and prints the result. Openmain.iand look at how long it is.In
notes.txt: roughly how many lines ismain.i, and how many ismain.c? Find your three prototypes in there — where are they, relative tomain? Then say in one sentence where the extra thousand-odd lines came from.Now look again at the
#ifndefand#defineat the top ofnumbers.h. If#includepastes, then a header included twice would paste twice. Those three lines are what stops that. You do not need them yet with only one header — they are there because every header you will ever read has them, and now you know why.Break the build on purpose — twice. Both of these are one command each. Record the exact message in
notes.txtboth times.- Leave a file out:
gcc -Wall main.c -o numbers- Put it back, but take the include out. Restore the full command from step 5, then delete the
#include "numbers.h"line frommain.cand build again.
Then, in three or four sentences: the two messages are different in kind, not just in wording. One of them names a line in your file and one of them does not. Which is which, and what does that tell you about how many separate programs
gccran on your behalf? Put the#includeback when you are done.Write a
Makefile, and submit. In the same directory, a file called exactlyMakefile:numbers: main.c numbers.c numbers.h gcc -Wall -o numbers main.c numbers.c clean: rm -f numbersThe whitespace before
gccand beforermmust be a single tab character, not spaces. This is the single most common way aMakefilefails, it looks completely fine on screen, and the error you get saysmissing separator. Innano, a tab is the Tab key and it stays a tab.Then run
make, andmake clean, andmakeagain. Put the output of all three innotes.txt.Finally: touch nothing, and run
makea second time in a row. It says something different. What, and why? One sentence. [DIS] 17.5 — make and Makefiles is the reading for this, and section 17.5.6 is the one about the errors.Then submit all nine files on Brightspace. Compile on a lab machine one last time first.
Grading
Your programs are marked on compiling and running, and notes.txt on answering the questions in steps 1, 2, 3, 4, 6, 7 and 8.
As in A1 and A2, written answers are graded on being honest about what you saw, not on being right. “It printed after: x = 3, y = 8 and I expected 8 and 3, and I do not know why” is worth full marks. A confident description of something that did not happen is not.
A file that does not compile earns nothing for the parts that would have worked, so if you are out of time, hand in the version that builds rather than the version you were in the middle of.
Presentation
One of you will be asked on Thursday, September 17 to put your files on the projector and walk the class through them. You will not be told in advance. There are ten of us and about twelve assignments over the term, so plan on presenting about once — twice at the most. See the syllabus on how presentation is graded.
What else to do (optional part)
Ungraded. No points, and skipping them costs nothing.
Give
numbers.ca fourth function of your own and add it tonumbers.h. Then add it tomain.cwithout rebuilding, and see what happens when you run the old./numbers.Add a prototype to
numbers.hfor a function you never define, and call it frommain.c. Which of the two failures from step 7 do you get?Take the multiplication table from A2 and turn it into
void print_table(int size). Then call it three times from onemain, with 3, 5 and 9. This is the argument for functions in one line of code.Change the
gccline in yourMakefileto something misspelled, runmake, and read what it tells you. Then replace the tab in front of it with four spaces and runmakeagain. Those are the two Makefile errors you will actually meet.Work the Functions set in Dive Into Systems: Exercises. They run in the browser and mark themselves.
After the deadline has passed — and only then, per the AI policy — paste your step 4
swap.cinto a chatbot and ask why it does not swap anything. Does the answer make sense to you yet? Write one line on which part of it you could not follow; that part is week 9.