CSC262 Jan13

slide version

single file version

Contents

  1. 1. New and Dynamic Arrays
  2. 2. Example - Read and Sort Values from a File
  3. 3. The delete Operator
  4. 4. Example
  5. 5. Memory Leak Example
  6. 6. Correcting the Memory Leak
  7. 7. Pointer Example 1
  8. 8. Pointer Example 2
  9. 9. Pointer Example 2
  10. 10. Pointer Example 3
  11. 11. Two Dimensional Arrays
  12. 12. Passing Two Dimensional Arrays
  13. 13. Two Dimensional Dynamic Arrays (1)
  14. 14. Two Dimensional Dynamic Arrays (2)
  15. 15. Example - Two Dimensional Dynamic Array
  16. 16. Assignment

1. New and Dynamic Arrays[1] [top]

The new operator also has a version that can allocate an array of elements. The address of the first of these elements is the value returned by this version of new.

Example: Create an array of 100 doubles and store the beginning address in a pointer, dp

 double *dp;

 dp = new double[100];
            
          

This is called a dynamic array, because the size doesn't have to be know before execution. For example, we could read the desired size and then create the array using new:

 double *dp;
 int sz;

 cout >> "Enter desired number of array elements: ";
 cin << sz;

 dp = new double[sz];
 ...
          

We can now use this feature of dynamic arrays to provide an excellent solution to the restriction that array sizes must be known when a program is written (compile time)! This makes programs much more robust and flexible.

2. Example - Read and Sort Values from a File[2] [top]

The point is that we want to use this program with any file of numbers. The number of values in the file can vary and so it is not known when the program is written.

How can we do this?

Problem: This causes a memory leak!

3. The delete Operator[3] [top]

If a function uses new to allocate memory, this memory is not released when the function returns!

It is the C++ programmer's responsibility to explicitly release memory when it is no longer used.

The delete operator is a unary operator that returns no value. Its operand must evaluate to the address of a memory location that was returned previously by the new operator!

Example 1: Create an integer with new,use it then release it.


 int *p = new int;
 cin >> *p;
 ... 
 delete p; 

Note: delete p doesn't delete p! It releases the memory whose address is stored in p.

Example 2: Create an array of doubles, do something with them (not shown), the relase the dynamic array.


double *q = new double[1000];

for(int i = 0; i < 1000; i++) {
   q[i] = sqrt(i);
}
...
delete [] q;

It is annoying to have to include the [] when releasing the memory for a dynamic array, but the close connection between pointers and arrays creates a subtle difference between including the square brackets, [], and not. So we are stuck with including the square brackets for 99.9999% of the the cases we want to delete a dynamic array.

4. Example[4] [top]

This example will illustrate the key steps in creating and using a dynamic array. It implements the ideas above for reading and sorting an unknown number of integer values.

Here is an initial version. (It works, but has a potential memory leak. Here it doesn't matter since the memory is released when the program terminates. However, in a larger program that continues after these steps, the memory leak would be a problem. In general, dynamic memory allocated from the heap by a program should be explicitly released when no longer used.)


int main()
{
      int sz;

      printDirections();

      cout << "How many integers to generate and sort? ";
      cin >> sz;
      int *nums = new int[sz];

      init(nums, sz);
      sort(nums, sz);
      print(nums, sz);

      return 0;
}

5. Memory Leak Example[5] [top]

If we modify the previous example to add a loop to repeat the 3 steps, we create garbage each time the nums pointer is assigned a new dynamic array.


int main()
{
    int sz;
    bool more = true;
    string ans;
    printDirections();
    
    while( more ) {
        cout << "How many integers to generate and sort? ";
        cin >> sz;
        int *nums = new int[sz];

        init(nums, sz);
        sort(nums, sz);
        print(nums, sz);

        cout << "\nAgain? [y/n] ";
        cin >> ans;
        if (ans == "n") {
            more = false;
        }
    }
    return 0;
}

6. Correcting the Memory Leak[6] [top]

To avoid the memory leak, simply delete the dynamic array in the loop after each use.


int main()
{
    int sz;
    bool more = true;
    string ans;
    printDirections();
    
    while( more ) {
        cout << "How many integers to generate and sort? ";
        cin >> sz;
        int *nums = new int[sz];

        init(nums, sz);
        sort(nums, sz);
        print(nums, sz);

        delete [] nums;

        cout << "\nAgain? [y/n] ";
        cin >> ans;
        if (ans == "n") {
            more = false;
        }
    }
    return 0;
}

7. Pointer Example 1[7] [top]

What is printed?


int main()
{
  int *p, *q;
  int x = 5;
  int y = 10;
  int n = 0;

  p = &x;
  q = &y;

  n = n + *p;
  p = q;
  n = n + *p;

  cout << n << endl;
  return 0;
}

Output: 

8. Pointer Example 2[8] [top]

Write a function to swap two integers if the parameters passed to the function are pointers to the two integers.


int main()
{
  int x = 5;
  int y = 10;

  swap(&x, &y);
  cout << "x = " << x << endl;
  cout << "y = " << y << endl;

  return 0;
}

void swap(int *p, int *q)
{
 
}

          

9. Pointer Example 2[9] [top]

What values are printed for x, y, z below?

Hint:


int main()
{
  int a[] = {10,11,12,13,14,15};
  int *p;
  int x, y, z;

  p = &a[2];
  x = p[2];
  y = *p++;
  z = *p;


  cout << "x = " << x << endl;
  cout << "y = " << y << endl;
  cout << "z = " << z << endl;
 
  return 0;
}

Output: 
 

10. Pointer Example 3[10] [top]

Write a function to print the elements in an array using pointers, but without using subscripts!

Hints:


void printArray(int *a, int n)
{
   int *p = a;
      
}

11. Two Dimensional Arrays[11] [top]

Two dimensional arrays logically consist of rows and columns. For example, an array with 3 rows and 4 columns:

        1  2  3  4
        5  6  7  8
        9 10 11 12
      

A C++ two dimensional int array with 3 rows and 4 columns can be declared like this:

        int a[3][4];

or with initialization
    
        int a[][4] = 
        {
          {1, 2, 3, 4},
          {5, 6, 7, 8},
          {9, 10, 11, 12}
        };


      

What is the scale factor for computing a + 1? That is what address is represented by a + 1?

Hint: In memory a two dimensional array is stored one row after another. So the 3 rows of a are stored as shown below. So a + 1 should be the address of the first element of the second row, namely the 5, which is 4 integers (or 16 bytes) from 1.

Each row has 4 integers and so the size of each row in bytes is 16.

        1 2 3 4 5 6 7 8 9 10 11 12
      

12. Passing Two Dimensional Arrays[12] [top]

Passing a two dimensional array to a function, the function needs to know how big each row is in order to know how to scale a + i. The number of rows can be passed as a separate parameter as in the one dimensional case, but the number of columns must be specified as a constant!!!

void printArray(int arr[][4], int rows);
      

This function can only be used with two dimensional arrays that have 4 columns (that is, whose rows have 4 elements)!!

13. Two Dimensional Dynamic Arrays (1)[13] [top]

Two see how to create a dynamic two dimensional array (i.e., specify the number of rows and columns during execution), first look again at creating a 1-dimensional dynamic array.

The typedef declaration gives a name to an existing type.

 typedef int blob;
      

This defines blob to be an alias for the type int.

Create a 1-dimensional array of blob's


typedef int blob;
int main()
{
 int n;
 blob *a;

 cout << "How many rows? ";
 cin >> n;        

 a = new blob[n];  // an array of n blob's
 ...
}

  a[ --]-------->[     ]
                 [     ]
                 [     ]
                 [     ]               
                  ....

The type of a is pointer to blob, that is pointer to int.

It points to the first int an an dynamically created array of n int's.

14. Two Dimensional Dynamic Arrays (2)[14] [top]

Now try the same example, but with a different typedef:


typedef int * blob;
int main()
{
 int n;
 blob *a;

 cout << "How many rows? ";
 cin >> n;        

 a = new blob[n];  // an array of n blob's
 ...
}

  a[ --]-------->[     ]
                 [     ]
                 [     ]
                 [     ]               
                  ....

The type of a is pointer to blob, that is pointer to int *. That is, a is a pointer to a pointer to an int.

It points to the first int an an dynamically created array of n pointers to int's.

Each of these pointers can now be assigned its own array of int's. These will be the rows of the dynamic array!

15. Example - Two Dimensional Dynamic Array[15] [top]

#include <iostream>
#include <iomanip>
#include <cstdlib>
#include <ctime>

using namespace std;


void print2DArray(int **arr, int r, int c);
int main()
{
  int r, c;

  cout << "How many rows? ";
  cin >> r;
  cout << "How many columns? ";
  cin >> c;

  int **a;

  a = new int *[r];
  for(int i = 0; i < r; i++) {
     a[i] = new int[c];
  }
  
  srand((unsigned int) time(0));
  for(int i = 0; i < r; i++) {
    for(int j = 0; j < c; j++) {
       a[i][j] = rand();
    }
  }

  print2DArray(a, r, c);

  return 0;
}

16. Assignment[16] [top]

The assignment is to write a program that creates magic squares implemented as dynamic 2-dimensional arrays. The program prompts the user for the number of rows (number of columns = number of rows). This should be an odd number. If the number of rows is n, the program should fill in the values 1, 2, 3, ... n*n so that the sum of integers in each row, column, main diagonal, and anti-diagonal are all the same. This is the condition for a magic square.

Here is the 3 x 3 example magic square:

  4  9  2
  3  5  7
  8  1  6	
      

To construct the magic square:

  1. Place 1 in the middle position of the last row.
  2. After value x has been placed in a square, place x + 1 in the square determined by:
    1. Go up (previous) 2 rows and right (next) 1 column. If the entry is not yet used, place x+1 in this position.
    2. Otherwise, go up (previous) 1 row and place x+1 there.

If any move goes out of the square, wrap around. For example if in the top row and trying to move up one row, move to the bottom row. Similarly if in the last column and trying to move right, move to the first column.