Sunday, September 12, 2010

C++: Declaring pointers

Last time we examined what a pointer is and a basic declaration of a pointer
- address of a variable. Let's discuss other ways of declaring pointers.
Along the way, I'll introduce you to memory arithmetic (especially in
regards to the last method of pointer declaration).
There are four or five ways of declaring a pointer (remember that in order
to declare a pointer, it needs the asterisk operator between the ptr name
and the data type). The first way is, of course, declaring the pointer to
hold the address of a known variable. For instance, if I store my age in an
integer variable and have an age pointer point to my age variable, I'd
write:
int age = 20;
int * age_ptr = &age;
Sound familiar? This is exactly the same declaration style I've used on
previous post, except that I'm now using integer pointer (as I've said
earlier, pointers can point to anything). Just like last time, the "age"
variable would hold my age right now (20) with age_ptr holding the address
where my age variable lives (not exactly "lives," but more towards
"located."). So, if we verify my age using cout, it'd be (like last time):
cout << *age_ptr << endl;
Remember what we've discussed earlier - just saying "age_ptr" would give us
the address of something, not the actual guy, thus I wrote dereference
operator along with age_ptr. On the monitor, my age (20) would be displayed.
But suppose if I have a friend who wish to store his age as a pointer and my
current age_ptr be modified (reassigned) to point to his age pointer? The
answer is perfect "yes." Pointers can point to other pointers, just like
values from one variable can be assigned to another. Let's see how we can do
this:
int friend_age = 24;
int * friend_age_ptr = &friend_age;
Here's the tricky part:
my-age_ptr = friend_age_ptr;
Wow, something we haven't seen before, but once you examine it carefully,
you'd understand that the above statement is perfectly valid. To help us
with a better illustration, suppose the address of my_age variable was 0001
and my friend's age variable address was 0002. Here's the map of the
variable assignment:
* my_age_ptr was initially assign to address 0001 (my age).
* Then friend_age_ptr was assigned to address 0002 (my friend's age).
* using the above map, we reassigned my-age_ptr to point to friend_age_ptr -
the address pointed to be my_age_ptr was changed from 0001 to 0002.
Now, if we print my_age_ptr (via dereference operator), we'd get my friend's
age instead (by the way, that friend of mine is my programming consultant).
But what if we have a pointer but don't know the variable to which it should
point to? In this situation (particularly when writing class constructor and
if part of the encapsulated variable is a pointer), then we can use a
safeguard - assigning the pointer to the address of zero (or null). A
typical declaration is:
data_type * pointer = NULL; // (notice that we need to capitalize the word
"NULL").
This is useful if you need to create a known pointer but doesn't know where
it should point to, or when creating default constructor for a class that
has a pointer variable as part of encapsulated data. But this declaration
has a cost: a likelyhood of a "fight" between you and your computer over not
being able to read from address of zero (one form of segmentation fault;
I'll come back to it in more details later). In order to avoid this, be sure
to assign a valid variable address to the pointer once the variable was
declared, like this:
double * double_ptr = NULL;
cout << *double_ptr << endl; // BAAAAAAD!
// Computer says, "Hey, you can't read from address of zero!".
double cosine_30_dg = 0.866; // Cos 30 = sqrt(3/2).
double_ptr = &cosine_30_dg;
cout *double_ptr << endl; // Thanks.
Now, when we print this, it shows 0.866 (cosine of 30 degrees, which, when
squared and multiplied by square of sine of 30, produces 1 (a well known
trig identity)).
The last method is something I need to come back in full detail later, as it
is related to the next post on memory management and dynamic memory, but
just want to show you how it can be done:
Suppose if I have a collection of data (like a vector) and want to store it
somewhere but don't want to bother with creating another variable just to
hold this collection. A neat way of accomplishing this task is using
so-called "dynamic memory" where we can work with it at run time. Unlike
so-callee variables we have worked with, this memory requires special
attention, as doing wrong things at wrong time could result in this memory
not being usable by other programs (called memory leak where a program would
use more and more memory but not relinquish memory space that it no longer
needs). Also, this memory does not have a name (actually, a variable name
does exist, but it is not that apparent yet).
To work with dynamic memory, we need pointers and two new keywords - new and
delete. I'll come back to meaning of these keywords in a sec (perhaps next
time), but to give you an idea of coding it, let's create a pointer that
stores five dynamic integers i.e. dynamic memory of five integers:
int * dynamic_ptr = new int[5];
Almost similar to how we'd declare an array of something as we've seen
earlier. Here, the pointer (dynamic_ptr) points to index 0 (first number) of
our array.
To tell the computer that we don't need this dynamic array anymore, we'd
use:
delete [] dynamic_ptr;
Note that we use brackets ([]) to delete the entire array. We'll examine the
operation of this task in more detail as we continue our pointer discussion
with dynamic memory allocation (coming up shortly), but a bit of review
exercise is in order...
// JL

No comments:

Post a Comment