In this lab, you will:
You won't always know the specific value in a pointer, but you won't care as long as it contains the address of the variable you are after. You need to declare and initialize pointers just as you would other variables, but there are special operators that you need to use.
|
dereference operator, indirection operator |
This is used
to declare
a variable as a pointer.
It is also used when you want to access the value pointed to by the pointer variable. |
|
reference operator, address-of operator |
Use before a variable to indicate that you mean the address of that variable. You'll often see this in a function header where the parameter list is given. |
|
member selection operator | This is used to refer to members of structures |
First, we'll declare two ordinary integers, and also pointers to those integers.
int alpha = 5; int beta = 20; int* alphaPtr = α int* betaPtr = βThe characters Ptr in the pointer variable name have no special significance. They are simply a memory aid for the programmer. Let's look more closely at one of the pointer declarations.
int* alphaPtr = α
An aside here: It is also permissable to position the asterisk closer to the pointer
variable name,
i.e. int *alphaPtr.
However the convention seems to be moving towards
placing the asterisk closer to the datatype.
Try to visualize memory after these declarations, thinking of the pointer variable as not having a particular value, simply links to the variables to which they had been assigned.
Now let's look at a trivial example of how to access this data.
*alphaPtr += 5;
*betaPtr += 5;
After these statements, it is only the contents of the alpha and
beta variables that would be changed.
You might say, "What's the big deal here? I could just as easily have written this:"
alpha += 5;
beta += 5;
Click here to download a simple pointer program.
Let's look at both pass by value and pass by reference to make sure we understand the difference. Here is a little example that illustrates how parameters are passed by value. Here's what that looks like:
int a = 5; int b = 9; swap(a,b); // main pgm function call ... void swap(int x, int y) // pass by value { int temp; temp = x; x = y; y = temp; return; }Simple, direct - right? Well yes, but the values of a and b in the main program have not been changed! If that is what you really wanted to do, you should have used pass by reference.
To do this, you need to change
the function prototype and header to
void swap(int
&
x, int
&
y)
Here is what the whole program looks like.
//Filename: swap.cpp #include <string.h> #include <iostream> using namespace std; void swap (int& x, int& y); int main () { int a = 5; int b = 9; cout << "This program exchanges 2 values using reference parameters." << endl; cout << "Values before the exchange:" << endl; cout << "a= " << a << " b= " << b << endl; swap(a, b); // code that calls the function cout << "Values after the exchange:" << endl; cout << "a= " << a << " b= " << b << endl; } // function for passing by reference void swap (int& x, int& y) { int temp; temp = x; x = y; y = temp; return; } // end swap
Now, when the function is executed, the values of a and b will be changed in the main program.
(Click to download swap.cpp)
Let us reimplement swap with pointers. This is how pass by reference was done in traditional C (before C++)// function for passing by reference using pointers void swap (int* x, int* y) { int temp; temp = *x; *x = *y; *y = temp; return; } // end swap
Why do we have to put the * in front of x and y when we are doing assignment?
Note that our call from main will be:
swap (&a, &b);
It may seem like we have added a level of complexity to pass by reference, but trust me, this information will help you if you ever work with traditional C functions (found in CS330).
Next, we will look at how pointers are used with C++ structures.
struct Student // define the structure { string name; int id; int mark[3]; }; .... void main() { Student stu; // declare an instance of the structureYou could simply pass the address of that instance directly to a function by coding:
function_name(&stu);You could also add a pointer declaration to reference that instance.
Student * stuPtr = &stu;The general syntax to declare a pointer and associate it with the instance of a structure is this:
structure_name * pointer_name = & instance_name;Now, using this pointer to reference the structure instance, we could write:
(*stuPtr).id = 1999;The *, (the indirection operator), tells the compiler to use what stuPtr is pointing to. The parentheses are necessary because you want the compiler to evaluate the address in stuPtr before that value is connected to the id member delimeted by the dot operator. This is because the dot operator normally takes precedence over the indirection operator. (Remember order of precedence and order of evaluation in an expression? If not, grab your text and review these topics.)
The syntax get awkward here though. i.e. (*stuPtr).id While that form of reference is technically correct, it is somewhat cumbersome, so C++ also allows another, easier form of reference.
stuPtr->id = 1999;This form eliminates the parentheses in a dot expression. We'll see more of this in the programming exercise for this week's lab exercise. The next section of these notes explains how pointers are necessary for a C++ construct called dynamic data.
new datatype | Used to allocate a dynamic variable.
e.g. int *tmpPtr = new int; |
delete pointer | Used to deallocate a dynamic variable.
e.g. delete tmpPtr; |
The new operator is used to create dynamic data variables in the so-called free store, available in memory for this purpose. Free space is also referred to as the heap. (Even gurus have been known to let their hair down sometimes.) A couple of points to remember when you use the new statement.
int* tmpPtr = new int;The first part, int* tmpPtr, declares an integer pointer named tmpPtr. The second part, new int, creates a space in the free store, and returns a pointer to that space. The returned pointer is assigned to tmpPtr. This is typical of C++, i.e. you can accomplish a lot in a single line of code.
You can declare arrays in free store as well as simple variables.
e.g. int* weightPtr = new int [3];
To use this dynamic array element you could code:
weightPtr[1] = 17; // Note: the "*" is not needed in an array reference.
We'll see more of this in the programming exercise for this week's lab exercise. The whole idea of using dynamic data is to economize on memory space. So when you've finished with a piece of dynamic data you should release the space with the delete statement.
delete tmpPtr; delete [] weightPtr;This releases space in the free store, but does not delete the pointer. Be careful here! If you try to use the pointer again, after the delete statement, you don't know what address will be in the pointer. Beware the dreaded segmentation fault, core dump! (See the next section for details.)
To safeguard an inadvertent overwrite of a critical area in memory, it is advisable to set pointers to NULL (or nullptr) after you delete the associated dynamic data space. e.g.
tmpPtr = NULL; //or tmpPtr = nullptr weightPtr = NULL; //or weightPtr = nullptr
Segmentation fault - core dump.This is a really ugly way of punting you and your program out of computer memory. Unix gurus might have some hope of decyphering the core file which is supposed to be a dump of the contents of memory (called core way back when the earth was cooling. Yes, C is that old.)
Normal people deal with the core file more simply:
rm coredoes the trick quite nicely. That can be a truly big file, so you don't want it hanging around taking up your disk space.
But what about the problem that caused the dump in the first place?! Well, if you're using pointers in your program, it's likely that one of them took on a bad value. Go back to your source code and look for statements that change pointer values. Remember that when you are in emacs, you can search for a particular string (such as the name of a pointer) by entering: C-s search_string
Wednesday, 04-Jan-2023 13:21:29 CST |
|
|
|