string
is not a built-in type.string
by index. This gave us some practice with indexed
collections of objects, but the type string
, remember, is
not a basic built-in type in C++. It is a type that is defined in the
string
library, as we will eventually learn to define new types in
our programs.
So, to learn about arrays in C++, we will have to return to the basic built-in
types and understand the concepts that are "beneath the hood" in the
string
type.
char
is 8
bits, an int
is 32 bits, etc. Arrays don't fit that mold of a
fixed size, at least not on the surface.
char
and int
, we need a different
way of using arrays.
For example, you create and use an array of 10 int
s as follows:
int
objects in memory.
int
s by index.
*
in front of the variable
name.
For example, to declare a variable p
that is a pointer to objects
of type int
, you'd write:
int *p;
int*
as being a new type, which is
really what it is. Indeed, many people also write the following when declaring a pointer to an integer:
int* p;
int* p, q; // not declaring two pointers!!
declares p
to be of type int*
and q
to
be of type int
.
So the int
applies to all the variables, but the *
part only applies to the next variable.
If you wanted both p
and q
to be "pointers to ints",
you'd have to write
int *p, *q; // now you are declaring two pointers!!
This is unfortunate; it's just because the syntax of C++ is inherited from that of C. For this reason, when it comes to pointers, some people always declare a single pointer variable.
int* p; // more keyboard strokes, but at least there is no confusion.
int* q;
new
operatorp
as a pointer to objects of type
int
, we have to actually allocate some int
s
in memory for it to point to! Just declaring p
leaves it
uninitialized - i.e. pointing to nothing, or what's worse, pointing to some
random area of memory.
new
operator using the following sytax:
new type[number]
This expression allocates number consecutive objects of type type in memory, and returns a pointer to the first object.
For example, to allocate 10 int
s in memory and set p
to point to the first of them, we'd write:
int* p; // delcare a pointer p
p = new int[10]; // p points to a newly allocated array of 10 ints
int* p; // delcare a pointer p
p = new int[10]; // p points to a newly allocated array of 10 ints
At this point, we can access any of these int
objects by the
indices 0,1,2,3,4,5,6,7,8,9 just as we did for the characters within a
string.
For example:
int
in the array, we write p[0]
.
Note: Since p[0]
is an int
, we can
do anything with it we can do with a normal int
.
int
in the array, we write p[9]
.
In summary:
new
operator are what we
use for arrays. In particular, an array is really a contiguous block of
objects in memory, and the pointer variable allows us to access the objects
in this block by index.
The following picture depicts what's going on when we create an array of 4
int
s and assign a value to one of the int
s in the
array.
DECLARE VARIABLES |
Notes on scope: The pointer is a variable with name and
scope like we're used to. That is, the pointer | |
ALLOCATE SPACE | Notes on scope:
However, the array that pointer points to is in a different area of the program's memory called, the heap. The array itself is not part of any function call record on the stack. That's what this picture is showing you. | |
ASSIGN A VALUE |
doubles
from the user
We already saw from our exercises with accessing characters within a
string
that indexed collections of objects would allow us to print
objects in reverse order.
int main()
{
// Get number of doubles from user
int n;
cout << "How many doubles? ";
cin >> n;
// Construct array
double *A;
A = new double[n];
// Read numbers
cout << "Enter " << n << " doubles: ";
for(int i = 0; i < n; i++)
cin >> A[i];
// Write in reverse order
cout << "In reverse your numbers are: ";
for(int j = n - 1; j >= 0; j--)
cout << A[j] << " ";
cout << endl;
return 0;
}
Even an example as simple as this deserves a fair bit of commentary the first
time around. In class we'll go over it in painful detail, including pictures.
If you keep this in mind, some things that might otherwise trip you up make perfect sense. For example:
What's hopefully clear here is that by creating a new pointer q
and setting q = p
, we did not
create a new array populated with the same integer values. We just created a
new pointer and set it to point to the same old array.
int* pa = new int[3];
int* pb = new int[3];
pa[0] = 1; pa[1] = 2; pa[2] = 3;
pb[0] = 6; pb[1] = 7; pb[2] = 8;
int* q;
q = pb;
q[1]++;
cout << pa[1] << " " << pb[1] << " " << q[1] << endl;
q = pa;
q[1]++;
cout << pa[1] << " " << pb[1] << " " << q[1] << endl;
Answer (drag your mouse):
2 8 8
3 8 3
new
have no scope
and live until you kill themnew
is
that they have no scope.
So, for example, if you wrote a function that called new
to
allocate some space, the objects in memory created by new
would
not be destroyed when the function returned, even though the pointer we
had goes out of scope.
Take this example:
Before the function call | |
During the function call | |
After the function call |
delete []
new
either stick around until the end of the
program, or are explicitly destroyed with the delete
operator.
For example:
char* p;
p = new char[30]; // creates 30 consecutive char's in memory
delete [] p; // destroys the objects created by new
We'll worry more about delete
later.
bool* B = new bool[10];
The type of B
is bool*
, and the type of
B[0]
is bool
.
If we have the declaration
string* A = new string[20];
The type of A
is string*
, and the type of
A[0]
is string
.
A[5]
is an lvalue, i.e. it is allowed on
the left-hand side of an assignment. This is tremendously important.
This means that A[5]
is not a copy of the index-5 element of the
array, it is the actual index-5 element. You can modify it.
For example:
string* A = new string[20];
A[5] = "hello";
int* C = new int[10]; // C has type int*
cin >> C[2]; // read an integer from the user to C[2]
C[2]++; // increment C[2]
You might wonder how an expression C[2]++
should be
evaluated. If you look at the operator precedence table (under
resources) you'll see that [.] has very high precedence, so
C[2]++
evaluates as (C[2])++
.
Consider the following code:
|
Give the type of each expression below (drag your mouse to check answers):
|
Enter dimension: 4 Enter two vectors: [3,-1,0,2] [5,0,9,-7] Dot product = 1Try it for yourself and then take a look at my solution.
doubles
?
p
and q
. When
q=p;
is executed, will the whole array be copied?
Answer: No
int* p = new int[n];