CHAPTER 2 Pointer
CHAPTER 2 Pointer
CHAPTER 2 Pointer
2. Pointer in C++
Pointers, References and Dynamic Memory Allocation are the most powerful features in C/C++
language, which allows programmers to directly manipulate memory to efficiently manage the
memory - the most critical and scarce resource in computer - for best performance. However,
"pointer" is also the most complex and difficult feature in C/C++ language.
Pointers are extremely powerful because they allow you to access addresses and manipulate their
contents. But they are also extremely complex to handle. Using them correctly, they could
greatly improve the efficiency and performance. On the other hand, using them incorrectly could
lead to many problems, from un-readable and un-maintainable codes, to infamous bugs such as
memory leaks and buffer overflow, which may expose your system to hacking. Many new
languages (such as Java and C#) remove pointer from their syntax to avoid the pitfalls of
pointers, by providing automatic memory management.
A computer memory location has an address and holds content. The address is a numerical
number (often expressed in hexadecimal), which is hard for programmers to use directly.
Typically, each address location holds 8-bit (i.e., 1-byte) of data. It is entirely up to the
programmer to interpret the meaning of the data, such as integer, real number, characters or
strings.
To ease the burden of programming using numerical address and programmer-interpreted data,
early programming languages (such as C) introduce the concept of variables. A variable is a
named location that can store a value of a particular type. Instead of numerical addresses, names
(or identifiers) are attached to certain addresses. Also, types (such as int, double, char) are
associated with the contents for ease of interpretation of data.
Each address location typically hold 8-bit (i.e., 1-byte) of data. A 4-byte int value occupies 4
memory locations. A 32-bit system typically uses 32-bit addresses. To store a 32-bit address, 4
memory locations are required.
1|Page
The following diagram illustrates the relationship between computers' memory address and
content; and variable's name, type and value used by the programmers.
A pointer variable (or pointer in short) is basically the same as the other variables, which can
store a piece of data. Unlike normal variable which stores a value (such as an int, a double, a char),
a pointer stores a memory address.
For example,
2|Page
int * iPtr; // Declare a pointer variable called iPtr pointing to an int (an int pointer)
// It contains an address. That address holds an int value.
double * dPtr; // Declare a double pointer
Take note that you need to place a * in front of each pointer variable, in other words, * applies
only to the name that followed. The * in the declaration statement is not an operator, but
indicates that the name followed is a pointer variable. For example,
Naming Convention of Pointers: Include a "p" or "ptr" as prefix or suffix, e.g., iPtr, numberPtr,
pNumber, pStudent.
The address-of operator (&) operates on a variable, and returns the address of the variable. For
example, if number is an int variable, &number returns the address of the variable number.
You can use the address-of operator to get the address of a variable, and assign the address to a
pointer variable. For example,
int * pAnother = &number; // Declare another int pointer and init to address of the variable number
3|Page
As illustrated, the int variable number, starting at address 0x22ccec, contains an int value 88. The
expression &number returns the address of the variable number, which is 0x22ccec. This address is
then assigned to the pointer variable pNumber, as its initial value.
For example,
Take note that pNumber stores a memory address location, whereas *pNumber refers to the value
stored in the address kept in the pointer variable, or the value pointed to by the pointer.
As illustrated, a variable (such as number) directly references a value, whereas a pointer indirectly
references a value through the memory address it stores. Referencing a value indirectly via a
pointer is called indirection or dereferencing.
4|Page
The indirection operator (*) can be used in both the RHS (temp = *pNumber) and the LHS
(*pNumber = 99) of an assignment statement.
Take note that the symbol * has different meaning in a declaration statement and in an
expression. When it is used in a declaration (e.g., int * pNumber), it denotes that the name followed
is a pointer variable. Whereas when it is used in a expression (e.g., *pNumber = 99; temp <<
*pNumber;), it refers to the value pointed to by the pointer variable.
int i = 88;
double d = 55.66;
int * iPtr = &i; // int pointer pointing to an int value
double * dPtr = &d; // double pointer pointing to a double value
iPtr = &d; // ERROR, cannot hold address of different type
dPtr = &i; // ERROR
iPtr = i; // ERROR, pointer holds address of an int, NOT int value
int j = 99;
iPtr = &j; // You can change the address stored in a pointer
Example
/* Test pointer declaration and initialization (TestPointerInit.cpp) */
#include <iostream.h>
int main() {
int number = 88;
int * pNumber;
pNumber = &number;
cout << pNumber << endl; // Print content of pNumber (0x22ccf0)
cout << &number << endl; // Print address of number (0x22ccf0)
cout << *pNumber << endl; // Print value pointed to by pNumber (88)
cout << number << endl; // Print value of number (88)
*pNumber = 99; // Re-assign value pointed to by pNumber
cout << pNumber << endl; // Print content of pNumber (0x22ccf0)
5|Page
cout << &number << endl; // Print address of number (0x22ccf0)
cout << *pNumber << endl; // Print value pointed to by pNumber (99)
cout << number << endl; // Print value of number (99)
// The value of number changes via pointer
cout << &pNumber << endl; // Print the address of pointer variable pNumber (0x22ccec)
}
Notes: The address values that you get are unlikely to be the same as mine. The OS loads the
program in available free memory locations, instead of fixed memory locations.
Null Pointers
You can initialize a pointer to 0 or NULL, i.e., it points to nothing. It is called a null pointer.
Dereferencing a null pointer (*p) causes an STATUS_ACCESS_VIOLATION exception.
int * iPtr = 0; // Declare an int pointer, and initialize the pointer to point to nothing
cout << *iPtr << endl; // ERROR! STATUS_ACCESS_VIOLATION exception
The meaning of symbol & is different in an expression and in a declaration. When it is used in an
expression, & denotes the address-of operator, which returns the address of a variable, e.g., if
number is an int variable, &number returns the address of the variable number.
However, when & is used in a declaration (including function formal parameters), it is part of
the type identifier and is used to declare a reference variable (or reference or alias or alternate
name). It is used to provide another name, or another reference, or alias to an existing variable.
For example,
/* Test reference declaration and initialization (TestReferenceDeclaration.cpp) */
#include <iostream.h>
int main()
{
int number = 88;
int & refNumber = number;
cout << number << endl; // Print value of variable number (88)
cout << refNumber << endl; // Print value of reference (88)
refNumber = 99; // Re-assign a new value to refNumber
cout << refNumber << endl;
cout << number << endl; // Value of number also changes (99)
7|Page
2.4. Dynamic Memory Allocation
Instead of define an int variable (int number), and assign the address of the variable to the int
pointer (int *pNumber = &number), the storage can be dynamically allocated at runtime, via a new
operator. In C++, whenever you allocate a piece of memory dynamically via new, you need to use
delete to remove the storage (i.e., to return the storage to the heap).
The new operation returns a pointer to the memory allocated. The delete operator takes a pointer
(pointing to the memory allocated via new) as its sole argument.
For example,
// Static allocation
int number = 88;
int * p1 = &number; // Assign a "valid" address into pointer
// Dynamic Allocation
int * p2; // Not initialize, points to somewhere which is invalid
cout << p2 << endl; // Print address before allocation
p2 = new int; // Dynamically allocate an int and assign its address to pointer
// The pointer gets a valid address with memory allocated
*p2 = 99;
cout << p2 << endl; // Print address after allocation
cout << *p2 << endl; // Print value point-to
8|Page
delete p2; // Remove the dynamically allocated storage
To initialize the allocated memory, you can use an initializer for fundamental types, or invoke a
constructor for an object. For example,
You can dynamically allocate storage for global pointers inside a function. Dynamically
allocated storage inside the function remains even after the function exits. For example,
#include <iostream.h>
void allocate() {
9|Page
}
int main() {
allocate();
delete p2;
return 0;
The main differences between static allocation and dynamic allocations are:
1. In static allocation, the compiler allocates and deallocates the storage automatically, and handles
memory management. Whereas in dynamic allocation, you, as the programmer, handle the
memory allocation and deallocation yourself (via new and delete operators). You have full control
on the pointer addresses and their contents, as well as memory management.
2. Static allocated entities are manipulated through named variables. Dynamic allocated entities are
handled through pointers.
Dynamic array is allocated at runtime rather than compile-time, via the new[] operator. To
remove the storage, you need to use the delete[] operator (instead of simply delete). For example,
10 | P a g e
// Assign random numbers between 0 and 99
for (int i = 0; i < SIZE; ++i) {
*(pArray + i) = rand() % 100;
}
// Print array
for (int i = 0; i < SIZE; ++i) {
cout << *(pArray + i) << " ";
}
cout << endl;
delete[] pArray; // Deallocate array via delete[] operator
return 0;
}
For example,
11 | P a g e
2.4. Pointer Arithmetic
As seen from the previous section, if numbers is an int array, it is treated as an int pointer pointing
to the first element of the array. (numbers + 1) points to the next int, instead of having the next
sequential address. Take note that an int typically has 4 bytes. That is (numbers + 1) increases the
address by 4, or sizeof(int). For example,
int numbers[100];
cout << sizeof(numbers) << endl; // Size of entire array in bytes (400)
cout << sizeof(numbers[0]) << endl; // Size of first element of the array in bytes (4)
cout << "Array size is " << sizeof(numbers) / sizeof(numbers[0]) << endl; // (100)
12 | P a g e