Embedded Wednesdays: Arrays
So far we have looked at all of the basic data types in the C language. This week we look at arrays.
Assume that you have a classroom of students who have all just written an exam. You need to calculate the average for the class, then calculate the difference from the average for each student. If we read in the data for the class, calculating the average is quite simple, but we would have to re-read the data to calculate the difference from the average for each student. We can avoid rereading the data by assigning each student’s score to their own variable:
float studentScore1;
float studentScore2;
float studentScore3;
float studentScore4;
float studentScore5;
…
float classAverage;
readin( studentScore1);
readin( studentScore2);
readin( studentScore3);
...
classAverage = studentScore1 + studentScore2 + studentScore3 …
Our data is stored in memory like:
studentScore1 | 96.0 |
studentScore2 | 48.0 |
studentScore3 | 50.0 |
studentScore4 | 42.0 |
studentScore5 | 17.0 |
... |
The problem with this approach is how do we determine, when we write the program, how many students we are going to have? What do we do if there is a fire in a nearby city and we get a bunch more students? Do we have enough variables defined, or do we have to rewrite our program to accommodate the new students?
What we really need is an array, “a block of consecutive locations in memory that are all referred to by a single name but can be accessed-fetched from or stored into-individually”. [1]
The individual elements of an array in the C language are numbered, and you access the elements using this number as an index. The first element in an array is element 0. Arrays are laid out in memory like:
float studentScores[5];
studentScores[0] | 96.0 |
studentScores[1] | 48.0 |
studentScores[2] | 50.0 |
studentScores[3] | 42.0 |
studentScores[4] | 17.0 |
... |
This is one variable, studentScores, but it holds 5 values.
They start at zero?
C arrays indexes start at 0 rather than 1, the reason for this choice is mired in intrigue and speculation. Some say that it is a holdover from the language that C was derived from, B, which also had 0 origin arrays. Others say that it comes from C’s assembly language roots, where each array element is (index * size) bytes from the start of the array, instead of ((index - 1) * size) bytes, saving a subtraction on every array access. But the most compelling reason that I have seen is simply that it made the compiler easier to write.
Meanwhile, back in the classroom...
Our problem, using an array, would start to look like:
float studentScores[100];
float classAverage;
float studentDifferenceFromAverage[100];
uint8_t quanStudents;
uint8_t i;
readin( quanStudents);
for (i = 0; i < quanStudents; i++) {
readin( studentScores[i]);
classAverage = classAverage + studentScores[i];
}
classAverage = classAverage / quanStudents;
I have declared two arrays, studentScores and studentDifferenceFromAverage, each with 100 elements. We can use an integer to specify the number of the element that we want, in this case i is the index into our array.
We still have the same issue with making sure that we have enough space for all of our students, but we can loop through our array using our index rather than spelling out each student’s variable name explicitly. To add more students, we only need to change the size of our arrays.
In my previous posts I described the basic integer, float, char and bool data types. The syntax to create a single value of that type is to simply state the type you want and provide a name:
uint8_t byteCount;
If you wanted an array of them, you place the number of elements you need in square brackets in the variable declaration:
float dailyTotals[366];
To access the individual elements of the array, we use the same square bracket notation:
float todaysTotal;
todaysTotal = dailyTotals[130];
So the first element of dailyTotals is element 0, and the last is element 355.
The index can be an integer variable:
uint16_t dayNumber = 130;
todaysTotal = dailyTotals[ dayNumber];
Multi-dimensional arrays
Arrays can have more than one dimension. A two dimensional array is declared like:
uint8_t sudokuBoard[9][9];
There is no limit to the number of dimensions you can have, other than the amount of memory in your computer.
If you wanted to make a 3D cube of LEDs, you might have an array indicating whether an individual LED is turned on. This code iterates through the 3-dimensional array, setting each element to false:
#define CUBE_SIZE 8
bool isLEDOn[CUBE_SIZE][CUBE_SIZE][CUBE_SIZE];
uint8_t x, y, z;
for ( x = 0; x < CUBE_SIZE; x++) {
for (y = 0; y < CUBE_SIZE; y++) {
for ( z = 0; z < CUBE_SIZE; z++) {
isLEDOn[x][y][z] = false;
}
}
}
Initializing arrays
To assign initial values to array elements, when your program gets compiled, you put them in curly braces:
int32_t vector[3] = {1,2,3};
A two-dimensional array is just an array of arrays and can be initialized like this:
void main(void) {
int32_t ary[3][3] = { {1,2,3}, {4,5,6}, {7,8,9} };
printf( "%d\n", ary[1][0]);
}
This prints the value 4.
The variable ary is made up of 3 rows of 3 integers. Each row in the initializer is surrounded by a set of braces, and the whole initializer is surrounded by braces: ‘{ }’.
Remember that the indexes used in this example go from 0 to 2. The array is laid out like the following. The first column and first row indicate the index values.
[ ][0] | [ ][1] | [ ][2] | |
---|---|---|---|
[0][ ] | 1 | 2 | 3 |
[1][ ] | 4 | 5 | 6 |
[2][ ] | 7 | 8 | 9 |
Sparse Array Initialization
A sparse array is mostly populated with default values, and only a few values needs to be initialized.
If you are making a sparse array you can use the index notation to set particular values and, if any initializers are given, all uninitialized values are set to zero:
int32_t ary[3][3] = { [1][2] = 42, [0][1] = 17};
[ ][0] | [ ][1] | [ ][2] | |
---|---|---|---|
[0][ ] | 0 | 17 | 0 |
[1][ ] | 0 | 0 | 42 |
[2][ ] | 0 | 0 | 0 |
float weekdayTemperatures[7] = { [3] = 42.0F };
0.0 | 0.0 | 0.0 | 42.0 | 0.0 | 0.0 | 0.0 |
---|
Character Arrays
Character arrays are also known as strings. They can be initialized using a list of characters or a quoted string:
char vowels[6] = { 'a', 'e', 'i', 'o', 'u', 'y'};
char hello[] = "Hello World!";
The second example uses the compiler’s ability to figure out how many elements are needed. This can be applied to any single dimensional array type where you specify all of the initial values:
int32_t ary[] = { 1,2,3,4,5,6,7,8,9};
Will give you a 9 element array.
C weirdness of the week
Arrays in the C language have some weirdness. First, and most important, arrays start at element 0 instead of 1. So an array of 10 elements goes from 0 through 9.
Next, C does not check the array size against the index, so if you access element 42 of a 10 element array, C will do that. Also if you access element -3, C will do that too. It isn’t enough to declare your index variable as an unsigned integer, you have to make sure it stays within the bounds that you have declared.
Next week we’ll look at the basic structure of a C program and look at some instructions to go along with our data.
[1] Programming and Languages, C. William Gear, SRA, 1978
This post is part of a series. Please see the other posts here.