Presented by UFMFGT-15-1: Programming for Engineers
Tom Knowles Week 15: Arrays and Loops
Code: XX-XX-XX
22 February, 2024
This session
• Arrays
o Scalar vs array data types
o How to declare them
o How to access their items
• Loops
o while, do-while, for
o Flow control within loops
• Loops and arrays together
Why do we need Arrays and Loops?
• Up to this point, you can make programs that can do user I/O and make decisions
o scanf, printf
o Flow control (if, switch)
Arrays Loops
• Declare a single block of • Runs a block of code a
memory number of times
• Holding multiple variables of • Can run repeatedly until a
the same type condition is met
• Can be treated as a group or • Can work with array items
individual items iteratively
Scalar declaration
data
type
name = value ;
• We can choose any valid combination of letters, numbers and underscores
• Names like this represent scalar variables – they are completely independent
Array declaration
data
type
name [N] = value
;
• Adding square brackets lets us declare how many memory items to associate with this
variable name
• Functionally, this is the same as declaring N variables one after the other
o Except, we can refer to them as a group by their name
Scalar vs Array
Scalar Declaration Array Declaration
int sc1; int arr[3];
int sc2;
int sc3; arr[0] = 1;
arr[1] = 2;
sc1 = 1; arr[2] = 3;
sc2 = 2;
sc3 = 3;
printf("My array items:
printf("My scalars: %d, %d, %d, %d",
%d, %d", sc1, sc2, arr[0], arr[1],
sc3); arr[2]);
Scalar vs Array
Scalar Declaration Array Declaration
int sc1 = 1; int arr[3] = {1, 2, 3};
int sc2 = 2;
int sc3 = 3;
printf("My scalars: %d, printf("My array items:
%d, %d", sc1, sc2, %d, %d, %d",
sc3); arr[0], arr[1],
arr[2]);
Scalar vs Array
Scalar Declaration Array Declaration
int sc1 = 1; int arr[] = {1, 2, 3};
int sc2 = 2;
int sc3 = 3;
printf("My scalars: %d, printf("My array items:
%d, %d", sc1, sc2, %d, %d, %d",
sc3); arr[0], arr[1],
arr[2]);
Designated Initialisers
Array Declaration
• Not all of an array needs to be initialised
• If we only want specific non-default values, int arr[100] = {[3] = 1,
we can refer to specific items within [49]= 3,
our initialisation [70] = 7};
• This assigns values to those indexes only;
the rest remain default for that data type
printf("My array items:
%d, %d, %d",
arr[0], arr[1],
arr[2]);
Using whole arrays
Array Declaration
• The array variable name is a pointer to int arr[] = {1, 2, 3};
where the array starts
• C represents the variables as they are in
memory; contiguous
arr
• When referring to the array itself, we can arr[0] arr[1] arr[2]
1 2 3
also refer to it as a whole by 'referencing'
the variable arr[0]; finding its starting
memory location
• Functionally, the difference ends up being
adding an 'address of' operator in front of
the array item to refer to the whole thing
Example: user input
Array Declaration
• Familiar from the 2nd week of exercises -> char input[3];
scanf("%2s", input); Whole array
input[0] input[1] input[2] referenced
input 'h' 'i' '\0' printf("%d,%d,%d",
input[0],
input[1], input[2]);
Array items
referenced
input[0] input[1] input[2]
input # # #
Example: user input
Array Declaration
• Familiar from the 2nd week of exercises -> char input[3];
&input[0]
scanf("%2s", &input[0]);Whole array
input[0] input[1] input[2] referenced
input 'h' 'i' '\0' printf("%d,%d,%d",
input[0],
input[1], input[2]);
Array items
&input[0] referenced
&
input[0] input[1] input[2] 'address of'
# # #
operator
input
Loops
• Structures that repeat an inner block of code
• Come in three flavours:
While Do-While For
• Runs whilst a • Runs whilst a • Runs a set number of
conditional expression conditional expression iterations
is true is true • Stops once the last
• Stops when this • Stops when iteration has been run
becomes false this becomes false • Uses its own
• Checks the expression • Checks the expression dedicated iterator
before the first after the first iteration variable
iteration
While
int start, end;
• While the expression is true, execute code scanf("%d %d", &start, &end);
block repeatedly
• Expression ideally will change at some point int t = start;
to allow the while loop to exit
while (t < end) {
• Does this loop ever end?
printf("%d", t);
t++;
}
While
int start, end;
• Yes; regardless of how large end is, t will scanf("%d %d", &start, &end);
eventually reach it
• If start >= end, it won't run int t = start;
while (t < end) {
printf("%d", t);
t++;
}
While
int start, end;
• Adding if statements to catch scanf("%d %d", &start, &end);
troublesome inputs is good practice
• Will this code complete, given enough int t = start;
time?
if start >= end {
printf("Warning: start time is
larger than end time.");
} else {
while (start < end) {
printf("%d", t);
t++;
}
}
While
int start, end;
• No! We have made a mistake, and are not scanf("%d %d", &start, &end);
checking the right variables
int t = start;
if start >= end {
printf("Warning: start time is
larger than end time.");
} else {
while (start < end) {
printf("%d", t);
t++;
}
}
While
int start, end;
• Now it is checking the correct variables in scanf("%d %d", &start, &end);
the expression
int t = start;
if start >= end {
printf("Warning: start time is
larger than end time.");
} else {
while (t < end) {
printf("%d", t);
t++;
}
}
While
int start, end;
• Similarly, a loop without a proper change in scanf("%d %d", &start, &end);
its expression could 'step over' the
expression that would stop the loop int t = start;
while (t != end) {
printf("%d", t);
t = t + 2;
}
While
int start, end;
• Sticking to lower-specificity expressions can scanf("%d %d", &start, &end);
catch these cases
int t = start;
while (t <= end) {
printf("%d", t);
t = t + 2;
}
Do-While
#include <robot.h> // not a real .h
int status;
• A while loop flipped on its head
• Performs the loop body, then checks the do {
condition
status = startUp();
• Great for if something needs to run at least
once before checking if it needs to be run if (status != 0) {
again
• Couple of easy things to miss printf("Startup exited with
o Semicolon after the while expression status
%d: retrying...",
o No condition after the do keyword
status);
}
} while (status != 0);
For
#include <dataSource.h> // not a real
.h
• A while loop that uses an iterator rather int data[10] = loadData();
than an expression
• An iterator is a variable that tracks which for (int i = 0; i < loops; i++) {
loop is currently being run
// do something until !(i <
• Always an integer, and always defined as loops)
part of the loop
o If you want to check an external }
variable's value, use a while loop with a
relational expression
For
#include <dataSource.h> // not a real
.h
Dedicated iterator (int i = 0; int data[10] = loadData();
int loops = 10;
Conditional
expression i < loops; for (int i = 0; i < loops; i++) {
Iterator change per
loop i++) // do something until !(i <
loops)
• Each for loop has three parts to its
expression }
• Typically an increment (counting upwards)
• Can add more that 1 each time
• Can decrement
For loops and arrays
#include <sensor.h> // not a real .h
&data[0] int samples = 10;
int data[] = readData(samples);
data[0] data[...] data[9]
? . . . ?
for (int i = 0; i < samples; i++) {
data
currentItem = data[i];
printf("Value of item %d = %d",
• Consider an array of known size, but i, currentItem);
unknown contents
}
• We might want to 'unpack' the
array; getting all of its values }
• We can use the iterator as an index
• Don't mix up the index (location) of the
data and the data at that index
Inspecting arrays
#include <stdio.h> // includes
&data[0] <stddef.h>
int samples = 10;
data[0] data[...] data[N] int data[] = readData(samples);
data ? . . . ? printf("Size of data (in bytes): %d",
sizeof(data));
Size of data (in bytes): 40
• Unlike some languages, C arrays are very
barebones
• They are not objects and do not have
properties outside of their data contents
• However, <stdio.h> has some functions #include <stdio.h> // includes
to help us work with them eg. sizeof() <stddef.h>
• This can also be used to find how large a
printf("Size of int: %d",
given data type is in memory Size of int (in bytes):
sizeof(int)); 4
Inspecting arrays
#include <stdio.h> // includes
&data[0] <stddef.h>
int samples = 10;
data[0] data[...] data[N] int data[] = readData(samples);
data ? . . . ? printf("Size of data (in elements):
%d", sizeof(data) / sizeof(int));
Size of data (in elements):
• To emulate the behaviour of MATLAB’s 10
numel() or Python’s len(), we can divide the
size in bytes, by the size of an integer in
memory
• This gives how many elements (in this case,
integers) exist in the array
• We can use this in our loop conditional
expressions to allow for varying array sizes
Memory overrun
#include <sensor.h> // not a real .h
&data[0] int samples = 10;
int data[] = readData(samples);
data[0] data[...] data[9] int nextItem;
data ? . . . ? for (int i = 0; i < samples; i++) {
nextItem = data[i+1];
• It is possible to refer to items outside of the printf("Value of next item %d =
array %d",
o What is presented as an index is really i+1, nextItem);
just a memory offset of }
sizeof(dataType)
• When this happens, behaviour is undefined }
• Make sure to keep indexing variables within
the bounds of the array
Memory overrun
#include <sensor.h> // not a real .h
&data[0] int samples = 10;
int data[] = readData(samples);
data[0] data[...] data[9] int nextItem;
data ? . . . ? for (int i = 0; i < samples; i++) {
if ((i + 1) < samples) {
• Like with while loops, we can catch nextItem = data[i+1];
problematic cases with if statements
} else {
nextItem = data[i];
printf("Value of next item %d =
%d",
i+1, nextItem);
}
Flow control - break
int emailAddress[40];
&data[0] int size = sizeof(emailAddress);
scanf("%s", emailAddress);
data[0] data[...] data[9] int username[20];
data ? . . . ? for (int i = 0; i < size; i++) {
if(emailAddress[i] == '@') {
• At times, we may want to end the loop break;
structure early
}
• Useful if the rest of the for loop is
redundant for the array printf("%c", emailAddress[i]);
• Note that this moves the program counter
outside the for block, regardless of the }
status of the iterator
Flow control - continue
int emailAddress[40];
&data[0] int size = sizeof(emailAddress);
scanf("%s", &emailAddress);
data[0] data[...] data[9] int username[20];
data ? . . . ? for (int i = 0; i < samples; i++) {
if(emailAddress[i] == '.') {
• At times, we may want to end the loop and continue;
restart
}
• Useful if the rest of the loop is redundant
for that item if(emailAddress[i] == '@')
• Note that this keeps the program counter {break;}
inside the for block, and will carry on to
the next iteration (if any) printf("%c", emailAddress[i]);
}
Casting arrays
&input[0] &output[0]
input[0] input[...] input[9] output[0] output[...] output[9]
input char . . . char int . . . int output
• Last week we explored the idea of casting; char input[10];
converting one variable to another data type scanf("%s", &input[0]);
• Casting arrays is tricky, because they are int output[10];
contiguous
• Generally, it is better to cast the items and for (int i = 0; i < sizeof(input); i+
+){
reassemble a new array iteratively
• We can use the sizeof() function to tell output[i] = (int)input[i];
us the size of the array
}
2D Arrays
int input[3][3] = {{1, 2, 3},
&input[0][0] {4, 5, 6},
{7, 8, 9}};
input[0] input[1] input[2]
? . . . ?
input[0][0] input[1][0] input[2][0]
• 2D (and higher) arrays are perfectly possible
1 4 7 • Each array item is itself an array
• Still contiguous in memory; this just helps us
organise certain data more naturally
input[0][1] input[1][1] input[2][1]
• Examples:
input
2 5 8 o Greyscale image data (3D for RGB image)
o Data tables
input[0][2] input[1][2] input[2][2]
3 6 9
Working with 2D Arrays
int input[3][3] = {{1, 2, 3},
&input[0][0] {4, 5, 6},
{7, 8, 9}};
input[0] input[1] input[2]
? . . . ?
input[0][0] input[1][0] input[2][0] n_cols = sizeof(input[0]);
n_rows = sizeof(input) / n_cols;
1 4 7 for (int i = 0; i < n_cols; i++){
input[0][1] input[1][1] input[2][1] for (int j = 0; j < n_rows; j++){
input
2 5 8
printf("Item (%d, %d): %d",
i, j, output[i][j]);
input[0][2] input[1][2] input[2][2] }
}
3 6 9 • Scales poorly, but is essential for working
with high-dimensional datasets
Flow control - break
int input[3][3] = {{1, 2, 3},
{4, 5, 6},
• break statements can also be used within {7, 8, 9}};
nested loops
• They only exit the innermost block they are n_cols = sizeof(input[0]);
called in, not all parent blocks n_rows = sizeof(input) / n_cols;
for (int i = 0; i < n_cols; i++){
for (int j = 0; j < n_rows; j++){
// do something
break;
}
Flow control - continue
int input[3][3] = {{1, 2, 3},
{4, 5, 6},
• continue can also be used within nested {7, 8, 9}};
loops
• Like break, continue stays within its n_cols = sizeof(input[0]);
block n_rows = sizeof(input) / n_cols;
for (int i = 0; i < n_cols; i++){
for (int j = 0; j < n_rows; j++){
// do something
continue;
}
Flow control - goto
int input[3][3] = {{1, 2, 3},
{4, 5, 6},
• If we want to exit several layers of {7, 8, 9}};
nested blocks at once, we can use goto
• Can simplify highly nested code n_cols = sizeof(input[0]);
n_rows = sizeof(input) / n_cols;
• It is, however, very difficult to debug
when overused for (int i = 0; i < n_cols; i++){
• If you find yourself wanting to use goto,
consider if there isn't a way to refactor your for (int j = 0; j < n_rows; j++){
code or rethink the problem first
// do something
goto exitPoint;
exitPoint: // more code below
Summary
• New data structure
o 1D arrays Attendance Code:
o ND arrays
XX-XX-XX
• Loops
o while
o do-while
o for
• Continue, break and goto
• Arrays and loops together
Assessment FAQs
Question Answer
Do I need to submit my source code? Yes; zipped, as per recent video demo
Do I need to do
flowcharts/pseudocode for every Yes; this is worth as much as the
exercise? source code itself
Do I need to include program banners
in my source code? Yes; see lecture 1 for what to include
When is the first deadline? This Sunday; 25th Feb, 2pm. #1 and #2
None for the progress submissions, yes
What about RAs/extensions? for the final submission