C++ Pointers
C++ Taking the Bull by the Horns – Part 5
Forward: In this part of the series, we look at the meaning of the C++ derived object type called, pointer.
By: Chrysanthus Date Published: 21 Aug 2012
Introduction
Recall
The memory of a computer is a series of cells. A consecutive group of these cells is a region or an object. An object can hold a datum. This datum is the value of the object. There are different types of data. In one of the previous parts of the series we talked about the int, float, char, _Bool and void object types. Those are the fundamental object types. A derived object type is a combination of two or more fundamental object types. There is a derived type called the pointer. The pointer is the focus of this part of the series. An object can hold a pointer.
The series of cells of the memory are numbered numerically, consecutively. These numbers are called addresses. So each memory cell has a unique memory address.
You will take things in this tutorial the way I give you. Do not try to make analogy with the human language (English). Do not also try to make analogy with mathematic statements. Just take things the way I give you in order not to be misled by human language or mathematics.
Note that I have been talking about object types and not data types. The phrase data type is having resemblance in human language and mathematics. Using the phrase, data types, can be misleading and make your understanding of C++ difficult. Remember, the philosophy in this series is to learn C++ as it is, after that you decide on what to do with it. That is, after learning it you decide on how you will use it to solve mathematical problems and other problems, expressed in human language.
If you are not new to computer programming, you might wonder why I teach pointers early in the series. Other authors teach pointers late in their books or tutorials because they are making analogy of C++ with human language and mathematics. It makes sense to teach a computer language by likening it to the human language and mathematics. I believe, that approach works well with other computer languages, but not with C++ or its small brother, C. As I said at the beginning, I present C++ to you the way the inventors (authors) see it, but in simple terms. The object type, pointer, is an important feature in C++, and you most learn it. It is used a lot to do many other things in C++. So I have to teach you pointers early enough in the tutorial. In that way, I believe, you will understand the rest of the series easily.
Note: If you cannot see the code or if you think anything is missing (broken link, image absent), just contact me at forchatrans@yahoo.com. That is, contact me for the slightest problem you have about what you are reading.
Now, this is important: Do not add or subtract in your mind, any idea to what I give you in this part of the series and in the rest of the series. In order for you to do that you would have to liken what I give you to the human language or mathematics. I believe, doing that would be misleading and you would not find the study of C++ easy.
You can have an object called the Pointer object. This is just like any other object. Here is the issue: the value (content) of a pointer object is a memory address. That is, if an object has a value that is a pointer, it means there is a memory address in the region of the object. This memory address is actually pointing to some other region (object) in memory. In other words, this memory address is the address of some other object. We have just talked about two objects: the pointer object and some other object. The value (content) of the pointer object is the memory address of some other object. Remember an object is a region in memory.
The value in the other object can occupy more than one consecutive memory cells. For the pointer object, the address it has is that of the first memory cell of the other object. We say the pointer object points to the other object, because it has the memory address (first cell) of the other object. The pointer is actually the memory address of the pointer object. The pointed object is the other object.
Of course the pointer object and the pointed object can have identifiers. We saw identifiers in the previous part of the series.
Note: When dealing with a pointer, know that two objects are concerned.
Reference operator
C++ has what is called the reference operator. It is the & symbol. If you have an identifier as we saw in the previous part of the series, when you precede it with the & symbol, the resulting expression returns an address to the object. Let us say you have the following definition:
int hisInt;
then the following expression will return the memory address of the object identified by, hisInt:
&hisInt
In programming, you do not need to know the exact number for the address, so do not worry about the exact address number as we go along. The symbol, &, is called the reference operator
The Dereference operator
C++ has many operators. One of them is called the dereference operator. It is the * symbol. It can be typed in front of the pointer object identifier. This operator actually has two meanings so far as pointers are concerned: When used in the definition or initialization of a pointer object, it refers to the address of the pointed object. When used with the pointer object after creation (definition or initialization) of the pointer object, it refers to the value of the pointed object. Always remember these two meanings. You will see examples of these below.
Consider the following code segment:
float hisFloat;
float *myPointer;
myPointer = &hisFloat;
As I said above, whenever you talk about a pointer, two objects are concerned: the pointed object and the pointer object. The pointed object above has the identifier, hisFloat. It is of type float. In the previous part of the series, we saw definitions similar to the first line (pointed object) above. You have the definition for hisFloat; you could assign a value to it if you wished.
Next in the code segment, you have the definition of the pointer object. It begins with the type of object of the pointed object. It does not begin with any special type indicator for the pointer object. Next, you have a space and then the dereference operator, *. Then you have the identifier that identifies the pointer object. Do not confuse between pointer object and pointed object.
There is no special object type indicator for the pointer object. * is not the type indicator for the pointer object. * has two meanings so far as pointers are concerned (see above).
The last statement in the previous code, obtains the address (&hisFloat) of the pointed object and assigns it to the pointer object, identified by the identifier, myPointer. Remember, when using an identifier after definition (or initialization), you do not precede it with any object type indicator; that is what we have done in the last statement. Also note that for the case of pointers, the identifier of the pointer is not preceded by *.
Note the way the pointer object identifier is defined. It is not defined the way object identifiers of other types are defined. The identifier is preceded first with the object type of the object it is pointing to; then you have the space and the dereference operator; before the identifier.
You can create a pointer object with initialization. Consider the following code segment:
float hisFloat;
float *myPointer = &hisFloat;
This code segment is similar to the previous. The second and third statements of the previous code have been joined into one, in this code segment.
In the above two code segments, the identifier of the pointer object is myPointer. Now, this is important: In the above code segment, the dereference operator has been used to assign a memory address as value to the pointer (object). This is one use of the dereference operator. The other use is given below.
After creating the pointer, you can use the identifier of the pointer object with or without the dereference operator in front of it. When the dereference operator is in front of it, the identifier has one meaning; when it is absent, the identifier has a different meaning. So after creation, you can use the above pointer, like,
myPointer
or like
*myPointer
After creation of the pointer object, to use its identifier, you do not precede it with any object type indicator; you can precede it with the dereference operator or omit the dereference operator, as shown above.
When the dereference operator is absent, the identifier refers to the address of a pointed object; that is the address of the pointed object can be assigned to the identifier (object) or the identifier can return the address of a pointed object, when * is absent. When the dereference operator is present, the identifier refers to the value (content) of the pointed object; that is, the value of the pointed object can be assigned to the identifier, or the identifier can return the value of the pointed object, when * is present.
In the following example, the dereference operator has been used in front of the object identifier after creation. Read the code and note that a value of 23.5 has been assigned to the pointed object. Try the code.
#include <iostream>
using namespace std;
int main()
{
float hisFloat;
float *myPointer = &hisFloat;
*myPointer = 23.5;
cout << *myPointer;
return 0;
}
The output should be 23.5. In the second line of the block, the dereference operator causes the identifier to refer to the address of the pointer object. In the third and fourth lines it is referring to the value (content) of the pointed object.
Some three points to note
There are three points to note: The first point is: when the dereference operator is used in the definition or initialization (during creation e.g. as in “float *myPointer = &hisFloat”) of a pointer object identifier, the pointer identifier is referring to the address of the pointed object (address of which would be in the pointer object). The second point is, after creation of the pointed (e.g. in “float hisFloat” above) and pointer (e.g. in “float *myPointer” above) objects, the absence of the dereference operator in front of the identifier of the pointer object, means that the pointer identifier is still referring to the address of the pointed object (like in the first code segment, above – under Creating a Pointer). The third point is, when the dereference operator is used after creation of the pointed and pointer objects, in front of the pointer identifier, the pointer identifier preceded by *, refer to (means) the value of the pointed object.
Before we continue, know that in a C++ statement, the expression on the left of the assignment operator (=) can be called the Left Operand; that on the right can be called the right operand.
Another way to put the above information is as follows: During initialization of the pointer, the dereference operator is used with the identifier of the pointer object in the left operand and the right operand is a memory address (e.g. in “*myPointer = &hisFloat;”). During definition of a pointer identifier, the dereference operator is used in front of the identifier for the pointer (e.g. in “float *myPointer;”). After initialization or definition of the pointer identifier, the dereference operator in front of the identifier of the pointer object means the value of the pointed object (e.g. in “*myPointer = 23.5;”), while the absence of the dereference operator in front of the identifier of the pointer object means the address of the pointed object (e.g. in “myPointer = &hisFloat;”).
You can get the address of an object from the identifier of a non-pointer object. A non-pointer object is one which is defined without the reference or dereference operator (e.g. “int hisInt;”) or one which is initialized without the reference or dereference operator (e.g. “int hisInt = 86;”). To get the address, you precede the identifier with, & (e.g. as in “&hisInt”).
The pointer object has a pointer object type (address) as value. However, there is no object type indicator for a pointer object. The object type indicator for an integer object is, int; that for a float object is, float; that for a Boolean object is bool; and that for a character object is, char. Despite this, there is no object type indicator for a pointer object. You just play around with the * symbol to have a pointer. After that, you use the identifier without * as the pointer (left operand pointer to which you could assign the real address with the reference operator in a right operand).
Creating Pointers without using the Reference Operator
Above, we created the pointer object either like,
float hisFloat;
float *myPointer;
myPointer = &hisFloat;
or like
float hisFloat;
float *myPointer = &hisFloat;
In either of these approaches, we had to create the pointed object first (in the first line) and then create the pointer object next assigning the address of the pointed object to the pointer object. It is possible to create the pointer object without first creating the pointed object; in this case you would not use the reference operator (&). However, in this case you have to be careful with the method you use. The successful method here is to define the pointer identifier, in one statement, then assign the value of the pointed object in another statement. Read and try the following code, which shows the successful method:
#include <iostream>
using namespace std;
int main()
{
int *myPointer;
*myPointer = 453;
cout << *myPointer;
return 0;
}
The above code works. We have obtained the pointer object without first creating the pointed object and so we have obtained the pointer object without using the reference operator. In this situation, the operating system creates the pointed object for you. As the operating system creates the pointed object for you, you do not have any identifier for the pointed object. In this situation, there is still an address for the pointed object, but you do not know it.
Constant
In the previous part of the series, we were dealing with one object. This one object has an identifier. There we talked about making the value (content) of the one object constant (unchangeable). In this part of the series, we are talking about two objects: the pointer object and the pointed object. In this section, the focus is on the pointed object. So, here, we have two things we can make constant (unchangeable): the value of the pointed object or the pointer (address) to the pointed object. Remember, the pointer (address) to the pointed object is the value of the pointer object.
You make either the value of the pointed object or the address (pointer) of the pointer object, constant, in the definition or initialization step. The syntax to make the value of the pointed object constant is:
const Type *pointerIndentifier
An example is,
const int *myPointr
This is important: Under this condition, you cannot change the value of the pointed object using the pointer.
The syntax to make the pointer to the pointed object constant is,
Type *const pointerIdentifier
An example is,
int *const myPointr
Note the position of * and the word, const, in the two constant cases. Read and try the following code where the value of the pointed object is made constant:
#include <iostream>
using namespace std;
int main()
{
int hisInt = 55;
const int *myPointer = &hisInt;
cout << *myPointer;
return 0;
}
The above code works. The following code is the above, modified, by trying to change the value of the pointed object using the pointer object. Read and try to compile the code, and note that the compiler will issue an error message.
#include <iostream>
using namespace std;
int main()
{
int hisInt = 55;
const int *myPointer = &hisInt;
*myPointer = 70; //cannot be allowed
cout << *myPointer;
return 0;
}
“*myPointer” outside of the definition or initialization means the value of the object pointed to by myPointer. myPointer is the identifier of the object having the pointer (address) of the pointed object. The statement added, “*myPointer = 70;” tries to change the constant value of the pointed object; so the compiler does not compile the code and issues an error message.
Read and try the following code where the pointer to the pointed object is made constant:
#include <iostream>
using namespace std;
int main()
{
int hisInt = 55;
int *const myPointer = &hisInt;
cout << *myPointer;
return 0;
}
The above code works. The following code is the above, modified, by trying to change the pointer (address) of the pointed object using the pointer object. Read and try to compile the code, and note that the compiler will issue an error message.
#include <iostream>
using namespace std;
int main()
{
int hisInt = 55;
int *const myPointer = &hisInt;
int herInt = 80;
myPointer = &herInt; //cannot be allowed
cout << *myPointer;
return 0;
}
Two statements were added. The first added statement creates a new object, identified by herInt. The value, 80 is assigned to this object. The second added statement tries to change the constant pointer (constant value of the pointer object) to the address of the new object. The compiler does not compile and issues an error message.
In the first of the above two const cases, where the value of the pointed object is kept constant through the pointer, you can change the value of the pointed object as the following code sample illustrates:
#include <iostream>
using namespace std;
int main()
{
int hisInt = 55;
const int *myPointer = &hisInt;
hisInt = 93;
cout << hisInt;
return 0;
}
This code sample works and the value of the pointed object is changed. How is this possible? In the case of constant pointed object value, through the pointer, it is the pointer (object) that controls and maintains the constancy, and not the pointed object itself. So the value of the pointed object can be changed directly, by using the identifier of the pointed object. You cannot change the value using the pointer.
In C++, when we talk of a constant value we are referring to the case of a single object whose value is constant as we saw in the previous part of the series. When we talk about constant pointed value, we are referring to the case of two objects (pointer and pointed), where the value of the pointed object is constant so far as the pointer is concerned. When we talk about constant pointer, we are referring to the case of two objects (pointer and pointed), where the pointer (address of the pointed object in the pointer object) is constant.
Void Object Type
When an object has no value, and it has not been reserved for an int or float or any of the other object types, it is said to have a void object type.
Void Pointer Type
A pointer that points to an object that is a void object type, is said to be a pointer of void pointer type. Here we have two objects: the pointer object and the pointed object. The pointer object is normal, and it has a pointer (address) to some other object. This other object is empty and has not been reserved for an int or float or any of the other object types.
Null Pointer
When you talk about a pointer you talk about two objects: the pointer object and the pointed object. When defining a pointer object you have to precede it with the object type of the pointed object; that is always the case. A situation may arise when you would have a pointer, for a particular type of object, but it has not been decided if that object is to exist. In other words you have just one object, the pointer object, which will hopefully point to an object of a particular type, but at the moment is not pointing to any object. In this case, the inventors decided that the value of the pointer object does not have to be empty, it has to be zero. Such a pointer does not point to any object. Such a pointer is called a null pointer. The following code illustrates this:
int * myPointer;
myPointer = 0;
The object identified by myPointer here, does not exist; that is, there is a pointer object and no pointed object. Its value is zero. It will hopefully point to an integer object, someday, but at the moment it is not pointing to any object. When you want it to point to an integer object, just assign the object’s address to it; something like,
myPointer = &hisInt;
You can have a null pointer that will hopefully point to a float object; in that case you start with float in the definition or initialization (the above pointer starts with int). In a similar way, you can have a null pointer for any object type; just start with the object type in the definition or initialization of the pointer.
Well, it has been a long ride for this tutorial. We have come to its end.
If you have understood all what I have written in this tutorial, then you have achieved something; you need to relax. Go somewhere and have a drink, relax, before you come back and continue with the next part of the series. The study of Pointers is one of the greatest headaches in C++; if you have understood it, that is very good; if you have not understood it, then repeat this tutorial. We continue the C++ studies in the next part of the series.
Chrys
Related Courses
C++ CourseRelational Database and Sybase
Windows User Interface
Computer Programmer – A Jack of all Trade – Poem
NEXT