So far, we've covered a lot of the fundamental tools C++ gives us, as well as some general principles of good programming design, in particular including the design of Abstract Data Types (ADTs).
We'll now look at various containers, which allow us to store and organize collections of other objects, and the underlying data structures we can use to implement them efficiently. You could think of this as the entry into the second half of "EECS 280: Programming and Introductory Data Structures" (although we'll come back to some more programming techniques later).
Consider std::vector
, which we've used extensively so far. How would we implement our own vector-like container? Given the operations we'd like (e.g. indexing, push_back()
, etc.), how should the internal memory for each element be arranged? How do we ensure that performance doesn't degrade unacceptably as the number of elements in the vector increases? These are the kinds of questions we'll consider in the next several lectures.
For today, we'll consider one of the fundamental approaches to representing a sequential container in memory - to simply store elements next to each other in memory within a large, contiguous allocation. In C++, an array (not to be confused with std::array
from the C++ standard library), provides a low-level abstraction over a sequence of objects stored within a larger block of memory. It's customary to work with arrays via pointers, including by using pointer arithmetic to compute addresses of individual elements, so we'll cover that too.
1: Introduction to Arrays
1.1
Arrays are a low-level abstraction over a sequence of objects in memory that we can fit into the memory model we've been building up so far…
1.1 Exercise: Introduction to Arrays
Determine whether each of the following statements are true or false. Arrays can be resized if you need more space. The elements in an array are stored contiguously in memory. All elements in a particular array must be the same type. All individual array elements must be the same size in memory. Each array element lives at the same address in memory. Sample solution…Arrays can be resized if you need more space. The elements in an array are stored contiguously in memory. All elements in a particular array must be the same type. All individual array elements must be the same size in memory. Each array element lives at the same address in memory. |
2: Arrays, Pointers, and Pointer Arithmetic
2.1
Because an array is essentially just a sequence of objects (one for each element in the array) that are laid out contiguously in memory, we can leverage pointers (i.e. addresses) to work with arrays. Here's one example, informally: Let's recap the fundamental arithmetic operations with pointers. Assume
Note that in both cases, we don't have to worry about how many bytes are involved - the compiler takes care of that behind the scenes based on the pointer types. We can think about offsets and differences in terms of the sequences of whole objects in memory. Finally, and perhaps one of the biggest takeaways here, indexing in arrays with the
2.1 Exercise: Pointer Arithmetic
Trace this code and draw a memory diagram as you go. Once you're finished, use your diagram to answer the question below. You could also run the lobster simulation to check your work. What values are printed for each of the expressions sent to
Sample solution…
You're welcome to check your solution with this walkthrough video: |
3: Pointer Comparison Operators
3.1
Just like we can do arithmetic with pointers by considering offsets and distances between locations in memory, we can also understand pointer comparisons naturally in terms of addresses. So, basically, It's also worth noting the equality operators
3.1 Exercise: Pointer Comparison
Given an array and some pointers:
Determine whether each of the following expressions evaluates to Sample solution… Why does the last expression yield a guaranteed |
4: Traversal by Pointer
4.1
There are two fundamental ways to approach sequential access of the elements in an array using a loop, which we might also call "traversal" or "iteration" through the array's elements:
Neither traversal by pointer nor traversal by index is fundamentally better or more efficient for arrays. You might hear someone say that traversal by index is slower, but this is generally not true given that modern compilers can optimize both approaches into the same machine code. You should use the one that feels more natural to you or that matches the generally accepted pattern for the code you're writing. In most cases, that's probably traversal by index. However, we're taking a look at traversal by pointer now because:
4.1 Exercise: Traversal By Pointer
Which of the following code snippets correctly implement traversal by pointer? For each, indicate whether it is correct or has a bug. If it has a bug, describe what's wrong. Is it a compile error or a runtime error? How would you fix it?
Surprise! Each of the code snippets above contains at least one mistake. If you didn't find this, double check the ones you marked as correct, or take a look this walkthrough video: |
5: Array Parameters and Functions
5.1
When working with arrays, it's often helpful to write helper functions that process the arrays in some way, perhaps using a loop to iterate through each element and perform some operation. An example of this would be a function that prints out an array… Two big takeaways here:
5.1 Exercise: Pass-by-Pointer
Write a function called You're welcome to check your solution with this walkthrough video: |
6: C-Style Strings
6.1
Let's take a quick look at a direct application of arrays - as the starting point for most reasonable representations of strings and/or text data. In particular, the low-level abstraction of an array of
6.1
Which of the following are true? |