Lecture 2

Arrays

In C++, the size of an array must be a constant value. Arrays will be stored in the stack memory along with local variables, and parameters passed to functions.

#include <iostream>
#include <cmath>
uding namespace std;

#define SIZE 100
  
int main()
{
 // a double array of size 100
  double sample[100];
  for (int i = 0; i < 100; i++)
  {
    sample[i] = sqrt(i);
  }
  // Display array
  for (int i = 0; i < 100; i++)
  {
    cout << "value[" << i << "] root [" << sample[i] << "]" << endl;
  }
  
  return 0;
}

The following is not valid as length is a variable, not a constant.

int length = 100;
float sample[length];

Pointers

A pointer is a variable, which is used to hold the memory address of another variable.

Declaration of a pointer
int* p;

Declares a pointer p, of type integer, but it is not pointed to any specific integer.

This declares an uninitialised pointer, which is not recommended.

The NULL pointer

It is good practice to assign the pointer NULL to a pointer variable in case an exact address to be assigned is not known.

int* p = NULL;
int* p = 0; // 0 is identical to NULL for pointers
Int* p = nullptr;

Always initialise an unused pointer as a NULL pointer

Two special operators: * and &
int m = 23;
int* p = &m;

Declare an integer pointer p, and point it to an integer m. So that p holds the memory address of m.

int n = *p;

This statement reads the value at memory address pointed to by p, which is m (as above = 23)

*p = -62;

This statement assigns a value -62 to memory address pointed to by p. This is equivalent to the statement m = -62;

Pointer arithmetic
p++; //1
++p; //2
p--; //3
--p; //4
p = p + 5; //5
p = p - 8; //6
  1. p is post incremented to the next integer address.
  2. p is pre-incremented to the next integer address.
  3. p is post decremented to the previous integer address.
  4. p is pre-decremented to the previous integer address.
  5. p is moved forward 5 integer addresses.
  6. p is moved backwards 8 integer addresses.
Dangling Pointers
if(true){
  int x = 50;
  p = &x;
}
*p = 100;

x's location is only valid within the scope of the if statement. It may still work, but the memory address may be used for another value at any time by the application or the operating system. It is undefined behaviour.

Pointer-Array Equivalence

In C++, arrays and pointers are closely related - the name of an array is a pointer to the 1st element of that array. Therefore, is is possible to have two methods of accessing array elements:

  1. Array indexing
  2. Pointer arithmetic
// a double array
double sample[100];
for (int i = 0; i < 100; i++)
  sample[i] = sqrt(i);

// display the array using array indexing
for (int i = 0; i < 100; i++)
  cout << sample[i] << endl;

//display the array using pointer arithmetic
for (int i = 0; i < 100; i++)
  cout << *(sample + i) << endl;

*(sample) is a pointer to the start of the array, then + i moves the pointer i places along the array each time the for loop executes.

References

In a computer language, there are two ways that arguments can be passed to a function:

  • Call by value - copies the value of an argument into the parameter of the function; changes made to the parametes have no effect on the argument
  • Call by reference - copies the address of the argument into the parameter, which is used inside the cuntion to access the argument; changes made to the parameter thus affect the argument.

By default, C++ and Java uses call by value to pass arguments. This means that the code within a function cannot alter the argments used to call the function. C++ contains a feature that is related to the pointer called a reference. The most important use is to allow for the creation of functions that automatically use call-by-reference parameter passing.

Dynamic Allocation in C++

Creates memory spaces in the Heap memory. Heap memory must be manually freed. There are two operators for managing heap memory, new and delete

  • new allocates memory and returns a pointer to the start of it
  • delete frees memory previously allocated using new for reuse
  • Two two operators must match to avoid a memory leak, or undefined behaviour

Allocating a single item with optional initialisation:

type_name* p = new type_name(initialiser);
delete p;

Allocating arrays:

type_name* p = new type_name[size];
delete[] p;

Allocated arrays can be accessed using array indexing. p[0], p[1] etc because of the pointer-array equivallence. No array allocated by new can have an initialiser.

Each new must have a matching delete. If there are more new calls than delete calls, there will be a memory leak. More delete calls than new calls, the program can start to give undefined behaviour.

Vector

It is not straightforward to do the following with an array, even if it is dynamically allocated (using new):

  • Increase or decrease the length
  • Insert an element at a specified position
  • Remove an element at a specified position

The C++ template class vector supports a dynamic array, that solves all these problems by allocating memory as needed. Although a vector is dynamic, you can still use the standard array subscript notation to access its elements. The word template means vector is generic or a framework, leaving the details to be filled in by the compiler.

The C++ vector combines the best features of arrays in C++ and Java.

Vector vs Pointer

The vector class eases the handling of dynamic arrays at the price of performance.