Pointers
Objective #1: Understand the basics of pointers.
- A pointer is a variable or a constant that holds a memory address. That is, a pointer
named gradesPtr holds a memory address such as AA324456, in which the value
of the variable, grades, is stored.
Just like "1600 Pennsylvania Avenue"
is the street address of the President and not the actually the President,
himself, a pointer "points" to where some variable is stored in
the RAM (memory) of the computer. C++ programmers rarely need to know the
actual memory address (which is probably a hexadecimal number) of a variable. But using pointers does help programmers handle data more efficiently sometimes.
- An integer data typed variable is stored in 4 contiguous bytes of memory
in C++. If you created a pointer that
pointed to that integer variable, the pointer would store the memory address
of the first byte (of four) of the integer variable. Since a pointer variable
itself needs 4 bytes of memory of storage, it may not seem efficient to use
a pointer to refer to a variables of certain data types when it uses up the
same amount of storage. There are however advantages to doing so that you
may learn later in your programming career.
You have already used pointers earlier in this course when you declared
character arrays, as in:
char stateAbbreviation[3];
stateAbbreviation is actually a pointer. It "points" to the first
character of the character array. Since there is a null terminator at the
end of every character array, C++ can determine how long a character array
is and having a pointer point to the first character in the array is all
that is necessary to keep track of the character array.
Objective #2: Use the address-of operator.
- The & symbol is called the address-of operator. It returns the memory address of a variable rather than the
variable's stored value.
- You should interpret the & operator as "the address
of".
- For example, the following statement
pSum = &total;
should be read as "pSum stores the memory address of the variable total."
- In fact the statement,
int total = 10;
cout << &total << endl;
displays the memory address of where the variable total stores the value 10.
Objective #3: Declare and use pointer variables and the dereferencing operator.
- A pointer variable (or just called "pointer") is declared similarly as other types of variables except that
the dereferencing operator (*) is used to indicated that it is a pointer.
int numberOfStudents;
int *pNumberOfStudents;
In the example above numberOfStudents is simply a variable of type int while pNumberOfStudents is a pointer variable which points to an int value. It is good style to use the prefix p or the suffix Ptr with pointer variables so it is easy to tell what other variable a pointer variable points to.
- Note usually we do not type a space between the dereferencing operator (*) and the first letter
of the pointer's variable name.
- Be careful when declaring a pointer and a regular int variable in the same declaration statement
int *countPtr, count;
declares countPtr as a pointer variable that points to an int while count is declared as a regular int variable. If you want to declare two pointers in the same declaration statement then you must use the * operator with each variable name as in
int *countPtr, *totalPtr;
Note that it is not good style to declare multiple variables in the same declaration statement.
- It is important to initialize or eventually assign a pointer as in
int *countPtr = &count;
or
int *countPtr = NULL;
It is dangerous to dereference a pointer that was never initialized.
- You should interpret (i.e. say to yourself) "that which
_____ points to" every time that you see a * symbol.
For example, the following line of code:
int *pMoney;
should be read as "that which pMoney points to is an int data type."
- The dereferencing operator can be used in at least 3 ways:
- It is used in the declaration of a pointer variable as you read
above.
- It can also be used within a program to reference that actual value
of another variable, possibly having it printed on the screen or used
in practically any other way.
Example:
int *pResult;
int numOne, numTwo;
int answer;
pResult = &answer;
answer = numOne * numTwo;
cout << "The answer is " << *pResult<< endl;
- It can also be used to actually change the value of a variable without
using referring to the variable, itself!
Example:
int *pSneaky;
int innocentVariable = 100;
pSneaky = &innocentVariable;
*pSneaky = 0;
cout << innocentVariable << endl;
The code above causes 0 to be printed out even though innocentVariable was initialized to 100 and never directly changed via an assignment
statement later in the program!
- The keyword const can be used with pointers in three ways. Notice that const goes directly in front of the item that is being made constant.
Example 1
int num1 = -5, num2 = 5;
const int *pNum1; // not required to initialize pNum1 even though it would be good style to do so
pNum1 = &num1;
num1 = 10;
*pNum1 = 15; // error, cannot assign to a variable that is const
cout << *pNum1 << endl; // 10
pNum1 = &num2;
cout << *pNum1 << endl; // 5
pNum1 is a pointer to a constant integer. The value that pNum1 points to cannot be changed by using the pointer variable pNum1.
Example 2
int num1 = -5, num2 = 5;
int * const pNum2 = &num2; // must initialize pNum2 to an address
num2 = 10;
cout << *pNum2 << endl; // 10
pNum2 = &num1; // error, cannot assign to a variable that is const
pNum2 is a constant pointer to an integer. The integer can be changed but pNum2 cannot point to anything else. In this case, the pointer must be initialized (i.e. initially set equal to the address of a regular variable.)
Example 3
int num1 = -5, num3 = 9;
const int * const pNum3 = &num3; // must initialize pNum3 to an address
num3 = 10;
cout << *pNum3 << endl; // 10
//pNum3 = &num1; // error, cannot assign to a variable that is const
//*pNum3 = 15; // error, cannot assign to a variable that is const
pNum3 is a constant pointer to a constant integer. The value that is pointed to cannot be changed by using the pointer variable pNum3. Also, pNum3 cannot be changed to point to anything else.
Objective #4: Use pointers with arrays.
- You can create a pointer to an array and use that pointer to access or even modify individual elements of the array
int nums[5] = {12, 3, 44, 26, -6};
int *numsPtr;
numsPtr = nums; // or numsPtr = &nums[0]; but not numsPtr = &nums; since nums is already considered to be a pointer
for (int i = 0; i < 5; i++) // traversing an array
{
cout << *(numsPtr + i) << ' ';
}
Since the sizeof operator returns the number of bytes in an array, the loop can be modified to
for (int i = 0; i < sizeof nums/sizeof (nums[0]); i++)
{
cout << *(numsPtr + i) << ' ';
}
- Using pointer arithmetic, you can access and modify elements of the array.
int nums[5] = {12, 3, 44, 26, -6};
int *numsPtr;
numsPtr = nums; // or numsPtr = &nums[0];
cout << *numsPtr << endl; // 12
cout << *(numsPtr + 2) << endl; //44
cout << *(nums + 2) << endl; // 44 since any array name like nums is implicitly a const pointer
numsPtr++;
// nums++; // error since num is an implicit const pointer
cout << *numsPtr << endl; // 3
numsPtr += 3;
cout << *numsPtr << endl; // -6
When you add one to numsPtr as in numsPtr++, you are actually adding four to the memory address stored in numsPtr since numsPtr is pointing to 4-byte int values in the array nums. If nums was an array of double's, then the value 8 would be added to numsPtr each time that you increment it with numsPtr++;
- Unlike Java, C++ does not offer range-checking (i.e. checking for out-of-bounds errors) with arrays. So the programmer must be careful when performing pointer arithmetic with arrays. Otherwise, run-time or logic errors could occur.
- Since a character array is really just a pointer to the first letter of the character array, a traditional pointer could be used to reference the characters of a character array. Study the following code segment:
char word[] = "america";
const char * wordPtr = "america"; // it is wise to use const here to prevent the chance of the string literal being modified by another character array reference to the same string literal value
char word2[] = {'a', 'm', 'e', 'r', 'i', 'c', 'a'};
cout << word << endl; // america
cout << *(wordPtr + 3) << endl; // r
cout << word2[3] << endl; // r
Objective #5: Pass pointers to functions as parameters.
- It is possible to pass a pointer to a function as a parameter as in
void squareMe(int *);
// function prototype
squareMe(aPtr); // call statement
void squareMe(int *nPtr)
// function
{
*nPtr = *nPtr * *nPtr;
}
- It is possible to pass an array to a function with a pointer to the first element of the array:
int main()
{
int nums[] = {2, 5, 19, -9};
int *numsPtr = nums;
display(numsPtr);
return 0;
}
void display(int *numsPtr)
{
for (int i = 0; i < 4; i++)
{
cout << *(numsPtr + i) << ' ';
}
}
- Here is a function that finds the length of a character array which is passed as a pointer. The while loop terminates when it finds the null-terminator of the character array pointed to by stringPtr.
int stringLength(char *stringPtr)
{
int numCharacters = 0;
while (*stringPtr++)
{
numCharacters++;
}
return numCharacters;
}
Objective #6: Return pointers as return values from functions.
- A pointer can be returned from a function as in
int * sum(int, int);
int main()
{
cout << *sum(3, 5) << endl;
return 0;
}
int * sum(int a, int b)
{
int total = a + b;
int *totalPtr = &total;
return totalPtr;
}
Objective #7: Use function pointers.
- A Function pointer is a pointer to a function. Since a function is really just a memory address, a pointer that points to that memory address can be used to invoke the function. You can even pass parameters to that function via the function pointer.
void displayName();
void sum(int, int);
int sum2(int, int);
int main()
{
void (*namePtr) ();
namePtr = displayName;
namePtr(); // or (*namePtr)();
void (*sumPtr) (int, int); // pointer to a void function
sumPtr = sum;
sumPtr(4, -8); // -4 or (*sumPtr)(4, -8);
int (*sum2Ptr) (int, int); // pointer to a function that returns an int
sum2Ptr = sum2;
cout << sum2Ptr(4, -8) << endl; // -4 or cout << (*sum2Ptr) (4, -8) << endl;
return 0;
}
void displayName()
{
cout << "John Doe" << endl;
}
void sum(int a, int b)
{
cout << a + b << endl;
}
int sum2(int a, int b)
{
return a + b;
}
Objective #8: Use arrays of pointers