Class 5 - Pointers
The majority of bugs in C and C++ programs are related to dynamic memory errors caused by bad or invalid pointers to memory.
Pointers are a special data type. A pointer data type holds the memory address of the pointed to object. A pointer indirectly accesses an object through the memory address of the object.
int * p = NULL; // variable p is a pointer to an // int data type assigned to NULL int x = 7; // variable x is an int containing the number 7 p = &x; // p is assigned the memory address of x
p now points to x through the address of x
Conceptually: p(0x77AB) =====> x(7) at memory address 0x77AB
A pointer is dereferenced to access the
value of the pointed to object.
*p means we dereference the pointer p.
cout << x << endl; // outputs 7 to the screen cout << *p << endl; // outputs 7 to the screen
On the other hand, since *p accesses the space where x contains the data, *p as an Lvalue can assign new data to the pointed to object.
Assign the address pointed to by p to the
value 11
*p = 11;
cout << x <<
endl; // outputs 11 to the screen
cout << *p <<
endl; // outputs 11 to the screen
Pointers can be assigned the memory address of stack based objects. Stack based
objects are automatically allocated to memory upon the beginning of the objects scope and
automatically destroyed as the object drops out of scope. It is poor design which
assigned a pointer to stack or automatic variables.
void
Bad_Design ( int * p )
{
int x = 110;
p = &x;
}
After the function call the pointer p, points to memory which is no longer allocated to x or any other real object. This can cause the program to crash.
Pointers were intended to be used with dynamic memory. C provides the function calloc to create and initialize dynamic memory. Object allocated in dynamic memory are independent of scoping rules and as such these type of objects can be passed from function to function.
int * p = (int * ) calloc ( 1, sizeof(int) );
calloc returns an address to memory to NULL if there is no more memory available. There fore one must always test the return value from any dynamic memory allocation.
C++ introduces the function called "new". The primary difference between new and calloc is that new fires the constrictor of the object
Passing by reference is the act of passing by pointer or reference to a
function. In a 32 bit operating system the bandwidth of data across the function
interface is 4 bytes. This can be quite a performance enhancement over passing by
value. If a 1 megabyte data structure is passed by value over the function
interface, then 1 megabyte must be bit-wise copied over the interface. This copy
could take a considerable amount of time.
void Show ( int * p );
void Show ( int & pr );
Passing object by reference is the only means to effect the passed object by the called function. A function acting upon an object passed by value only effects the bit-wise copy, not the original object.
When a pointer is declared the pointers should ALWAYS be initialized to NULL;
NULL is a symbol declared with in stdlib.h and it's value may be operating system
dependent. Normally NULL is assigned to 0 (zero). In C++ NULL and 0 are valid
for pointer assignments.
When a pointer is declared, it may contain anything. So what would happen if the pointer was then tested against 0 (zero). The pointer would appear valid but the value of the pointer being "garbage" pointer to an invalid memory address.
int * p;
if ( p )
{
cout << *p << endl; // accessing a bad pointer address
}
Rule 1: Always
declare and assign pointer to NULL.
int * p = NULL;
Dynamic memory resides upon the memory heap. Objects created in dynamic memory
are independent of scope and live until explicitly destroyed by the programmer.
There is always a possibility that you may run out of system memory and the memory
allocating functions can return a NULL pointer. If memory is successfully created
the allocating function returns a positive number which is the address of the memory
allocated.
C++ uses the function "new" to create dynamic memory. New performs the following operations.
int * p = new int();
if ( !p)
{
cerr << "Error No: " << errno << endl
<< strerror(errno) << endl;
}
if ( p )
{
delete (p);
p = NULL;
}
Other forms of construction: int * p = new int(7);
You can also use the "C" memory allocation approach
int * cp = static_cast< int *>( calloc ( 1, sizeof (int) ));
if ( !cp )
{
cerr << "mem alloc error" << endl;
}
Rule 2: Always
test pointers from dynamic allocation.
Rule 3: Always test
pointers before "freeing" dynamic memory.
A pointer passed to a function must always be assumed to be NULL. This assumption
forces us to create safe functions.
Rule 4: When a pointer is passed to a function, always test the pointer against NULL.
void
Show ( int * p )
{
if ( p )
{
cout << "p = " << *p << endl;
}
}
Pointers to functions are commonly used as call back functions to GUI interface functions. If you typedef the function pointer type passing the functions become much simpler. The following example illustrates passing functions as pointers.
funcptr.cpp
When a pointer is incremented, the pointer is incremented by the sizeof the object pointed at in bytes.
int parray[10] = {0};
int * p = parray;
p++; // points to the second object in the array.
09/23/1998