CS 112
Introduction to Data Structures
Week 01
Control Structures, Functions, Parameter Passing
Eric Araújo
Calvin University · Fall 2026
if and switchwhile, for, break/continuemakeif
if (hp <= 0) {
cout << "You have fallen." << endl;
} else if (hp < 20) {
cout << "You are badly hurt." << endl;
} else {
cout << "You fight on." << endl;
}
== != && || != is assignment, == is comparison: the classic C++ bugswitch
switch (roll) {
case 1:
cout << "Critical miss!" << endl;
break;
case 19:
case 20: // 19 and 20 share a case
cout << "Critical hit!" << endl;
break;
default:
cout << "A normal swing." << endl;
}
break and execution falls through into the next case. Sometimes useful, usually a bug.roll is 19?
case 19:
case 20:
cout << "Critical hit!" << endl;
break;
default:
cout << "A normal swing." << endl;
case falls through to the next one, so 19 and 20 run the same code. The break then stops it before default.
while
// how many times can we halve this before reaching 1?
double value = 45444443442345345.0;
int count = 0;
while (value > 1.0) {
value /= 2.0;
++count;
}
cout << "2^" << count << " clears it" << endl;
while loop tests before each pass. If the condition starts false, the body never runs.arr hold after this runs?
int arr[] = { 3, 7, 11, 13 };
int i = 1;
while (i < 4) {
arr[i] = arr[i] * 2;
++i;
}
i starts at 1, so arr[0] is never touched; the loop stops after i == 3, so the last element is doubled.
while to for
int i = 1;
while (i < 4) {
arr[i] = arr[i] * 2;
++i;
}
while to for
for (int i = 1; i < 4; ++i) {
arr[i] = arr[i] * 2;
}
i now lives only inside the loop, using it afterwards is a compile error.break and continue
for (int i = 0; i < n; ++i) {
if (party[i] == target) {
found = i;
break; // stop looking
}
}
for (int i = 0; i < n; ++i) {
if (hp[i] <= 0) {
continue; // skip the fallen
}
total += hp[i];
}
int rollDamage(int dice, int sides) {
int total = 0;
for (int i = 0; i < dice; ++i) {
total += rand() % sides + 1;
}
return total;
}
int main() {
int hit = rollDamage(2, 6); // 2d6
}
C++ reads top to bottom. Calling a function the compiler hasn't seen yet is an error, so we declare it first with a prototype.
int rollDamage(int dice, int sides);
Just the signature, what exists.
#include "dice.h"
int rollDamage(int dice, int sides) {
...
}
The body, how it works.
.cpp that calls it just does #include "dice.h". This is the header/source split from week 0, now with a reason.
// default: callers may omit sides
int rollDamage(int dice, int sides = 6);
rollDamage(2); // 2d6
rollDamage(2, 20); // 2d20
// overloading: same name, different parameter lists
int attack(int strength);
int attack(int strength, int bonus);
void attack(const string &spell);
void printParty(int hp[], int n); // these two are
void printParty(int *hp, int n); // the same thing
int party[] = { 12, 30, 8 };
printParty(party, 3); // nothing is copied
size.vals more efficiently?
void print(int *arr, int size);
void print2(int arr[], int size);
int vals[] = { 1, 3, 4 };
print(vals, 3);
print2(vals, 3);
print() is more efficientprint2() is more efficientint arr[] in a parameter list is int *arr. Both calls pass one address; neither copies the array.
print2() compile?
void print2(int arr[], int size);
int vals[] = { 1, 3, 4 };
int val = 7;
print2(vals, 3);
print2(&val, 1);
&val is an address, and int arr[] takes an addressprint2() requires an actual array&val is a valid int*. It compiles happily; passing a size larger than 1 would then read memory that isn't yours.
const
int totalHP(const int hp[], int size) {
int total = 0;
for (int i = 0; i < size; ++i) {
total += hp[i];
}
// hp[0] = 99; ❌ compile error: hp is const here
return total;
}
const is a promise to the caller: this function will not modify your data. The compiler enforces the promise.Writing a function the linker can actually find.
(declared with total confidence, defined never)
// IN
int totalHP(const int hp[], int n);
// OUT
void rollStats(int &str, int &dex);
// IN/OUT
void applyDamage(int &hp, int hit);
| Mechanism | Syntax | Copies? | Can change caller's value? |
|---|---|---|---|
| By value | int hp | Yes | No |
| By reference | int &hp | No | Yes |
| By const reference | const string &name | No | No |
int is free.x flow in, out, or in/out of j()?
int j(int val) {
val = val + 1;
return val;
}
int main() {
int x = 3;
int y = j(x);
}
val is a copy. j() changes its own copy and returns it; x back in main() is still 3.
x flow in, out, or in/out of j()?
void j(int &val) {
val = val + 1;
}
int main() {
int x = 3;
j(x);
}
val is a reference to x. The function reads the old value and writes a new one, so data flows both ways.
x flow in, out, or in/out of j()?
void j(int &val) {
val = 17;
}
int main() {
int x = 3;
j(x);
}
makeThree files, one command. make recompiles only what changed.
CXX = g++
CXXFLAGS = -Wall -std=c++17
game: main.o dice.o
$(CXX) $(CXXFLAGS) -o game main.o dice.o
main.o: main.cpp dice.h
$(CXX) $(CXXFLAGS) -c main.cpp
dice.o: dice.cpp dice.h
$(CXX) $(CXXFLAGS) -c dice.cpp
clean:
rm -f game *.o
make Earns Its Keepdice.cpp → only dice.o recompiles, then it relinksmake clean throws away everything built, so you can start freshif/switch and loops read the same as Python, the braces and types are newmake rebuilds only what changed, and tabs matter
This deck stands on earlier CS112 materials by
Joel Adams and Victor Norman · adapted and extended by Eric Araújo