CS U111 · Lecture 8 · practice ladder
Arrays, one rung at a time
Seventeen small programs in seven rungs. Each one uses one idea and comes with the exact output to check against and a folded solution. Lecture 8 goes from "declare an array" to "sort it" in a single hour. This page fills in the steps between. Every program here was compiled with clang -Wall and run before it was published.
Type every program yourself. Reading a program and nodding along teaches very little. Here's the routine:
- Read the task and write down what the loop needs: the index range (
0tosize - 1) and what happens to each element. - Type the program, compile it with
clang -Walland run it. Compare your output with the sample, character by character. - Open the solution only once your version runs. Use it to compare, not to copy.
- Move to the next rung when that rung's programs compile on the first or second try.
Every array loop here is a loops-ladder loop with a[i] in its body, so nothing on this page is a new kind of loop. Once you've climbed it, do the predict-the-output drill.
An array of size n has valid indices 0 to n - 1. So every traversal is for (int i = 0; i < n; i++), with < and not <=. C does not check bounds. a[n] reads whatever memory happens to sit after the array. That is undefined behaviour, and it may print garbage, silently corrupt another variable or crash. clang -Wall can warn when the index is a fixed number like a[5], but not when it comes from a loop.
On this page
Store, reach, change
One name, many boxes, numbered from 0. Goal: writing a[i] inside a for loop feels exactly like writing i did.
Problem 1
Print every element with its index, then change one
Declare int marks[5] = {72, 85, 91, 68, 77};. Print each element with its index, one per line. Then set marks[2] to 95 and print it again.
Sample run:
marks[0] = 72
marks[1] = 85
marks[2] = 91
marks[3] = 68
marks[4] = 77
After the change, marks[2] = 95i from 0 while i < 5. Each element: print i and marks[i]. Changing an element is a plain assignment to marks[2].Solution — open after yours runs
#include <stdio.h>
int main(void)
{
int marks[5] = {72, 85, 91, 68, 77};
for (int i = 0; i < 5; i++) {
printf("marks[%d] = %d\n", i, marks[i]);
}
marks[2] = 95;
printf("After the change, marks[2] = %d\n", marks[2]);
return 0;
}
The first line prints marks[0], not marks[1]. The fifth element is marks[4], and there is no marks[5].
Problem 2
Read five numbers, print them backwards
Read 5 integers into an array. Then print them in reverse order on one line.
Sample run (the numbers after the prompt are what you type):
Enter 5 numbers: 3 8 1 9 4
In reverse: 4 9 1 8 3i from 0 up to 4, scanf("%d", &a[i]). Loop 2 (print): i from 4 down to 0, so the condition is i >= 0 and the step is i--.Solution — open after yours runs
#include <stdio.h>
int main(void)
{
int a[5];
printf("Enter 5 numbers: ");
for (int i = 0; i < 5; i++) {
scanf("%d", &a[i]);
}
printf("In reverse:");
for (int i = 4; i >= 0; i--) {
printf(" %d", a[i]);
}
printf("\n");
return 0;
}
This is the first program in the course that has to remember input: the loop that reads is over before the loop that prints begins. Without an array you would need five variables. The & in &a[i] is the same & as in scanf("%d", &n), because a[i] is an ordinary int variable.
Accumulate over an array
These are the loops-ladder rung 2 patterns again: a variable outside the loop that the loop feeds. The only change is that the loop now feeds it a[i] instead of i.
Problem 3
Sum and average
For marks = {72, 85, 91, 68, 77}, print the sum and the average to two decimal places.
Sample run:
Sum = 393, average = 78.60sum, set to 0 before the loop. Each element: sum += marks[i]. After the loop: (double) sum / 5.Solution — open after yours runs
#include <stdio.h>
int main(void)
{
int marks[5] = {72, 85, 91, 68, 77};
int sum = 0;
for (int i = 0; i < 5; i++) {
sum += marks[i];
}
double average = (double) sum / 5;
printf("Sum = %d, average = %.2f\n", sum, average);
return 0;
}
Without the (double) cast, 393 / 5 divides two ints and gives 78. Storing that result in a double afterwards doesn't help: the fraction is lost at the moment of division.
Problem 4
Count the elements that pass a test
For the same marks, count how many are 75 or more.
Sample run:
3 of 5 students scored 75 or morecount, set to 0. Each element: if (marks[i] >= 75) count++;. This is a traversal with an if inside.Solution — open after yours runs
#include <stdio.h>
int main(void)
{
int marks[5] = {72, 85, 91, 68, 77};
int count = 0;
for (int i = 0; i < 5; i++) {
if (marks[i] >= 75) {
count++;
}
}
printf("%d of 5 students scored 75 or more\n", count);
return 0;
}
Check by hand: 85, 91 and 77 pass, and 72 and 68 don't, so the count is 3. Always check a count this way on data small enough to count yourself.
The extremes
These keep a "best so far" instead of a running total. Goal: you always start the best-so-far at a[0] and loop from i = 1, and you can say why.
Problem 5
Minimum and maximum in one pass
Print the lowest and highest marks, using a single loop.
Sample run:
Lowest = 68, highest = 91minVal and maxVal, both set to marks[0]. Each element from index 1: two separate ifs, one for each.Solution — open after yours runs
#include <stdio.h>
int main(void)
{
int marks[5] = {72, 85, 91, 68, 77};
int minVal = marks[0], maxVal = marks[0];
for (int i = 1; i < 5; i++) {
if (marks[i] < minVal) minVal = marks[i];
if (marks[i] > maxVal) maxVal = marks[i];
}
printf("Lowest = %d, highest = %d\n", minVal, maxVal);
return 0;
}
Why not start minVal at 0? Every mark is bigger than 0, so the minimum would come out as 0, a value that isn't even in the array. marks[0] is always a real candidate. Use two ifs here, not if … else if: one element can update both.
Problem 6
Who topped? Remember the index, not the value
Print the index of the highest mark as well as the mark itself.
Sample run:
Student 2 topped with 91maxIdx = 0, not a value. Compare: marks[i] > marks[maxIdx]. Once you know where the maximum is, you also know what it is.Solution — open after yours runs
#include <stdio.h>
int main(void)
{
int marks[5] = {72, 85, 91, 68, 77};
int maxIdx = 0;
for (int i = 1; i < 5; i++) {
if (marks[i] > marks[maxIdx]) {
maxIdx = i;
}
}
printf("Student %d topped with %d\n", maxIdx, marks[maxIdx]);
return 0;
}
Tracking the index is the key step for selection sort in Rung 6. There, you find the position of the smallest element so you can swap it into place. Knowing only its value would not be enough.
Functions meet arrays
This rung joins Lecture 7 and Lecture 8. Each function takes one mark (a plain int), and the loop calls it once per element. Passing a whole array into a function is coming later, after pointers, so it doesn't appear on this page.
Problem 7
A grade for every mark
Write char grade(int mark). It returns 'A' for 90 and above, 'B' for 80–89, 'C' for 70–79, 'D' for 60–69 and 'F' below 60. In main, print each mark with its grade. Put the prototype above main and the definition below it.
Sample run:
72 -> C
85 -> B
91 -> A
68 -> D
77 -> Cif … return, highest band first. Loop: grade(marks[i]) sits straight inside the printf, with %c for the char it returns.Solution — open after yours runs
#include <stdio.h>
char grade(int mark);
int main(void)
{
int marks[5] = {72, 85, 91, 68, 77};
for (int i = 0; i < 5; i++) {
printf("%d -> %c\n", marks[i], grade(marks[i]));
}
return 0;
}
char grade(int mark)
{
if (mark >= 90) return 'A';
if (mark >= 80) return 'B';
if (mark >= 70) return 'C';
if (mark >= 60) return 'D';
return 'F';
}
The function needs no else: each return ends the function immediately, so a mark of 91 never reaches the >= 80 test. Delete the prototype line and clang refuses to compile: call to undeclared function 'grade'.
Problem 8
Add a bonus to every mark, capped at 100
Write int addBonus(int mark, int bonus). It returns mark + bonus, but never more than 100. Starting from {72, 85, 97, 68, 77}, give every student 5 bonus marks, storing the result back in the array. Then print the array.
Sample run:
77 90 100 73 82marks[i] = addBonus(marks[i], 5);. The function hands back a new value, and the caller stores it in the array.Solution — open after yours runs
#include <stdio.h>
int addBonus(int mark, int bonus);
int main(void)
{
int marks[5] = {72, 85, 97, 68, 77};
for (int i = 0; i < 5; i++) {
marks[i] = addBonus(marks[i], 5);
}
for (int i = 0; i < 5; i++) {
printf("%d ", marks[i]);
}
printf("\n");
return 0;
}
int addBonus(int mark, int bonus)
{
mark = mark + bonus;
if (mark > 100) mark = 100;
return mark;
}
Inside addBonus, the line mark = mark + bonus changes only the function's own copy. That's pass-by-value from Lecture 7. Replace the loop body with just addBonus(marks[i], 5); and the array prints unchanged: 72 85 97 68 77. The marks[i] = … in front of the call is what saves the result.
Search and count
Is the value there, and where? How many times? Goal: telling "stop at the first match" apart from "look at every element".
Problem 9
Linear search, with −1 for "not found"
Read a target. Search marks for it and print its index, or say that it isn't there.
Two sample runs:
Search for: 68
68 found at index 3Search for: 80
80 is not in the arrayfoundIndex = -1, a value that can never be a real index. Each element: on a match, save i and break. After the loop: if foundIndex is still −1, there was no match.Solution — open after yours runs
#include <stdio.h>
int main(void)
{
int marks[5] = {72, 85, 91, 68, 77};
int target, foundIndex = -1;
printf("Search for: ");
scanf("%d", &target);
for (int i = 0; i < 5; i++) {
if (marks[i] == target) {
foundIndex = i;
break;
}
}
if (foundIndex == -1)
printf("%d is not in the array\n", target);
else
printf("%d found at index %d\n", target, foundIndex);
return 0;
}
Why −1 and not 0? Index 0 is a real position; 68 might be sitting there. −1 is a sentinel, a value no genuine answer can take. The break is there only for speed. Remove it and you still get the right answer here, but you then find the last match instead of the first.
Problem 10
Count every occurrence
Use the Lecture 8 activity data, int scores[8] = {55, 90, 72, 90, 61, 45, 90, 78};. Read a value and print how many times it appears.
Sample run:
Count how many times: 90
90 appears 3 time(s)== as the test, and there is no break. Every element has to be looked at.Solution — open after yours runs
#include <stdio.h>
int main(void)
{
int scores[8] = {55, 90, 72, 90, 61, 45, 90, 78};
int target, count = 0;
printf("Count how many times: ");
scanf("%d", &target);
for (int i = 0; i < 8; i++) {
if (scores[i] == target) {
count++;
}
}
printf("%d appears %d time(s)\n", target, count);
return 0;
}
This answers activity question 3. A search can stop at its first match, but a count cannot, because the third 90 is at index 6, near the end. Put a break in and the answer becomes 1.
Problem 11
Binary search, printing each step
The array {12, 19, 23, 31, 44, 58, 67, 80} is already sorted. Read a target and binary-search for it, printing left, right, mid and a[mid] on every step.
Two sample runs:
Search for: 58
left=0 right=7 mid=3 a[mid]=31
left=4 right=7 mid=5 a[mid]=58
58 found at index 5Search for: 30
left=0 right=7 mid=3 a[mid]=31
left=0 right=2 mid=1 a[mid]=19
left=2 right=2 mid=2 a[mid]=23
30 is not in the arrayleft = 0, right = 7. Loop while left <= right. Each step: mid = left + (right - left) / 2. On a match, stop. If a[mid] is too small, set left = mid + 1. If it's too big, set right = mid - 1.Solution — open after yours runs
#include <stdio.h>
int main(void)
{
int a[8] = {12, 19, 23, 31, 44, 58, 67, 80};
int target, left = 0, right = 7, foundIndex = -1;
printf("Search for: ");
scanf("%d", &target);
while (left <= right) {
int mid = left + (right - left) / 2;
printf("left=%d right=%d mid=%d a[mid]=%d\n", left, right, mid, a[mid]);
if (a[mid] == target) {
foundIndex = mid;
break;
} else if (a[mid] < target) {
left = mid + 1;
} else {
right = mid - 1;
}
}
if (foundIndex == -1)
printf("%d is not in the array\n", target);
else
printf("%d found at index %d\n", target, foundIndex);
return 0;
}
This is the guessing game from Lab 4 Problem 5 again, now with array positions instead of numbers. Search for 30 and the range closes in until left passes right, which is how the loop knows 30 isn't there. The + 1 and - 1 matter: without them the range can stop shrinking, and the loop never ends.
Unpack this step
(right - left) / 2 is integer division, so it rounds down: (7 − 0) / 2 = 3, not 3.5. So mid is always a whole index. It gives the same number as (left + right) / 2 but never adds two large numbers, which is why Lecture 8 slide 16 prefers it.
Rearrange: reverse and sort
Every rearrangement is built from one move: swapping two elements through a temp variable. Goal: the three-line swap becomes automatic, and you can print the array after every pass to watch a sort happen.
Problem 12
Reverse an array in place
Reverse {10, 20, 30, 40, 50, 60} in the same array, with no second array, then print it.
Sample run:
60 50 40 30 20 10a[i] with a[n - 1 - i], the element the same distance from the other end. Stop: i < n / 2, once you reach the middle.Solution — open after yours runs
#include <stdio.h>
int main(void)
{
int a[6] = {10, 20, 30, 40, 50, 60};
int n = 6;
for (int i = 0; i < n / 2; i++) {
int temp = a[i];
a[i] = a[n - 1 - i];
a[n - 1 - i] = temp;
}
for (int i = 0; i < n; i++) {
printf("%d ", a[i]);
}
printf("\n");
return 0;
}
Why three lines? Write a[i] = a[n-1-i] first and the old a[i] is gone, so temp keeps it safe. Run the loop to i < n instead and every pair gets swapped twice, which puts the array back where it started.
Unpack this step
Why n - 1 - i? The last index is n - 1 (here 5). Step in i places from each end: i = 0 pairs 0 with 5, i = 1 pairs 1 with 4, and i = 2 pairs 2 with 3. For odd n, n / 2 rounds down, so the middle element is never swapped, which is correct.
Problem 13
Bubble sort that stops early
Bubble-sort {68, 72, 85, 77, 91} into ascending order, printing the array after each pass. Add a swapped flag so the sort stops as soon as a pass makes no swaps.
Sample run:
After pass 1: 68 72 77 85 91
After pass 2: 68 72 77 85 91
No swaps - already sorted, stopping earlya[i] and a[i + 1], and swap them if they're out of order. Stop the inner loop at i < n - 1 - pass, because the end of the array is already sorted. Flag: set swapped = 0 at the start of each pass and 1 on any swap; break if it's still 0 at the end of the pass.Solution — open after yours runs
#include <stdio.h>
int main(void)
{
int a[5] = {68, 72, 85, 77, 91};
int n = 5;
for (int pass = 0; pass < n - 1; pass++) {
int swapped = 0;
for (int i = 0; i < n - 1 - pass; i++) {
if (a[i] > a[i + 1]) {
int temp = a[i];
a[i] = a[i + 1];
a[i + 1] = temp;
swapped = 1;
}
}
printf("After pass %d:", pass + 1);
for (int k = 0; k < n; k++) printf(" %d", a[k]);
printf("\n");
if (swapped == 0) {
printf("No swaps - already sorted, stopping early\n");
break;
}
}
return 0;
}
Run it on the lecture's data, {72, 85, 91, 68, 77}, and it needs all four passes. The array is already sorted after pass 3, but it takes pass 4, with zero swaps, to show that. The flag earns its keep on nearly sorted data like this one: two passes instead of four. And why i < n - 1 - pass? The inner loop reads a[i + 1], so at i = n - 1 it would go past the end.
Problem 14
Selection sort, pass by pass
Selection-sort {72, 85, 91, 68, 77}, printing the array after each pass.
Sample run:
After pass 1: 68 85 91 72 77
After pass 2: 68 72 91 85 77
After pass 3: 68 72 77 85 91
After pass 4: 68 72 77 85 91i, find the index of the minimum among positions i to n - 1 and swap it into position i. Then the unsorted part starts one place later.Solution — open after yours runs
#include <stdio.h>
int main(void)
{
int a[5] = {72, 85, 91, 68, 77};
int n = 5;
for (int i = 0; i < n - 1; i++) {
int minIdx = i;
for (int j = i + 1; j < n; j++) {
if (a[j] < a[minIdx]) minIdx = j;
}
int temp = a[i];
a[i] = a[minIdx];
a[minIdx] = temp;
printf("After pass %d:", i + 1);
for (int k = 0; k < n; k++) printf(" %d", a[k]);
printf("\n");
}
return 0;
}
Pass 4 changes nothing: the minimum of {85, 91} is already at the front, so a[3] is swapped with itself. This matches Lecture 8's trace ("sorted after 3 swaps"): the code still runs a fourth pass, but that swap doesn't move anything.
Two dimensions
A grid uses loops-ladder rung 5: rows in the outer loop, columns in the inner loop, a newline between them. The index order is always [row][column].
Problem 15
Print a grid with its row sums
For int g[3][4] = {{1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10, 11, 12}};, print the grid with each row's sum at the end of that row.
Sample run:
1 2 3 4 | row sum 10
5 6 7 8 | row sum 26
9 10 11 12 | row sum 42r from 0 to 2. At the start of each row, set rowSum = 0. Inner loop: c from 0 to 3; print g[r][c] and add it to rowSum. After the inner loop, print the sum and the newline.Solution — open after yours runs
#include <stdio.h>
int main(void)
{
int g[3][4] = {{1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10, 11, 12}};
for (int r = 0; r < 3; r++) {
int rowSum = 0;
for (int c = 0; c < 4; c++) {
printf("%4d", g[r][c]);
rowSum += g[r][c];
}
printf(" | row sum %d\n", rowSum);
}
return 0;
}
Where rowSum = 0 goes is the whole lesson. Inside the outer loop, it restarts for every row. Move it above both loops and you get running totals instead: 10, 36, 78.
Problem 16
Transpose a 2 × 3 matrix
For A = {{1, 2, 3}, {4, 5, 6}} (2 rows, 3 columns), build its transpose T (3 rows, 2 columns) and print it.
Sample run:
1 4
2 5
3 6T[j][i] = A[i][j], with the indices swapped. Loops: walk over A's shape (i < 2, j < 3) to fill T. Then print T using its shape (3 × 2).Solution — open after yours runs
#include <stdio.h>
int main(void)
{
int A[2][3] = {{1, 2, 3}, {4, 5, 6}};
int T[3][2];
for (int i = 0; i < 2; i++) {
for (int j = 0; j < 3; j++) {
T[j][i] = A[i][j];
}
}
for (int r = 0; r < 3; r++) {
for (int c = 0; c < 2; c++) {
printf("%4d", T[r][c]);
}
printf("\n");
}
return 0;
}
Row 0 of A (1 2 3) became column 0 of T. Declaring T[2][3] by mistake means T[j][i] reaches T[2][…], a row that doesn't exist. That is out of bounds, and C won't stop you.
Unpack this step
An m × n matrix has m rows and n columns, and its transpose is n × m. Rows become columns, so the two sizes trade places.
Problem 17
Add two matrices
Add A = {{1, 2}, {3, 4}} and B = {{5, 6}, {7, 8}} element by element into C, then print C.
Sample run:
6 8
10 12C[i][j] = A[i][j] + B[i][j], using the same position in each matrix. The loops are the same shape as in Problem 15.Solution — open after yours runs
#include <stdio.h>
int main(void)
{
int A[2][2] = {{1, 2}, {3, 4}};
int B[2][2] = {{5, 6}, {7, 8}};
int C[2][2];
for (int i = 0; i < 2; i++) {
for (int j = 0; j < 2; j++) {
C[i][j] = A[i][j] + B[i][j];
}
}
for (int i = 0; i < 2; i++) {
for (int j = 0; j < 2; j++) {
printf("%4d", C[i][j]);
}
printf("\n");
}
return 0;
}
This matches the table on Lecture 8 slide 26. Matrix multiplication (slide 27) needs a third loop to add up products. The lecture only shows its shape, so don't drill it until a lab asks for it.
Then the lecture activity and the sheet
Each rung maps onto part of Lecture 8 and its in-class activity, "Class Statistics":
| Rung | Lecture 8 slides | Activity question it unlocks |
|---|---|---|
| 1 · store, reach, change | 3–8 (declare, layout, index, initialise) | (setting up scores[8]) |
| 2 · accumulate | 9–10 (traversal, sum and average) | Q1: sum and average |
| 3 · extremes | 11 (minimum) | Q1: minimum and maximum |
| 4 · functions meet arrays | 17–18 (grade distribution), plus Lecture 7 | (none — this combines the two lectures) |
| 5 · search and count | 13–16 (linear and binary search) | Q2: is 61 there? · Q3: how many 90s? |
| 6 · rearrange | 19–23 (bubble and selection sort) | Q4: sort the scores |
| 7 · two dimensions | 24–28 (2D arrays, addition, transpose) | (none) |
Once you've climbed the ladder, try the whole activity as one program on scores[8]. It is Problems 3, 5, 9, 10 and 13 (or 14) run one after another on the same array. Lab Sheet 6 hasn't been shared yet. A rung-to-sheet map like the one on the loops ladder will be added here when it arrives.
Check yourself — the activity's answers, once your program runs
Sum 581, average 72.625, minimum 45, maximum 90. 61 is at index 4. 90 appears 3 times. Sorted: 45 55 61 72 78 90 90 90.
"Give me one C array problem like Hanly & Koffman chapter 7, with one idea only (for example, find the index of the smallest element) and a sample output. Check my code when I paste it, and don't show me a solution first." · "Here is my bubble sort and the array it prints after each pass. Is my inner-loop bound right, or does it read past the end? Explain in two lines." Stick to arrays inside main and functions that take single values. Passing arrays to functions comes after pointers.