Chapter 6 Computer Programmingodp
Chapter 6 Computer Programmingodp
[ECEg-1052]
Chapter Six:
Pointers
3
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
4
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:
5
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.
6
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.
7
Cont’d...
8
Cont’d...
At this point, and following with the same example initiated above where:
andy = 25;
ted = &andy;
you should be able to clearly see that all the following expressions are true:
andy == 25
&andy == 1776
ted == 1776
*ted == 25
9
Cont’d...
The general form of a pointer declaration is:
type * pointer_name;
where type is the type of data pointed, not the type of the pointer itself.
For example:
int * a; //creates integer pointer a
char * b; // creates a character pointer b
float * c; // creates a float pointer c
➔
Multiple pointers require multiple asterisks
int *myPtr1, *myPtr2;
10
Cont’d...
Two special operators * and & are used with pointers.
The & returns the memory address of its operand (usually a variable).
The & is used to in initialize a pointer to point to a variables address.
Example:
int i=25 ; // declares an int variable i
int * a ; // declares an int pointer a
a= & i ; // initialises a; stores the memory address of i into a
11
Cont’d...
The pointer operator, *, does the reverse of &.
The unary operator * returns the value of the variable located at the address
following it.
Example: Suppose address of i is 1776
Output:
int i =80;
7
int j; 80
int * a; 1776
a=&i;
j =* a ; // the value (80) stored in the address it points to
* a=7 ; // puts 7 at address (&i) pointed by a
cout << i<<endl;
cout << j<<endl;
cout << a <<endl; 12
Cont’d...
#include <iostream>
int * mypointer;
mypointer = &value1;
*mypointer = 10;
mypointer = &value2;
*mypointer = 20;
} 13
Cont’d...
//Here is an example a bit more complicated:
#include <iostream> Output:
using namespace std; Value1==10
int main (){
value2==20
int value1 = 5, value2 = 15;
int *p1, *p2; // more pointers
p1 = &value1; // p1 = address of value1
p2 = &value2; // p2 = address of value2
*p1 = 10; // value pointed by p1 = 10
*p2 = *p1; // value pointed by p2 = value pointed by p1
p1 = p2; // p1 = p2 (v alue of pointer copied)
*p1 = 20; // value pointed by p1 = 20
cout << "value1==" << value1 << endl;
cout<<" value2==" << value2;
} 14
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;
15
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.
16
Cont’d...
Due to the character of variables all the expressions that include pointers in the following example are perfectly valid:
#include <iostream>
using namespace std;
int main (){
int numbers[5]; Output:
int * p;
10, 20, 30, 40, 50,
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] << ", ";
}
17
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.
18
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;
19
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.
20
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.
21
Cont’d...
If we imagine that "hello" is stored at addresses 1702 and following, the
previous declaration could be outlined thus:
It is important to indicate that terry contains the value 1702 and not 'h' nor
"hello", although 1702 points to these characters.
22
Cont’d...
The pointer terry points to a string of characters and can be used exactly as if it was an
Array (remember that an array is just a constant pointer).
For example, if our temper changed and we wanted to replace the 'o' by a '!' sign in the
content pointed by terry, we could do it by any of the following two ways:
terry[4] = '!';
*(terry+4) = '!';
23
Arithmetic of pointers
Only addition and subtraction operations are allowed to be conducted.
When we saw the different data types that exist, we saw that some occupy
more or less space than others in the memory.
For example, in the case of integer numbers, char occupies 1 byte, short
occupies 2 bytes and long occupies 4.
Let's suppose that we have 3 pointers:
char *mychar;
short *myshort;
long *mylong;
24
Cont’d...
They point to memory locations 1000, 2000 and 3000 respectively.
So if we write:
mychar++;
myshort++;
mylong++;
mychar, as you may expect, would contain the value 1001. Nevertheless, myshort
would contain the value 2002, and mylong would contain 3004.
The reason is that when adding 1 to a pointer we are making it to point to the
following element of the same type with which it has been defined, and therefore
the size in bytes of the type pointed is added to the pointer.
25
Cont’d...
This is applicable both when adding and subtracting any number to a pointer. It would
happen exactly the same if we write:
mychar = mychar + 1;
myshort = myshort + 1;
mylong = mylong + 1;
26
Cont’d...
It is important to warn you that both increase (++) and decrease (--)
operators have a greater priority than the reference operator asterisk
(*), therefore the following expressions may lead to confusion:
*p++;
*p++ = *q++;
The first one is equivalent to *(p++) and what it does is to increase p (the
address where it points to - not the value that contains).
27
Cont’d...
In the second, because both increase operators (++) are after the expressions to
be evaluated and not before, first the value of *q is assigned to *p and then both
q and p are increased by one. It is equivalent to:
*p = *q;
p++;
q++;
Like always, I recommend you use parenthesis () in order to avoid unexpected
results.
28
Pointers to pointers
C++ allows the use of pointers that point to pointers, that these, in its
turn, point to data. In order to do that we only need to add an asterisk (*)
for each level of reference:
char a;
char * b;
char ** c;
a = 'z';
b = &a;
c = &b;
29
Cont’d...
Supposing the randomly chosen memory locations of 7230, 8092 and
10502:
c is a variable of type (char **) with a value of 8092
*c is a variable of type (char*) with a value of 7230
**c is a variable of type (char) with a value of 'z'
30
void pointers
The type of pointer void is a special type of pointer.
Void pointers can point to any data type, from an integer value or a float
to a string of characters.
Its sole limitation is that the pointed data cannot be referenced directly
(we can not use reference asterisk * operator on them), since its length is
always undetermined, and
➔
For that reason we will always have to resort to type casting or
➔
Assignations to turn our void pointer to a pointer of a concrete data type to
which we can refer.
31
Cont’d...
#include <iostream>
Output:
using namespace std;
9, 40
int main (){
int b = 9;
float c = 12.4;
float* d;
void* e;
d=&c;
*d=40;
e=&b;
cout << (*((int*)e)) << ","<<c;
}
32
Pointers to functions
C++ allows operations with pointers to functions.
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++.
Example
void centimize(double* ptrd)
33
Cont’d...
// arguments passed by pointer Output:
#include <iostream> Var = 10.0 inches
using namespace std;
Var = 25.4 centimeters
void centimize(double* ptrd){
*ptrd *= 2.54; //*ptrd is the same as var
}
int main(){
double var = 10.0; //var has value of 10 inches
cout << "var = " << var << " inches" << endl;
centimize(&var); //change var to centimeters
cout << "var = " << var << " centimeters" << endl;
return 0;
} 34
Cont’d...
The function centimize() is declared as taking an argument that is a
pointer to double :
➔
void centimize(double*) // argument is pointer to double
When main() calls the function, it supplies the address of the variable as
the argument:
➔
centimize(&var);
Remember that this is not the variable itself, as it is in passing by
reference, but the variable’s address.
35
Cont’d...
Because the centimize() function is passed an address, it must use the
dereference operator,
➔
*ptrd , to access the value stored at this address:
➔
*ptrd *= 2.54; // multiply the contents of ptrd by 2.54 of course this is the same as
*ptrd = *ptrd * 2.54; // multiply the contents of ptrd by 2.54 where the standalone
asterisk means multiplication.
Passing a pointer as an argument to a function is in some ways similar to
passing a reference.
➔
They both permit the variable in the calling program to be modified by the
function.
36
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.
37
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.
38
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.
39
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.
40
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.
};
41
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).
42
Cont’d...
#include <iostream> // introduces operator new
#include <cstring>
using namespace std;
int main(){
char* str = "Idle hands are the devil’s workshop.";
int len = strlen(str); //get length of str
char* ptr; //make a pointer to char
ptr = new char[len+1]; //set aside memory: string + ‘\0’
strcpy(ptr, str); //copy str to new memory area ptr
cout << "ptr=" << ptr << endl; //show that ptr is now in str
delete[] ptr; //release ptr’s memory
return 0;
Output:
}
Ptr= Idle hands are the devil’s workshop.
43
Cont’d...
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.
44
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.
45
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 type size_t is defined in stdlib.h as, more or less, an unsigned integer.
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.
46
Cont’d...
Example:
char * ronny;
ronny = (char *) malloc (10);
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.
47
Cont’d...
Luckily 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.
48
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.
49
Cont’d...
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 initializes all
its elements to 0.
50
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.
51
Cont’d...
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 possible 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.
52
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.
53
Thank You !
54