Schedule‎ > ‎

09A: Arrays and Vectors

Arrays

Learner Outcomes

At the end of the lesson the student will be able to:

  • Declare and allocate memory for arrays
  • Generate code to initialize arrays
  • Access array elements
  • Use arrays with functions

^ top

Introduction to Arrays

  • The most basic way to organize lists of the same type is to use arrays
  • Arrays are a low-level construct and can be inconvenient (but are fast and use minimal memory)
  • Arrays cannot be resized
  • We usually create some extra space in each array and check to make sure we do not run out of room
  • Arrays work in C and C++. C++ has more advanced ways to store lists.
  • Arrays are fast and efficient. 

Check Yourself

  1. True or false: arrays cannot be resized.
  2. True or false: we use arrays because they are the most convenient

^ top

Strings and Character Arrays

The C programming language has existed since the early 1970s. As computers have become more powerful the C (and eventually C++) language has gained features that make programming easier for the programmer at the cost of program performance. The place where C's evolution is most apparent is in how strings are stored and manipulated.

Strings are sequences of characters. They're important for human interaction. You've already used them extensively (though you may not have realized it). The simplest form of string is a constant string. Here's an example:

Serial.println("Hello World");

So what is "Hello World" to C/C++? It's an array of characters. Each array element has one character in it. The first (left most) letter is the zeroth element in the array and the array has a hidden null element at the end. The picture shows the array that's generated by the above string: 

Notice the "null" character at the end of the string. Functions that use strings need to know where the string ends. Those functions read through the string until they see the null character ('\0'). Forgetting the null character is one of the major pitfalls of using character arrays in C and C++. It's the most common cause of buffer overflow attacks on programs today. 

The table below describes the evolving syntax of strings:

 Declaration  Support  Example 
 char *s = "Hello World"; C/C++ 
 Always
void setup() {
  Serial.begin(9600);
}
void p(char *s) {
  int i=0; 
  Serial.print("\"");
  while (s[i] != '\0') {
    Serial.print(s[i]);
    i++;
  }      
  Serial.print("\" Length: ");
  Serial.println(i+1);
}

void loop() {
  char *s = "Hello World";
  p(s);
}
 char s[] = "Hello World"; C/C++
 Always
void setup() {
  Serial.begin(9600);
}
void p(char s[]) {
  int i=0; 
  Serial.print("\"");
  while (s[i] != '\0') {
    Serial.print(s[i]);
    i++;
  }      
  Serial.print("\" Length: ");
  Serial.println(i+1);
}

void loop() {
  char s[] = "Hello World";
  p(s);
}
 string s = "Hello World"; C++98
#include <cs-11m.h>
void setup() {
  Serial.begin(9600);
}
void p(string &s) {
  Serial.print("\"");
  Serial.print(s.c_str());
  Serial.print("\" Length: ");
  Serial.println(s.size());
}

void loop() {
  string s = "Hello World";
  p(s);
}
 String s = "Hello World"; C++

void setup() {
  Serial.begin(9600);
}
void p(String &s) {
  Serial.print("\"");
  Serial.print(s);
  Serial.print("\" Length: ");
  Serial.println(s.length());
}

void loop() {
  String s = "Hello World";
  p(s);
}

Try It: String Sizes

  • Use each of the string types in a program
  • Can you spot the difference between C-Strings and C++ strings? 
  • Why do you think the difference exists? 

Use for C-Strings

  • Many string functions in the standard library depend on '\0' terminators in character arrays
  • For example, the following function returns the size of a C-string:
    int strlen(const char s[]) {
        int i = 0;
        while (s[i] != '\0') {
            i++;
        }
        return i;
    }
    
  • This function will not work without the null '\0' terminator in the char array
  • Generally we should use the String class and avoid using char arrays
    • Arduino uses the String class (notice the upper case "S")
  • The String class is safer and far more convenient
  • Sometimes we need to convert a String into a C-string to call a function written before the String class was invented
  • We can easily convert a String to a C-string using the c_str() member function
  • For example, we may want to convert a String to a number using atof(), which needs a C-string argument:
    String input = "9.99";
    double num = atof(input.c_str());
    Serial.println(num);
    

Check Yourself

  1. The size of the following C-string is ________.
    char msg[] = "Hello";
    
  2. True or false: all C-strings require a '\0' character at the end, and an assignment statement adds one automatically.
  3. True or false: some useful C functions require a C-string parameter and will not work with type String.
  4. To convert a String to a C-string, use the String member function ________.

More Information On C-string Functions

^ top

Declaring and Initializing Arrays

  • Declaring an array is easy:
    dataType variableName[size];
    
  • Where:
    • dataType: the data type of all the array items
    • variableName: the name you make up for the array
    • size: the number of data items the array can hold
  • For example, the following is the declaration of an array named scores that holds 10 values of type int:
    int scores[10];
  • Arrays like this can never change size and the array size must be set when the program is compiled
  • When defining an array, you must guess the maximum number of elements you need to store:
    const int MAX_SCORES = 100;
    int scores[MAX_SCORES];
    
  • The programmer must keep track of the capacity
  • We use a constant to hold the capacity of the array as shown above
  • This allows us to know the size anywhere in our code
  • If we need to change the size, we change only a single constant and recompile

Initializing Array Items

  • We specify which slot of an array to access with the [] operator:
    scores[4] = 98;
  • The indexes of arrays are numbered starting at 0
  • We can assign a value to an array element any time after it is declared:
    const int MAX_SCORES = 5;
    int scores[MAX_SCORES];
    scores[0] = 90;
    scores[1] = 95;
    scores[2] = 87;
    scores[3] = 89;
    scores[4] = 98; // fifth item
    
  • We can initialize array elements in the declaration statement:
    • Called static initialization
    • We use a comma-separated list inside curly-braces
  • For example:
    int scores[] = { 90, 95, 87, 89, 98 };
    
  • This produces the same array as in the previous example
  • The compiler computes the size automatically by counting the items in the list
  • If we want a larger array with only the first few elements initialized, we can use:
    int scores[MAX_SCORES] = {90, 95, 87};
    
  • Note that if we do not assign a value to an array element, its value is not known.  Always initialize your arrays to an initial value like 0.  Uninitialized array elements are a common source of program error.

Example Program Using Arrays

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <ArduinoSTL.h>
using namespace std;

void setup() {
Serial.begin(9600);
}

void loop() {
const int MAX_SCORES = 10;
int scores[MAX_SCORES];
cout << "Enter " << MAX_SCORES << " scores:" << endl;
for (int i = 0; i < MAX_SCORES; i++) {
cin >> scores[i];
}
cout << "You entered: ";
for (int i = 0; i < MAX_SCORES; i++) {
cout << scores[i] << " ";
}
cout << endl;
}

Be sure to set your Serial Monitor to add newlines when trying the above.

Try It: Define an Array (3m)

  1. Begin a new Arduino program.
  2. Inside loop(), add a statement to define a String array named names that is suitable for holding a list of three (3) names.
  3. Compile your code to make sure it has correct syntax.

    If you have problems, ask a classmate or the instructor for help as needed.

  4. Assign values to each of the array elements like:
    names[0] = "Sharon Strelitz";
    
  5. Add a for-loop to display all three array values.  Remember that you have to use Serial.print with Strings.
  6. Compile your code to make sure it has correct syntax.

    If you have problems, ask a classmate or the instructor for help as needed.

  7. Review the summary below and be prepared to answer the following Check Yourself questions when called upon.

Check Yourself

  1. Enter the code to declare an array, named myArray, and allocate space for 10 values of type double.
  2. The first element in any array has an index value of ________.
    1. -1
    2. 0
    3. 1
    4. 2
  3. Of the following, ________ will correctly access the fifth element of an array named "foo".
    1. foo[5]
    2. foo(5)
    3. foo[4]
    4. array.foo[4]
  4. Enter the code to declare an array, named myArray, allocate space for 5 doubles and assign the values 1.2, 2.3 and 3.4 to the first 3 elements.
  5. True or false: the programmer must keep track of the size of an array, which should be stored in a constant.

^ top

Arrays as Function Parameters

  • When writing a function with an array parameter, we place an empty [] after the parameter name:
    void print(int values[], int size);
    
  • We pass the size of the array to the function so the function knows the size of the array
    • There is no size() member function for an array
  • When we call the function, we do NOT include the []:
    print(data, size); // function call
    
  • Unlike other parameters, array parameters are always passed by reference
  • However, we use [] rather than an & when declaring the array parameter
  • For example:
  • int mySize = 0;
  • read(funArray, 10, mySize);
  • cout << mySize;      //this will be the number of times cin executed inside the read() function.
  • void read(int data[], int capacity, int& size) {
    size = 0;
    while (size < capacity) {
    cin >> data[size];
    size++;
    }
    }
  • Note the use of both the capacity and size parameters
  • The capacity parameter tells the function the maximum array size
  • The size parameter records how many items the user entered into the array
  • The programmer must keep track of both the capacity and size when working with arrays
  • Note that arrays cannot be function return types
  • Since arrays are always passed by reference, then returning an array with a return statement is not necessary

Using the const Modifier

  • Normally, a function can change the values of array elements
  • We can prevent the modification using the const modifier:
    void print(const int values[], int size);
    
  • The compiler will issue an error message if you try to change the value of an array element
  • If a function with a constant array parameter calls another function using the const array parameter as an argument, the called function must use a const array parameter as well
  • Otherwise, the compiler will issue an error
  • The following program shows arrays used with functions

Example Program Using Arrays with Functions

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
#include <ArduinoSTL.h>
using namespace std;

void setup() {
Serial.begin(9600);
}

/**
Fills an array with values from the keyboard.

@param data The array to fill with data.
@param capacity The capacity of the array.
@param size The number of items entered. user indicates end of input with a negative number
*/

void read(int data[], int capacity, int& size) {
size = 0;
bool stop = false;
while (size < capacity && stop == false) {
cin >> data[size];
if (data[size] < 0) {
stop == true;
//cout << "stop is true. size before ++ is " << size << endl;
return;
}
size++;
}
}


/**
Displays an array to the screen

@param data The array to fill with data.
@param size The number of items in the array.
*/
void print(const int values[], int size) {
for (int i = 0; i < size; i++) {
cout << values[i] << endl;
}
}

void loop() {
const int MAX_ITEMS = 10;
int data[MAX_ITEMS];
int size = 0;

cout << "Enter numbers (-1 to quit):" << endl;
read(data, MAX_ITEMS, size);

cout << "You entered:";
print(data, size);
}

Check Yourself

  1. The problem with the following function is ________.
    double[] readItems(double data[], int cap, int& size) {
        size = 0;
        double value;
        bool more = true;
        while (more) {
            if (size < capacity) {
                value = Serial.parseFloat();
                data[size] = value;
                size++;
            } else {
                more = false;
            }
        return data;
    }
    
    1. the data parameter is missing the size inside the square brackets
    2. you cannot assign a value to the parameter size
    3. array parameters require an ampersand (&)
    4. arrays cannot be return types
  2. True or false: array parameters do not include the array size inside the square brackets.
  3. To prevent a function from modifying an array, add the ________ keyword to the parameter declaration.

^ top

Exercise 1: Exploring Arrays 15m)

In this exercise we explore declaring, allocating and assigning values to arrays containing lists of data.

Specifications

  1. Create the myarrays.ino file, complete the following exercise:
  2. Add the following function to the code:
  3. void print(const int values[], int size) {
    for (int i = 0; i < size; i++) {
    cout << values[i] << " ";
    }
    cout << endl;
    }
  4. Compile your code to make sure it has correct syntax.

    If you have problems, ask a classmate or the instructor for help as needed.

  5. Declare and initialize an array for a list of 10 integer scores after the current arrays using the following code:
    const int NUM_SCORES = 10;
    int scores[NUM_SCORES] = {90, 91, 92, 93, 94, 95, 96, 97, 98, 99};
    

    For more information see section Declaring and Initializing Arrays.

  6. After declaring and initializing the array, call the print() function using the code:
    cout << "Integer scores:";
    print(scores, NUM_SCORES);
    

    For more information see section 9.3.3: Arrays as Function Parameters.

  7. Compile and run the program to make sure you made the changes correctly. When you run the program, the output should look like:
    Integer scores:
    90 91 92 93 94 95 96 97 98 99
    
  8. Declare and initialize an array of double values holding the temperatures 25.730.3 and 40.9 in order.

    For more information see section Declaring and Initializing Arrays.

  9. Write another print() function with two parameters: one for the array of double values and one for the size of the array.  Notice that you can re-use the function name print() because each print() has different parameter types.  Remember that this is called Function Overloading.

    For more information see section Arrays as Function Parameters.

  10. After declaring and initializing the array, call the print() function.

    For more information see section Arrays as Function Parameters.

  11. Compile and run the program to make sure you made the changes correctly. When you run the program, the output should look like:
    Integer scores:
    90 91 92 93 94 95 96 97 98 99
    Double temperatures:
    25.7 30.3 40.9
    
  12. Declare and allocate an array of char values and initialize it with the vowels 'a', 'e', 'i', 'o', 'u' and '\0'. (backslash-zero)

    For more information see section 9.3.4: Character Arrays.

  13. After declaring and initializing the array, write a new print() function to print your char array.

    For more information see section 9.3.4: Character Arrays.

  14. Compile and run the program to make sure you made the changes correctly. When you run the program, the output should look like:
    Integer scores:
    90 91 92 93 94 95 96 97 98 99
    Double temperatures:
    25.7 30.3 40.9
    Char vowels:
    a e i o u
    
  15. Save your program source code as myarrays.ino and submit to Canvas.

Check Yourself

Be prepared to answer these questions. You can find more information by following the links after the question.

  1. What is the syntax for declaring an array? (9.3.1)
  2. Why must the programmer track both the capacity and number of elements used in an array? (9.3.1)
  3. What are two ways to declare and initialize an array? (9.3.2)
  4. What is the syntax of an array parameter? (9.3.3)
  5. True or false? An array parameter is always pass by reference. (9.3.3)
  6. What is a C-string or char array? (9.3.4)
  7. What is the ending character for a C-string or char array? (9.3.4)
  8. How large should you make a C-string or char array? Why? (9.3.4)
  9. What function can you use to convert a String to a C-string char array? (9.3.4)
  10. What function can you use to convert a C-string char array to a double? (9.3.4)

^ top

Summary

  • In this section we looked at how to use arrays
  • Arrays are a lower-level abstraction than vectors, so they are less convenient
  • Vectors are a recent addition to C++, so many older programs use arrays instead
  • Declaring an array is similar to declaring a vector:
    int scores[10]
  • Arrays can never change size and the array size must be set when the program is compiled
  • When defining an array, we must guess on the maximum number of elements we need to store
  • Then we must keep track of both the capacity and number of elements in use in an array
  • When writing a function with an array parameter, we place an empty [] after the parameter name:
    void print(int values[], int size);
    
  • We need to pass the size of the array into the function, because the function has no other way of knowing the size of the array
  • Sometimes we must pass both the capacity and size of an array to a function:
    void read(int data[], int capacity, int& size);
    
  • The capacity parameter tells the function the maximum array size
  • The size parameter records how many items the user entered into the array
  • Unlike all other parameters, array parameters are always passed by reference
  • However, we never use an & when defining an array parameter
  • Also note that functions cannot return arrays
  • There was a time when C++ had no String class
  • Instead, we used a variation of a char array sometimes called a C-string
  • Generally we should use the String class and avoid using char arrays
  • The string class is safer and far more convenient
  • Sometimes we need to convert a string into a C-string to call a function written before the string class was invented
  • We can easily convert a String to a C-string using the c_str() member function:
    string input = "9.99";
    double num = atof(input.c_str());
    cout << num;

Vectors

Arrays have existed as long as there's been C and they way they work was considered a major advancement in the ability to efficiently program a computer. Their great efficiency comes at a cost to the programmer. They are difficult to use because they cannot be resized and stepping over their boundary causes programs to crash (or worse, is a way for computer viruses to enter the system). What if you wanted to store input into an array but you didn't know how much input your program would get? There's no simple answer to that question, but in C++ there's a better way (that's almost as efficient): vectors.

Including Vector Support in Arduino


#include <ArduinoSTL.h>    //this also contains vectors for the arduino
using namespace std;

void setup() {
  // put your setup code here, to run once:

}

void loop() {
  // put your main code here, to run repeatedly:

}

Now you will be able to compile the examples in this section.

Defining a Vector

Just like an array a vector must have a type associated with it, also just like an array you can only store one type in a vector. Here an example of a vector declaration:

vector<int> my_vector_of_ints; // no size specified
vector<char> my_vector_of_chars(5); //size of 5 specified

 
Notice that you don't have to specify a maximum size. Vectors automatically grow and shrink when you add and remove elements. The type of the vector is between the '<' and '>' symbols. This is called "template" syntax in C++ and is a topic for a more advanced course. Now that you have a vector what can you do with it? 

Using Vectors

If you're familiar with arrays vectors are a relief. You can access elements in a vector just like accessing elements in an array:

vector<int> my_vector(1);  //size of 1 integer
...
my_vector[0] = 1; //can only use this method if vector size indicated on declaration
cout << vector[0];

You can also add an element to the back of the vector without first knowing how many elements are in the vector: 

vector<int> my_vector(1);
my_vector.push_back(10);  // adds the integer 10 to the end of the vector
my_vector.push_back(11); //add the integer 11 to the end of the vector, new size()is 2
my_vector.push_back(12); //new size() is 3

You don't need to keep track of how many elements are in a vector, it does that for you:

vector<int> my_vector(2); 
...
int elements = my_vector.size(); 
my_vector.push_back(1);
my_vector.push_back(2);
int foo = my_vector.size() - elements; 
// What is the value of foo? 

Passing Vectors to Functions

Vectors are designed for convenience and ease. As you saw in the previous section arrays are always passed to functions by reference. Vectors can be passed by reference or by value. Beware passing a vector by value makes a complete copy of the vector!  This can be a problem when you have limited memory (like on the Aruduino).

VectorsTry It Yourself 

1.  Write a program with these two functions:


void add_ten_by_value(vector<int> vect) {
  vect.push_back(10)
}

void add_ten_by_reference(vector<int> &vect) {
  vect.push_back(10);
}

2.  Your program should be able to print every value in an array. You can do that like this:

for (int i=0; i<vect.size(); i++) {
    cout << vect[i];
}

3.  In your program create a vector and call both of the above functions using your vector.
4.  First call the add_ten_by_value,  then call add_ten_by_reference.
5.  Print out what is in the vector after each call (add_ten_by_value vs. by_reference).
6.  Questions:
  1. How many 10's were added to your vector in add_ten_by_value?
  2. How many 10's were added to your vector in add_ten_by_reference?
  3. Why?

Exercise 2: From Array to Vector

Write a function that takes the values from an array and places them into a vector. Start with the following code:

#include <ArduinoSTL.h>
using namespace std;

void setup() {
  Serial.begin(9600);
}

// put toVect() here

void loop() {
  vector<int> vect; 
  int numbers[] = {10, 34, 23, 43};
  toVector(vect, numbers, 4);
}

Specifications:
  1. Add the toVector() function before the loop() function.
  2. After calling toVector() the vector vect must have the same values as numbers[]
  3. The order is important (vect must be in the same order): use the vector push_back function as assignment (vect[i] = array[i];) doesn't change the vector size() function properly.
  4. Be sure to print the values in the loop() function. You may add a separate print() function.
  5. Define toVector() by reference, not value, for the vector<int> parameter.
Submit your vector.ino file on Canvas with the homework.

Subpages (1): vector
Comments