Chapter 07 Pointers
Chapter 07 Pointers
[CoEng1112]
Chapter Seven:
Pointers
Introduction
• A pointer is a variable that holds a memory
address, usually the location of another
variable in a memory.
7.1 Declaring variables of type pointer
• Anything (data or instruction) that needs to be
processed must be loaded into internal memory
before its processing takes place. Therefore,
every data and instruction that is being executed
must be allocated some area in the main
(internal) memory.
• Variables are stored in concrete places of the
computer memory.
• The computer memory is only a succession of 1
byte cells (the minimum size for a datum), each
one with a unique address.
Cont’d
Address (dereference) operator (&)
• It is used as a variable prefix to know where
the variable is stored and can be translated as
"address of", thus: &variable1 can be read as
"address of variable1"
• For example:
ted = &andy;
would assign to variable ted the address of
variable andy
Cont’d
• Suppose that andy has been placed in the memory
address 1776 and that we write the following:
andy = 25;
fred = andy;
ted = &andy;
the result is shown in the following diagram:
Cont’d
• A variable storing a memory address is called
a pointer (like ted in the previous example) as
it points to a specific memory location whose
address it is storing under its name.
Cont’d
Reference operator (*)
• To access the value stored in the variable pointed by
pointer just by preceding the pointer identifier with the
reference operator asterisk (*), that can be literally
translated to "value pointed by".
* mypointer can be read as "value pointed by
mypointer".
• Therefore, following with the values of the previous
example, if we write:
beth = *ted;
read as: "beth equal to value pointed by ted“
beth would take the value 25, since ted is 1776, and the
value pointed by 1776 is 25.
Cont’d
int main ()
{
int value1 = 5, value2 = 15;
int * mypointer;
mypointer = &value1;
*mypointer = 10;
mypointer = &value2;
*mypointer = 20;
cout << "value1==" << value1 << "/
value2==" << value2;
return 0;
}
ex2
Cont’d
// my first pointer value1==10 / value2==20
#include <iostream.h>
int main ()
{
int value1 = 5, value2 = 15;
int * mypointer;
mypointer = &value1;
*mypointer = 10;
mypointer = &value2;
*mypointer = 20;
cout << "value1==" << value1 << "/
value2==" << value2;
return 0;
}
ex2
Cont’d
• Here is an example a bit more complicated:
// more pointers value1==10 /
value2==20
#include <iostream.h>
int main ()
{
int value1 = 5, value2 = 15;
int *p1, *p2;
cout << "value1==" << value1 << "/ value2==" << value2;
return 0;
}
ex3
7.2 Pointers and Arrays
• C++ interprets the name of an array as a
pointer pointing to the first element of the
array. For example, supposing these two
declarations:
int numbers [20];
int * p;
the following allocation would be valid:
p = numbers;
Cont’d
• The only difference is that we could assign another value to
the pointer p whereas numbers will always point to the first of
the 20 integer numbers of type int with which it was defined.
So, unlike p, that is an ordinary variable pointer, numbers is a
constant pointer (indeed an array name is a constant pointer).
Therefore, although the previous expression was valid, the
following allocation is not:
numbers = p;
because numbers is an array (constant pointer), and no values
can be assigned to constant identifiers.
Cont’d
• Due to the character of variables all the expressions that
include pointers in the following example are perfectly valid:
// more pointers 10, 20, 30, 40, 50,
#include <iostream.h>
int main ()
{
int numbers[5];
int * p;
p = numbers; *p = 10;
p++; *p = 20;
p = &numbers[2]; *p = 30;
p = numbers + 3; *p = 40;
p = numbers; *(p+4) = 50;
for (int n=0; n<5; n++)
cout << numbers[n] << ", ";
return 0;
}
ex4
Cont’d
• Since name of an array (array name) is a pointer
to its first element, the array name +1 gives the
address of the second element of the array, array
name +2 gives the address of the third element
and so forth. For example, both following
expressions:
a[5] = 0; // a [offset of 5] = 0
*(a+5) = 0; // pointed by (a+5) = 0
are equivalent and valid either if a is a pointer or if
it is an array.
7.3 Pointer initialization
• When declaring pointers we may want to
explicitly specify to which variable we want
them to point,
int number;
int *tommy = &number;
this is equivalent to:
int number;
int *tommy;
tommy = &number;
Cont’d
• You must consider that at the moment of
declaring a pointer, the asterisk (*) indicates only
that it is a pointer, it in no case indicates the
reference operator (*). Remember, they are two
different operators, although they are written
with the same sign. Thus, we must take care not
to confuse the previous with:
int number;
int *tommy;
*tommy = &number;
that anyway would not have much sense in this
case.
Cont’d
• As in the case of arrays, the compiler allows the
special case that we want to initialize the content
at which the pointer points with constants at the
same moment as declaring the variable pointer:
char * terry = "hello";
in this case static storage is reserved for containing
"hello" and a pointer to the first char of this
memory block (that corresponds to 'h') is assigned
to terry.
Cont’d
• If we imagine that "hello" is stored at addresses
1702 and following, the previous declaration
could be outlined thus:
int main ()
{
char a = 5;
short b = 9;
long c = 12;
increase (&a,sizeof(a));
increase (&b,sizeof(b));
increase (&c,sizeof(c));
cout << (int) a << ", " << b << ", " << c;
return 0;
}
ex5
Cont’d
• sizeof is an operator integrated in the C++
language that returns a constant value with
the size in bytes of its parameter, so, for
example, sizeof(char) is 1, because char type is
1 byte long.
7.6 Pointers to functions
• C++ allows operations with pointers to functions.
The greatest use of this is for passing a function
as a parameter to another function, since these
cannot be passed dereferenced. In order to
declare a pointer to a function we must declare it
like the prototype of the function except the
name of the function is enclosed between
parenthesis () and a pointer asterisk (*) is
inserted before the name. It might not be a very
handsome syntax, but that is how it is done in
C++:
Cont’d
// pointer to functions 8
#include <iostream.h>
int main ()
{
int m,n;
m = operation (7, 5, addition);
n = operation (20, m, minus);
cout <<n;
return 0;
ex6 }
Cont’d
• In the example, minus is a global pointer to a
function that has two parameters of type int,
it is immediately assigned to point to the
function subtraction, all in a single line:
• int (* minus)(int,int) = subtraction;
7.7 Dynamic memory
• Until now, in our programs, we have only had as much
memory as we have requested in declarations of
variables, arrays and other objects that we included,
having the size of all of them fixed before the execution
of the program. But, what if we need a variable
amount of memory that can only be determined during
the program execution (runtime), for example, in case
that we need an user input to determine the necessary
amount of space?
• The answer is dynamic memory, for which C++
integrates the operators new and delete.
Cont’d
Operators new and new[ ]
• In order to request dynamic memory, the operator new
exists. new is followed by a data type and optionally
the number of elements required within brackets []. It
returns a pointer to the beginning of the new block of
assigned memory. Its form is:
pointer = new type
or
pointer = new type [elements]
• The first expression is used to assign memory to
contain one single element of type. The second one is
used to assign a block (an array) of elements of type.
Cont’d
• For example:
int * bobby;
bobby = new int [5];
in this case, the operating system has assigned space for 5
elements of type int in a heap and it has returned a pointer to its
beginning that has been assigned to bobby. Therefore, now,
bobby points to a valid block of memory with space for 5 int
elements.
Cont’d
• You could ask what is the difference between
declaring a normal array and assigning memory
to a pointer as we have just done. The most
important one is that the size of an array must be
a constant value, which limits its size to what we
decide at the moment of designing the program
before its execution, whereas the dynamic
memory allocation allows assigning memory
during the execution of the program using any
variable, constant or combination of both as size.
Cont’d
• The dynamic memory is generally managed by the
operating system, and in multitask interfaces it can be
shared between several applications, so there is a
possibility that the memory exhausts. If this happens and
the operating system cannot assign the memory that we
request with the operator new, a null pointer will be
returned. For that reason it is recommended to always
check to see if the returned pointer is null after a call to
new.
int * bobby;
bobby = new int [5];
if (bobby == NULL) {
// error assigning memory. Take measures.
};
Cont’d
Operator delete
• Since the necessity of dynamic memory is usually limited to
concrete moments within a program, once it is no longer
needed it should be freed so that it becomes available for
future requests of dynamic memory. The operator delete
exists for this purpose, whose form is:
delete pointer;
or
delete [] pointer;
The first expression: to delete memory allocated for a
single element, and
the second one for memory allocated for multiple elements
(arrays).
Cont’d
// Dynamic memory example
#include <iostream.h>
#include <stdlib.h>
int main ()
{
char input [100];
int i,n;
long * l;
cout << "How many numbers do you want to type in? ";
cin.getline (input,100);
i=atoi (input);
l= new long[i];
if (l == NULL)
exit (1);
ex7
Cont’d
for (n=0; n<i; n++)
{ cout << "Enter number: ";
cin.getline (input,100);
l[n]=atol (input);
}
cout << "You have entered: ";
for (n=0; n<i; n++)
cout << l[n] << ", ";
delete[] l;
return 0;
}
ex7
Cont’d-sample output
How many numbers do you want to type in? 5
Enter number : 75
Enter number : 436
Enter number : 1067
Enter number : 8
Enter number : 32
You have entered: 75, 436, 1067, 8, 32,
ex7
Cont’d
• This simple example that memorizes numbers does not have a limited
amount of numbers that can be introduced, thanks to us requesting to the
system to provide as much space as is necessary to store all the numbers
that the user wishes to introduce.
• NULL is a constant value defined in many fold C++ libraries specially
designed to indicate null pointers. In case that this constant is not defined
you can do it yourself by defining it to 0:
#define NULL 0
• It is indifferent to put 0 or NULL when checking pointers, but the use of
NULL with pointers is widely extended and it is recommended for greater
legibility. The reason is that a pointer is rarely compared or set directly to
a numerical literal constant except precisely number 0, and this way this
action is symbolically masked.
Cont’d
Dynamic memory in ANSI-C
• Operators new and delete are exclusive of C++
and they are not available in C language. In C
language, in order to assign dynamic memory
we have to resort to the library stdlib.h. We
are going to see them, since they are also valid
in C++ and they are used in some existing
programs
Cont’d
The function malloc
• It is the generic function to assign dynamic memory to
pointers. Its prototype is:
• void * malloc (size_t nbytes);
• where nbytes is the number of bytes that we want to
be assigned to the pointer. The function returns a
pointer of type void*, which is the reason why we have
to type cast the value to the type of the destination
pointer, for example:
• char * ronny;
• ronny = (char *) malloc (10);
Cont’d
• This assigns to ronny a pointer to an usable block of 10
bytes. When we want to assign a block of data of a different
type other than char (different from 1 byte) we must
multiply the number of elements desired by the size of
each element. Luckyly we have at our disposition the
operator sizeof, that returns the size of the type of a
concrete datum.
int * bobby;
bobby = (int *) malloc (5 * sizeof(int));
• This piece of code assigns to bobby a pointer to a block of 5
integers of type int, this size can be equal to 2, 4 or more
bytes according to the system where the program is
compiled.
ex8
Cont’d
The function calloc
• calloc is very similar to malloc in its operation, its main difference is
in its prototype:
void * calloc (size_t nelements, size_t size);
• since it admits 2 parameters instead of one. These two parameters
are multiplied to obtain the total size of the memory block to be
assigned. Usually the first parameter (nelements) is the number of
elements and the second one (size) serves to specify the size of
each element. For example, we could define bobby with calloc thus:
int * bobby;
bobby = (int *) calloc (5, sizeof(int));
• Another difference between malloc and calloc is that calloc
initializates all its elements to 0.
Cont’d
The function realloc
• It changes the size of a block of memory already assigned to a
pointer.
void * realloc (void * pointer, size_t size);
• pointer parameter receives a pointer to an already assigned
memory block or a null pointer, and size specifies the new size that
the memory block shall have. The function assigns size bytes of
memory to the pointer. The function may need to change the
location of the memory block so that the new size can fit, in that
case the present content of the block is copied to the new one to
guarantee that the existing data is not lost. The new pointer is
returned by the function. If it has not been posible to assign the
memory block with the new size it returns a null pointer but the
pointer specified as parameter and its content remains unchanged.
ex9
Cont’d
The function free
• It releases a block of dynamic memory
previously assigned using malloc, calloc or
realloc.
• void free (void * pointer);
• This function must only be used to release
memory assigned with functions malloc, calloc
and realloc.