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.
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?
- Initially create a dynamic array of size, say
10.
- Keep track of the array size and how many elements
have been stored in it.
- If the array fills up
- create another one of twice the size
- copy the values from the old array into the new
array
- insert the new item and increment the element count
Problem: This causes a memory leak!
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.
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;
}
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;
}
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;
}
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)
{
}
What values are printed for x, y, z below?
Hint:
- *p++ executes as if parenthesized like this:
*(p++)
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:
Write a function to print the elements in an array using
pointers, but without using subscripts!
Hints:
- Pointers can be compared. In particular p < p +
1 is always true!
- What address is in a + n?
- The initial value of p is the address of the first element of
the array. How do you make p point to the next element of the
array? How do you know when p is pointing to a location beyond
the last element of the array? How do you print the integer p
is pointing to?
void printArray(int *a, int n)
{
int *p = a;
}
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
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)!!
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.
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!
#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;
}
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:
- Place 1 in the middle position of the last row.
- After value x has been placed in a square, place x + 1 in
the square determined by:
- Go up (previous) 2 rows and right (next) 1 column. If
the entry is not yet used, place x+1 in this
position.
- 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.