Assignment 4 — Arrays, strings, and the end of the array
Five small programs about arrays and strings — including one that reads past the end on purpose — and then the useful half of them moved into a three-file program with a Makefile.
Due Sep 23, 2026
A3 gave your code names. This week it gets collections: a hundred numbers under one name, and a word as a thing you can pass around.
The one sentence this week is built on is in the reading, and it is the promise made on the first day of this course. Java throws ArrayIndexOutOfBoundsException. C does not check at all — not when it compiles your program, and not when it runs it. Step 2 is you finding out what happens instead, on purpose, with the compiler watching and saying nothing.
Start a new directory for this assignment. Not the A3 one. The last step builds everything in the directory at once.
You will hand in ten files: stats.c, offend.c, modify.c, word.c, shout.c, text.h, text.c, main.c, Makefile and notes.txt.
Step 2 is the one to do while you are fresh, not at eleven at night — it is short, it asks you to guess before you run anything, and a guess you make in a hurry is not worth writing down.
Step 6 is the one Midterm Part 1 is most likely to ask you for, and it is the step with an order that has to be right. Doing it once this week is worth more than reading about it twice next week.
Steps 3, 4 and 5 are the drill. They are short and they are where the actual fluency comes from, so do them if you have the evening.
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.5 — Arrays and Strings is the reading that explains it, and it is due before Tuesday.
An array is declared with a type, a name, and a size in square brackets. The size is fixed for the life of the variable:
int scores[10];That gives you ten ints, at positions 0 through 9. You can also declare and fill it at once, and then the size can be left out:
int scores[] = {4, 8, 15, 16, 23, 42};The array does not know how long it is. There is no scores.length and there is no len(scores). If a piece of code needs the length, some variable has to be carrying it:
int scores[] = {4, 8, 15, 16, 23, 42};
int n = 6;A for loop over an array is always the same shape, and the < is doing real work — with <= you would touch position n, which is one past the end:
for (int i = 0; i < n; i++) {
printf("%d\n", scores[i]);
}A string is an array of char with a '\0' on the end. That last byte is not decoration: it is the only way any function can find where the string stops. "hello" occupies six bytes, not five.
char word[100];
scanf("%99s", word);
printf("%s has %d letters\n", word, (int) strlen(word));strlen needs #include <string.h>. Two details in that scanf are worth noticing now and are explained in step 4: there is no & in front of word, and the 99 is not optional.
Part A — arrays
stats.c— ten numbers and three answers. Declareint values[10]. Read ten numbers into it with aforloop andscanf, then print three things: their sum, the largest, and the average.If you were in class on Thursday, you have most of this already. Finish it here.
The average is the one with a trap in it.
sum / 10where both areintgives you anint, so the average of ten numbers ending in a7quietly loses its fraction. Print it with%fand make the division produce a real number —(double) sum / 10is one way.In
notes.txt: what did the average print before you fixed the division, for an input whose true average is not a whole number? One line.offend.c— the one that reads past the end.#include <stdio.h> int main() { int a[5] = {10, 20, 30, 40, 50}; printf("a[4] is %d\n", a[4]); printf("a[5] is %d\n", a[5]); printf("a[6] is %d\n", a[6]); printf("a[7] is %d\n", a[7]); }Write down your prediction before you compile it. Three questions, and “I don’t know” is an answer to any of them, as long as it is written down first:
- Does
gcc -Wallcomplain? If so, what about? - Does the program crash?
- If it does not crash, what do the last three lines print?
Now compile it with
gcc -Wall offend.c -o offendand run it. Then run it five more times without recompiling. Record every answer innotes.txt, beside your prediction.Look carefully at whether the six runs agree with each other. Some of those positions will give you the same number every time and some will not. Say in
notes.txtwhich did which. That difference is undefined behavior — the reading’s phrase, happening in front of you. The language declines to say what is there, so nothing has to be consistent: not between machines, and not between two runs of one binary.Then add a line printing
a[1000], and another printinga[-1]. Rebuild and run. Innotes.txt, say what each one did, including if one of them crashed.Finally, two or three sentences: in Java, how far would this program have got? You do not need the exact exception name. The point is the difference in when you find out.
Do not try to make this program correct. Being wrong is its entire job.
- Does
modify.c— a function that changes its caller’s data. In A3 you wrotevoid swap(int a, int b)and it did not swap anything: the function got copies, andmain’s variables never moved. Now write this:void double_all(int arr[], int n) { for (int i = 0; i < n; i++) { arr[i] = arr[i] * 2; } }And a
mainthat makes an array, prints it, callsdouble_all, and prints it again:int nums[4] = {1, 2, 3, 4}; print_all(nums, 4); double_all(nums, 4); print_all(nums, 4);Write
void print_all(int arr[], int n)too — one line per element, or all on one line; your choice.In
notes.txt, and this is the important one:swapcould not changemain’s twoints, anddouble_allcan changemain’s whole array. Both are functions, both take arguments, neither returns anything. In three or four sentences: what is different? Your honest guess is what is being asked for — the machinery that explains it is several weeks away, and nobody expects you to have it.Then one more line:
double_allis givennseparately. Why can it not just askarrhow long it is?
Part B — strings
word.c— how long is a word, twice. Read one word from the user and print its length two ways: once withstrlen, and once with a loop you write yourself that counts characters until it reaches'\0'. Print both. They must agree.char word[100]; printf("word: "); scanf("%99s", word);Two things in that
scanfto put innotes.txt:- There is no
&. Everyscanfyou have written so far saidscanf("%d", &n). This one does not. Say what is different aboutword— one sentence, and a guess is fine. - The
99is a limit, andwordholds 100. Try%swith no number and type in something much longer than a hundred characters. Say what happened. Then put the99back and leave it there.
Your counting loop is three lines and it is the whole idea of a C string:
int len = 0; while (word[len] != '\0') { len++; }- There is no
shout.c— uppercase, in place. Writevoid shout(char s[])which turns every lowercase letter insinto its uppercase version, changing the array itself rather than returning anything.mainreads a word, callsshout, and prints it.It takes no length. It does not need one — the
'\0'is the length.Do the conversion by hand rather than with a library call:
if (s[i] >= 'a' && s[i] <= 'z') { s[i] = s[i] - 'a' + 'A'; }That line only makes sense if a
charis a number, which it is. Innotes.txt: print'a'and'A'with%dinstead of%cand say what the two numbers are, and what their difference is.There is a library function,
toupperin<ctype.h>, that does exactly this. Now that you have written it, use it: replace your two lines with a call totoupperand check the program still works.
Part C — one program, three files
Move the useful half into a library, and build it with
make. Nothing new is computed here. This is A3’s Part B again, with this week’s functions, and it is deliberate repetition — it is the step most likely to turn up on Midterm Part 1.text.h— prototypes fordouble_all,print_allandshout, and nothing else. It needs its own include guard, named after this file and notNUMBERS_H:
#ifndef TEXT_H #define TEXT_H void double_all(int arr[], int n); void print_all(int arr[], int n); void shout(char s[]); #endiftext.c— the three definitions, copied from steps 3 and 5, and nomain. Its first line is#include "text.h".main.c—mainand nothing else, calling all three so the output makes it obvious which is which.Makefile— and this time write it from scratch rather than copying A3’s. It builds a program calledtextfrommain.candtext.c, and it has acleanrule.
The whitespace before each command is a single tab, not spaces. With spaces,
makesaysmissing separator.Done when
makebuilds it,./textruns, andmakea second time in a row says there is nothing to do.Then, in
notes.txt: runmake clean, thenmake, thentouch text.h, thenmakeagain. Say what the last one rebuilt and why.Submit. All ten 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, 5 and 6.
As in every assignment so far, written answers are graded on being honest about what you saw, not on being right. Step 2 in particular: “I predicted a crash, it printed 32767, and the number was the same all six times” is worth full marks. So is “I predicted a crash and I have no idea why it did not.” 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 24 to put your files on the projector and walk the class through them. You will not be told in advance. See the syllabus on how presentation is graded.
What else to do (optional part)
Ungraded. No points, and skipping them costs nothing.
Make
stats.cwork for any number of values up to 100: read a count first, then that many numbers. What should it do if the user says200?Write
int contains(int arr[], int n, int target)returning 1 iftargetis in the array and 0 otherwise. Then writeint index_of(...)returning the position, or-1when it is not there. Why can it not return0for “not found”?Reverse a string in place, without making a second array.
In
offend.c, declare a second arrayint b[5]full of77s beside the first, and then go hunting for it througha— printa[i]forifrom-8to10and find the77s. They may be belowa, at negative indices, rather than above it; which side they land on is not something the language promises. One array reading another array’s contents is among the two or three most consequential facts in computer security, and you now have the equipment to demonstrate it.Compile
offend.cwithgcc -Wall -O2instead ofgcc -Wall, and read what it says now. The compiler could see this the whole time. Why do you think it only mentions it when you ask for optimisation?Compile
offend.cwithgcc -Wall -fsanitize=addressand run it. Instead of a number you get a diagnosis with a name. What is the name, and — given that this tool exists and is one flag away — why is it not simply on all the time?Work the Arrays and Strings 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 3 answer into a chatbot and ask it whether you got the difference between
swapanddouble_allright. Write one line on the part of its answer you could not follow. That part is week 9.