1. Arrays, Pointers, and Structures

Objective: Discuss these three C++ features: arrays, pointers, and structures. We'll show

Reference: Weiss, Chapter One.

1.1 Pointers

1.2 Operations on Pointers

1.3 Reference Variables

1.4 Arrays and Pointers

An array's name represents a pointer to element zero of that array. Hence for the definitions:

   int a[10];
   int *iptr;

The name a is the same as &a[0]. The assignment

   iptr = a;  // same as iptr = &a[0];

is well-defined. In this case, a[i], *(a+i), iptr[i], and *(iptr+i) are all equivalent. In fact, a[i] is changed to *(a+i) by the compiler immediately.

Even though iptr and a can be used interchangeably in the above case, there are significant differences between them:

1.5 Arrays as Function Parameters

If an array is a parameter in a function prototype, its name can only be a passed by value. Since the name of an array is the address of its first element, when an array is passed as an argument in a function call, the address of its first element is copied. Hence the entries of the array are passed by reference.

Example: The following are equivalent implementation of the same function:

   int find_max (int a[], int n)
   // find index of maximum element/
   // in array a of size n./
   {
      int max = a[0], index = 0, i;

      for (i = 1; i < n; i++)
      {
         if (max < a[i])
         {
            max = a[i];
            index = i;
         }
      }
      return index;
   }

   int find_max (int *a, int n)
   // find index of maximum element
   // in array a of size n.
   {
      int max = *a, index = 0, i;

      for (i = 1; i < n; i++)
      {
         if (max < *(a+i))
         {
            max = *(a+i);
            index = i;
         }
      }
      return (index);
   }

1.6 Multidimensional Arrays

1.7 Dynamic Variables

Since a pointer can be used to refer to a variable, a program can manipulate variables even if the variables have no identifiers to name them. This is a very important feature of C++ because in many applications, the actual number of variables cannot be determined until the execution time. Some variables should be created as needed while the program is being run. These variables are called dynamic variables. Nameless dynamic variables can be created using the new operator.

Example:

   int *p;
   p = new int;
   cin >> *p;
   *p += 7;
   cout << *p;

The operation new int above creates a nameless variable of type int and returns a pointer to it. Thus p is a pointer to this newly created variable. As a result, the variable can be refered to as *p. If new fails to create a new variable of the required type, it invokes a handling routine. The default action is to exit the program.

Example: What is printed?

// Program to demonstrate dynamic varaibles.
#include <iostream>
using namespace std;

int main()
{
   int *p, *q;
   p = new int;
   *p = 42;
   q = p;
   cout << "*p == " << *p << endl;
   cout << "*q == " << *q << endl;
   *q = 53;
   cout << "*p == " << *p << endl;
   cout << "*q == " << *q << endl;
   p = new int;
   *p = 88;
   cout << "*p == " << *p << endl;
   cout << "*q == " << *q << endl;
   cout << "Hope you got the point!.\n";
   return 0;
}

1.8 Dynamic Arrays

C++ demands that the sizes of all regular arrays must be known at the compilation time. However, one may not know the size of the array when the program is written. The size is only determined in run time. With the regular arrays, one has to estimate the largest possible size one may need. There are two problems with this:

Dynamic arrays can solve this problem very nicely.

Example:

// Program to demonstrate dynamic array.
char *dstrcpy (const char *cs)
{
   char *s, *tmp;
   unsigned size = strlen(cs) + 1;

   tmp = s = new char[size];

   while (*cs != '\0')
   {
      *(tmp++) = *(cs++);
   }

   *tmp = '\0';
   return s;
}

To destroy a dynamic array, p say, one can use

   delete [] p;

The square bracket tell C++ that a dynamic array variable is being eliminated, so the system checks the size of the array and removes that many cells.

Example:

// Program that sorts a dynamic array.
#include <cstdlib>
#include <iostream>
using namespace std;

typedef int* IntPtr;
void fill_array(int a[], int size);
void sort(int a[], int size);

int main()
{
   int array_size, i;
   IntPtr a;
   cout << "How many numbers will be sorted? ";
   cin >> array_size;

   a = new int[array_size];

   fill_array(a, array_size);
   sort(a, array_size);
   cout << "In sorted order the numbers are:\n";

   for (int i = 0; i < array_size; i++)
   {
      cout << a[i] << ' ';
   }
   cout << endl;

   delete [] a;
   return 0;
}

void fill_array(int a[], int size)
{
   cout << "Enter " << size << " integers.\n";
   for (int i = 0; i < size; i++)
   {
      cin >> a[i];
   }
}

void sort(int a[], int size)
{
  ...
}

1.9 Dynamic Two-Dimensional Arrays

Syntax of dynamic allocation does not allow direct two-dimensional notations. Thus, code such as

double **arr = new double[m][n];  // syntax error

are not possible. However, the function dyn2d returns a double** that can be used as a two-dimensional array:

double **dyn2d(int m, int n)
{
   double *arr = new double[m*n];
   double **a = new double*[m];

   for (int i = 0; i < m; i++)
   {
      a[i] = arr + i*n;
   }

   return a;
}

void del2d(double **a)
{
   delete [] *a;  // free data cells
   delete [] a;   // free pointer cells
}

Example: Allocating and deallocating two-dimensional arrays can be done alternatively as follows:

double **dyn2d_1(int m, int n)
{
   double **a = new double*[m]; // pointer cells

   for (int i = 0; i < m; i++)
   {
      a[i] = new double[n];     // data cells
   }

   return a;
}

void del2d_1(double **a, int m)
{
   for (int i = 0; i < m; i++)
   {
      delete [] a[i];  // free data cells
   }

   delete [] a;        // free pointer cells
}

1.10 The vector Class Template

1.11 The string Class

1.12 Structures

1.13 Exogenous Versus Indigenous Data

1.14 The Classic Linked List

  +------+---+    +-------+---+    +----+---+    +-----+---+
  | Dale | o-+--->| Katie | o-+--->| Ed | o-+--->| Rob | ^ |
  +------+---+    +-------+---+    +----+---+    +-----+---+

Assume the following structure of a node:

   +---------------------+---+
   | item (of type data) | o-+------>
   +---------------------+---+  next (of type link)

A node is represented by a link to it, and a linked list is represented by a link to its first node, the head node. The symbol ^ is used to denote the empty link, one that does not link to any node.

1.14.1 Basic operations on linked lists

1.15 Linked Storage of Nodes

struct Node {
   Object item;
   Node *next;
};

Node *getNode (void)
{
   return new Node;
}

void freeNode (Node *p)
{
   delete p;
}

Object getItem (Node *p)
{
   return (p -> item);
}

Node *getNext (Node *p)
{
   return (p -> next);
}

void setItem (Node *p, const Object& x)
{
   p -> item = x;
}

void setNext (Node *p, const Node *q)
{
   p -> next = q;
}