Last modified: Tue, 02 Nov 2010 22:34:04 +0200
Complete: ###################- (95%)
1.1 Introduction
1.2 Variables and Memory Addresses
1.3 References
1.4 Pointers
1.5 Pointer Arithmetic
1.6 Pointers and Arrays
1.7 Use of Pointers in Functions
1.8 The NULL Pointer
1.9 Examples
1.10 Exercises
1.1 Introduction
The most powerful tools of the C++ programming language are pointers and references that allows the programmer to
manipulate computer memory directly. In this section, at the introductory level, we will learn what references and
pointers are and how to declare and use them.
1.2 Variables and Memory Addresses
Computer memory can be considered as a very large array of bytes.
For example, a computer with 1 GB of RAM (Random Access Memory) actually contains
an array of 1024 x 1024 x 1024 = 1,073,741,824 bytes.
As an array, these bytes are indexed from 0 to 1,073,741,824. The index of each byte is known as its memory address.
So a 1 GB computer has memory addresses ranging from 0 to 1,073,741,823,
which is 0x00000000 to 0x3fffffff in hexadecimal.
Fig 10.1 represents that array of bytes, each with its hexadecimal address.
Fig 10.1: Array of bytes and addresses in hexadecimal
When a variable is declared and assigned a value, four fundamental attributes are associated with it:
- its name
- its type
- its value (content)
- its address
For example, the declaration
associates the name n, the type int, and the address of some location in
memory where the value of n is stored. Suppose that address is 0x0024fdf0. Then we can visualize the variable n like this.
On most computers, a variable of type int is 4-byte long in memory.
So the variable n shown above would occupy the 4-byte block of memory represented by the shaded rectangles
in the Fig 10.2. Note that, the address of this object, which is called the region of storage, is the address of the
first byte in the block of memory, i.e. 0x0024fdf0.
Fig 10.2: Representation of an integer variable in the memory
In C++, the value of a variable is accessed via its name. The address of a variable is accessed via the address operator &.
The expression &n evaluates to the address of the variable n. The use of this operator is illustrated in the following program.
01prg01.cpp: The use of address operator
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
|
// The use of the address operator
#include <iostream>
using namespace std;
int main(){
int n = 25;
cout << "n = " << n << endl;
cout << "&n = " << &n << endl;
return 0;
} |
n = 25
&n = 0x0024fdf0
|
1.3 References
A reference is an alias. When you generate a reference, you initialize it with the name of another object, the target.
Hence, the reference acts as an alternative name for the target. That is, anything you do to the reference is really done to the target.
A reference is declared by using a reference operator &. A basic use of the reference is shown in the following program.
01prg02.cpp: A basic use of the reference
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
|
// A basic use of the reference
#include <iostream>
using namespace std;
int main(){
int n = 25; // the target
int &r = n; // the alias of the target
cout << "1. n and r = " << n << '\t' << r << endl;
n = 40;
cout << "2. n and r = " << n << '\t' << r << endl;
r = 12;
cout << "3. n and r = " << n << '\t' << r << endl;
return 0;
} |
1. n and r = 25 25
2. n and r = 40 40
3. n and r = 12 12
|
Note that
- The reference operator (&) is the same symbol as used for the address operator.
These are not the same operators, however, they are related.
- Like constants, when a reference is declared it must initialized.
|
A reference is not a variable. A variable is an object; i.e. the region of storage.
Different objects must occupy disjoint blocks of memory. However, the address of a reference and corresponding variable is the same.
This is illustrated the program given below.
01prg03.cpp: The memory address of a variable and its alias
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
|
// The memory address of a variable and its alias
#include <iostream>
using namespace std;
int main(){
int n = 25; // the target
int& r = n; // the alias of the target
cout << "n and r = " << n << '\t' << r << endl;
n = 40;
cout << "&n and &r = " << &n << '\t' << &r << endl;
return 0;
} |
n and r = 25 25
&n and &r = 0x0024fdf0 0x0024fdf0
|
More than one reference can be associated with the same variable. For example, in the following code,
r1, r2 and r3 all represent val1.
int val;
int &r1 = val; // r1 is an alias for val
int &r2 = val; // r2 is another alias for val
int &r3 = r1; // r3 is another alias for val
References are used mostly for reference parameters in a function
(see Section 6.1).
1.4 Pointers
Every variable has an address. We can also store the address of a variable in another variable, called pointer.
Basically, a pointer is a variable that holds a memory address.
The pointer is a numerical variable. So, like other variables, before using a pointer it has to be
declared by using indirection operator (*).
The general form of the declaration is
For example
char *ch; // a single character pointer
int *mass; // an integer pointer
float *xPos; // a single-precision real pointer
One can use the address operator (&) to assign the memory address of a variable to a pointer.
Note that, since a pointer is a variable itself, it is stored in another memory location.
A simple usage of the pointer is demonstrated in the following program.
01prg04.cpp: Use of a pointer variable
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
|
// Use of a pointer variable
#include <iostream>
using namespace std;
int main(){
int n = 25;
cout << "n = " << n << '\n' << "&n = " << &n << endl;
int *pn = &n; // pn holds the address of n
cout << "pn = " << pn << '\n' << "&pn = " << &pn << endl;
return 0;
} |
n = 25
&n = 0x0022ff74
pn = 0x0022ff74
&pn = 0x0022ff70
|
Here, in line 9, the variable pn is called a 'pointer', because its value 'points' to the location of another value.
Briefly, we say that pn points to n.
Note that, the 'value' of pn is an address and it is stored in another memory location (since pn is a variable itself).
The assignments in this program can be visualized as follows:
The indirection operator (*) is also called as dereference operator.
If pn points to n, we can obtain the value of n directly from pn
using the dereference operator *. The expression *pn evaluates to the value of n.
This evaluation is called dereferencing the pointer pn.
01prg05.cpp: Use of dereferencing
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
|
// Use of dereferencing
#include <iostream>
using namespace std;
int main(){
int n = 25, *pn;
cout << "n = " << n << endl;
pn = &n; // pn holds the address of n
cout << "*pn = " << *pn << endl << endl;
*pn = 50;
cout << "n = " << n << endl;
cout << "*pn = " << *pn << endl;
return 0;
} |
n = 25
*pn = 25
n = 50
*pn = 50
|
A pointer can point to another pointer (known as Pointers to Pointers).
Let pn point to n. another pointer ppn can point to pn.
In this case, *pn is an alias for n and similarly *ppn is an alias for pn.
Therefore we can say that **ppn is also an alias for n. In C++, these operations can be implemented as follows.
int n = 12;
int *pn = &n; // pn holds the address of n
int **ppn = &pn; // ppn holds the address of pn
*pn = 25; // now n = 25
**ppn = 50; // now n = 50
Pointers are used, most often, for three tasks:
- Managing data in memory
- Accessing functions and class member data
- Passing variables by reference to functions
1.5 Pointer Arithmetic
When using pointers, sometimes it is required to obtain the previous or next memory locations of the address holding by the pointer.
This entails the use of arithmetic operators that work on pointers.
These operators are +, -, ++ and --.
Consider the following pointers that hold the address of a char, an int and a double respectively.
char *kar;
int *tam;
double *ger;
Let the addresses held by them be 10000 (0x2710), 20000 (0x4e20) and 30000 (0x7530) respectively.
Accordingly, the assignments
will point the values stored in the memory addresses 10001 (0x2711), 20004 (0x4e24) and 30008 (0x7538);
i.e the size in bytes of the type pointed is added to the pointer.
Therefore, when adding one to a pointer we make it to point to the following element of the same type with which it has been defined.
In general, to add (or subtract) the number n to a pointer p is to evaluate the address of the next
(or previous) nth element of the given type pointed by the p. Examine the following assignments.
kar++; // equivalent to kar = kar + sizeof(char)
tam = tam + 5; // equivalent to tam = tam + 5*sizeof(int)
ger = ger - 3; // equivalent to ger = ger - 3*sizeof(double)
1.6 Pointers and Arrays
The concept of an array is very much bound to the pointer.
In fact, the name of an array is a pointer and it stores the address of its first element.
Because of this, any one can reach any element of an array via a pointer. Consider the following declaration.
The following assignment operations are valid and equivalent
p = mass; // assign first element of mass to p
p = &mass[0]; // assign first element of mass to p
Since the name of the array is a pointer, it can be assigned to a pointer of same type.
In addition, according to above declaration and assignment for an int variable i,
the following assignments are equivalent.
Note that
*p + i // adds i to the first element
*(p+i) // evaluates the address i'th block after p
|
01prg06.cpp: Pointers and arrays
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
|
// Pointers and arrays
#include <iostream>
using namespace std;
int main(){
float a[5];
float *p;
p = a; // p holds the adr. of the 1st element of a
*p = 1.5; // that means a[0] = 1.5;
p = &a[1]; // now p holds the adr. of the 2nd element
*p = 2.2; // that means a[1] = 2.2;
p = a + 2; // now p holds the adr. of the 3rd element
*p = 7.1; // that means a[2] = 7.1;
p = a; // now p holds the adr. of the 1st element of a
*(p+3) = 8.3; // that means a[3] = 8.3;
*(p+4) = 9.9; // that means a[4] = 9.9;
cout << " a[i]: ";
for (int i=0; i<5; i++) cout << a[i] << " ";
cout << endl;
cout << "*(p+i): ";
for (int i=0; i<5; i++) cout << *(p+i) << " ";
cout << endl;
return 0;
} |
a[i]: 1.5 2.2 7.1 8.3 9.9
*(p+i): 1.5 2.2 7.1 8.3 9.9
|
1.7 Use of Pointers in Functions
We have seen the concept of arguments passed by reference to the functions in
Section 6.1.
We have used this idea to change external variables passed to the function inside a function.
We can also use the pointers to manipulate the external variables inside the function. In this case,
the address of the external variables (using the address operator &) must be supplied to the function.
Examine the following swap() function.
01prg07.cpp: Swapping two values via pointers
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
|
// Swapping two values via pointers
#include <iostream>
using namespace std;
void swap(int *, int *);
int main()
{
int x = 22, y = 33;
cout << "Values before swapping: "
<< x << " " << y << endl;
swap (&x, &y);
cout << "Values after swapping: "
<< x << " " << y << endl;
return 0;
}
void swap(int* x, int* y){
int z = *x;
*x = *y;
*y = z;
} |
Values before swapping: 22 33
Values after swapping: 33 22
|
One can use the relation between the pointers and arrays in function parameters. This is
illustrated in the following program.
01prg08.cpp: Random array function
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
|
// Assigns integer random number [0,100] to an array of given size
#include <iostream>
#include <cstdlib>
void randomArray(int *array, int size){
for(int i=0; i<size; i++)
*(array + i) = rand() % 100;
}
int main(){
int x[5];
randomArray(x, 5); // call the function
for(int k=0; k<5; k++)
std::cout << x[k] << std::endl;
return 0;
} |
83
86
77
15
93
|
A function may return an address. In the following program, the function maxAdr() returns the
address of the maximum element of an array.
01prg09.cpp: Getting the address of the maximum element of an array
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
|
// Getting the adress of the maximum element of an array
#include <iostream>
#include <cmath>
using namespace std;
// Returns the address of the maximum element
double * maxAdr(double *array, int size){
double mval = array[0];
double *madr = &array[0];
for(int i=1; i<size; i++){
if(array[i] > mval) {
mval = array[i];
madr = &array[i];
}
}
return madr;
}
int main(){
double a[5] = {1.0, -2.0, 4.0, 8.0, -16.0};
double *b;
b = maxAdr(a, 5);
cout << "the address of the maximum is " << b << endl;
cout << "the value of the maximum is " << *b << endl;
return 0;
} |
the address of the maximum is 0x22ff58
the value of the maximum is 8
|
1.8 The NULL Pointer
A pointer whose value is zero is called a null pointer. A null pointer points to nothing.
This can be used for canceling (omitting) the address of the location where it is pointed by the pointer.
To do that a zero or the constant NULL can be used.
int *p, a = 12, b;
p = &a; // p points to a
p = NULL; // (equivalent to "p = 0") p points nothing (NULL pointer)
*p = 8 // ERROR: you cannot assign a value since p is NULL pointer
1.9 Examples
The function strCopy() in the following program makes a copy of a string.
01prg10.cpp: Making a copy of a string of type char
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
|
// Making a copy of a string of type char
#include <iostream>
using namespace std;
void srtCopy(char *, char *);
int main(){
char x[10] = "Hello", y[10];
srtCopy(x, y);
cout << "x = " << x << endl;
cout << "y = " << y << endl;
return 0;
}
// Makes a copy of str1
void srtCopy(char *str1, char *str2){
while(*str1 != '\0')
*(str2++) = *(str1++);
*(str2++) = '\0';
} |
x = Hello
y = Hello
|
1.10 Exercises
- What operator is used to determine the address of a variable?
- What is a reference?
- What is a pointer?
- What is the difference between a reference and a pointer?
- What is the difference between the reference operator and address operator?
- What is the difference between the indirection operator and the dereference operator?
- What is wrong with the following code:
int &r = 35;
- What is wrong with the following code:
int* p = &45;
- If x has the address 0x0fffd1c, then what will values of p and q be for each of the following:
double x = 1.01;
double * p = &x;
double * q = p + 5;
- What is the output of the following program?
#include <iostream>
using namespace std;
int main(){
double x = 1.7;
double &rx = x;
double *px, **ppx;
px = &x; ppx = &px; **ppx = 0.5;
cout << x << '\t' << rx << endl;
*px = 5.0*rx - 1.0;
cout << rx << '\t' << **ppx << endl;
return 0;
}
- What is the output of the following program?
#include <iostream>
using namespace std;
int main(){
float a[5] = {1.1, 2.2, 3.3, 4.4, 5.5};
float *p = a;
for (int i=4; i>-1; i--)
cout << p[i] << " ";
return 0;
}
- What is the output of the following program?
#include <iostream>
using namespace std;
void fizik(int *, int);
int main(){
int a[] = {2, 4, -2, 5, 9};
int n = sizeof(a)/sizeof(int);
fizik(a, n);
for(int i=0; i<n; i++) cout << a[i] << endl;
return 0;
}
void fizik(int *array, int size){
for(int j=0; j<size; ++j)
*(array + j) *= *(array + j);
}
- What is the output of the following program?
#include <iostream>
using namespace std;
int * location(int a[],int n, int target){
for (int i = 0; i < n; i++)
if (a[i] == target) return &a[i];
return NULL;
}
int main(){
int a[8] = {22,33, 44,55,66,77,88,99}, *p, n = 44;
p = location(a,8,n);
if(p==NULL) cout << n << " was not found.\n";
else cout << p << " , " << *p << endl;
return 0;
}
|