Unit 06: CS 200 review - Pointers

  1. Memory addresses
  2. Pointer variables
  3. Program memory layout
  4. Using pointers

  5. Example code and additional resources

This week's stuff:

Accessibility

You can read through / play the audio for each slide at your own pace, or

Memory addresses

  • Memory addresses: When we declare a variable, what we're actually doing is telling the computer to set aside some memory (in RAM) to hold some information. Depending on what data type we declare, a different amount of memory will need to be reserved for that variable.

Here's an array of floats, and the address of each element:

ELEMENT:        arr[0]              arr[1]              arr[2]              arr[3]              arr[4]
ADDRESS (HEX):  0x7ffd67be86e0      0x7ffd67be86e4      0x7ffd67be86e8      0x7ffd67be86ec      0x7ffd67be86f0
ADDRESS (DEC):  140726343993056     140726343993060     140726343993064     140726343993068     140726343993072
                ^ Each element is 4 bytes apart in memory

A float has a size of 4 bytes, and each element in the array sequence is different by only 4 bytes as well - each element of an array is contiguous in memory.

Pointer variables

  • Pointers: We can declare special variables that store memory addresses rather than storing an int or float or char value. These variables are called pointers.
int number; int* ptr;

Program memory layout

  • Stack storage: Local variables and parameter variables are stored in stack memory space. There is a limited amount of stack storage available to a program. This is why if your recursive function has a bug that would make it repeat infinitely, it will eventually crash - stack overflow from declaring its parameter variables too many times in memory.
  • Heap storage: There is a virtually unlimited amount of available storage in heap memory space (as long as there is free memory in general). However, this memory can only be accessed via pointers. When we use the new keyword with a pointer, we allocate memory dynamically in heap space.

Using pointers

Declaring a pointer requires specifying a data type, the asterisk symbol *, and a variable name for the pointer. Note that the asterisk can be attached to the data type, variable name, or on its own. I prefer attaching it to the data type. Assigning an address to a pointer - The ampersand & put before a variable will give you its address - this is known as the address-of operator. When not in use, a pointer should be initialized to nullptr for safety. Copying an address from one pointer to another - A basic assignment statement will copy the value from one variable to another. This also works with pointers, since pointer values are addresses. Dereferencing a pointer requires prefixing the pointer's name with an asterisk. Dereferencing a pointer allows us to access the value stored within the address that the pointer is pointing to.
// Declare pointers
int* ptr;
float * ptr2;
string *ptr3;
// Assign ptr to 
// address of some_variable
ptr = &some_variable
// Assign to nullptr
ptr = nullptr;
// Set ptrA to point to same
// address as ptrB. 
ptrA = ptrB;
// Display current value
cout << *ptr;
// Override value
*ptr = 100;

Allocating and deallocating memory

We can allocate and deallocate memory on the heap using pointers. We could do this for one variable or an array.

VariablesArrays

Allocate memory for a single variable:

Student* ptr_vip_student = new Student;

Deallocate memory for a single variable:

delete ptr_vip_student;

Allocate memory for an array:

Student* ptr_student_list = new Student[10];

Deallocate memory for an array:

delete [] ptr_student_list;

Resizing a dynamic array

We can "resize" a dynamic array... though we're not really resizing it. Because arrays must be contiguous in memory, instead we actually just allocate more memory, copy data over, and free the old memory address, updating the pointer to the new address. Here are the steps:

1. Create a new, bigger array:
string* bigger_array = new string[ old_size + 5 ];
2. Copy information from old to new:
for ( int i = 0; i < old_size; i++ )
{
  bigger_array[i] = original_array[i];
}
3. Free old memory space:
delete [] original_array;
4. Update address:
original_array = bigger_array;
5. Update size:
old_size = old_size + 5;

Linked data structure

Why bother allocating space for a single variable? This concept is foundational to building linked data structures such as a Linked List or Binary Search Tree. Here's a simplified example...

struct Node
{
  string data;
  Node* ptr_next;
}
We create a Node struct, which contains some data, as well as a pointer to the next item in the list.
// Create first node
Node* first_node = new Node;

// Create second node
first_node->ptr_next = new Node;

// Create third node
first_node->ptr_next->ptr_next = new Node;
Each Node links to the next Node in the list.

Smart pointers

C++ also has smart pointers available, which handle the memory allocation and deallocation automatically. Usually it's better to not be manually doing memory management in your programs, so these objects would be preferred in the real world. However, you may still need to work with traditional pointers in future classes, so I tend to focus more on using those.

shared_ptr
https://cplusplus.com/reference/memory/shared_ptr/

Shared pointers are meant for spaces in memory that will be pointed to by multiple pointers. The memory allocated at that address stays active up until the last pointer is destroyed - that triggers the deallocation of the memory once nobody is pointing to it anymore.
unique_ptr
https://cplusplus.com/reference/memory/unique_ptr/

With a unique_ptr, we create a pointer that is the only "owner" of the memory block it points to. We can move around "ownership" of that address, but only one pointer may point to it. When the owning unique_ptr goes out of scope and is destroyed, it will automatically free the space allocated at the memory address it owns.

Example code and additional resources

Example code: (repository)

  1. Pointers and structs
  2. Resizing a dynamic array
  3. Smart pointers: shared_ptr
  4. Smart pointers: unique_ptr

Archived videos and class lectures: