 # ARRAYS AND POINTERS Although pointer types are not integer types, some integer arithmetic operators can be applied to pointers. The affect of this arithmetic.

## Presentation on theme: "ARRAYS AND POINTERS Although pointer types are not integer types, some integer arithmetic operators can be applied to pointers. The affect of this arithmetic."— Presentation transcript:

ARRAYS AND POINTERS Although pointer types are not integer types, some integer arithmetic operators can be applied to pointers. The affect of this arithmetic is to cause the pointer to point to another memory location. The actual change in address depends upon the size of the fundamental type to which the pointer points. Pointers can be incremented and decremented like integers. However, the increase or decrease in the pointer’s value is equal to the size of the object to which it points:

EX10 Traversing an Array with a Pointer
This example shows how a pointer can be used to traverse an array. int main() { const int SIZE = 3; short a[SIZE] = {22,33, 44}; cout << "a = " << a << endl; cout << "sizeof(short) = " << sizeof(short) << endl; short* end = a + SIZE; // converts SIZE to offset 6 short sum = 0; for (short* p = a; p < end; p++) { sum += *p; cout << "\t p = " << p; cout << "\t *p = " << *p; cout << "\t sum = " << sum << endl; } cout << "end = " << end << endl;

ARRAYS AND POINTERS Last example 10 shows that when a pointer is incremented, its value is increased by the number SIZE (in bytes) of the object to which it points. For example, float a; float* p = a; // p points to a ++p; // increases the value of p by sizeof(float) If floats occupy 4 bytes, then ++p; increases the value of p by 4, and p += 5; increases the value of p by 20. This is how an array can be traversed: by initializing a pointer to the first element of the array and then repeatedly incrementing the pointer. Each increment moves the pointer to the next element of the array. We can also use a pointer for direct access into the array. For example, we can access a by initializing the pointer to a and then adding 5 to it: p += 5; // now p points to a So once the pointer is initialized to the starting address of the array, it works like an index

ARRAYS AND POINTERS The array subscript operator [] is equivalent to the dereference operator *. They provide direct access into the array the same way: a == *a a == *(a + 1) a == *(a + 2), etc. So the array a could be traversed like this: for (int i = 0; i < 8; i++) cout << *(a + i) << endl;

ARRAYS AND POINTERS

Explanation of last example
The pattern matching algorithm uses two loops. The outer loop is controlled by the pointer p1 which points to elements in array a1 where the inner loop will begin checking for a match with array a2. The inner loop is controlled by the integer j which is used to compare corresponding elements of the two arrays. If a mismatch is found, the inner loop aborts and the outer loop continues by incrementing p1 to look for a match starting with the next element of a1. If the inner loop is allowed to finish, then the condition (j == n2) will be true and the current location pointed to by p1 is returned. The test driver verifies that the match has indeed been found by checking the actual addresses.

THE new OPERATOR When a pointer is declared like this:
float* p; // p is a pointer to a float it only allocates memory for the pointer itself. The value of the pointer will be some memory address, but the memory at that address is not yet allocated. This means that storage could already be in use by some other variable. In this case, p is uninitialized: it is not pointing to any allocated memory. Any attempt to access the memory to which it points will be an error: *p = ; // ERROR: no storage has been allocated for *P A good way to avoid this problem is to initialize pointers when they are declared: float x = ; // x contains the value float* p = &x; // p contains the address of x cout << *p; // OK: *p has been allocated In this case, accessing *p is no problem because the memory needed to store the float was automatically allocated when x was declared; p points to the same allocated memory. Another way to avoid the problem of a dangling pointer is to allocate memory explicitly for the pointer itself. This is done with the new operator: float* q; q = new float; // allocates storage for 1 float *q = ; // OK: *q has been allocated

THE new OPERATOR float* q = new float( ); cout << *q; // ok: both q and *q have been initialized In the unlikely event that there is not enough free memory to allocate a block of the required size, the new operator will return 0 (the NULL pointer): double* p = new double; if (p == 0) abort(); // allocator failed: insufficient memory else *p = ; This prudent code calls an abort() function to prevent dereferencing the NULL pointer. Consider again the two alternatives to allocating memory: float x = ; // allocates named memory float* p = new float( ); // allocates unnamed memory In the first case, memory is allocated at compile time to the named variable x. In the second case, memory is allocated at run time to an unnamed object that is accessible through *p.

THE delete OPERATOR The delete operator reverses the action of the new operator, returning allocated memory to the free store. It should only be applied to pointers that have been allocated explicitly by the new operator: float* q = new float( ); delete q; // deallocates q *q = ; // ERROR: q has been deallocated Deallocating q returns the block of sizeof(float) bytes to the free store, making it available for allocation to other objects. Once q has been deallocated, it should not be used again until after it has been reallocated. A deallocated pointer, also called a dangling pointer, is like an uninitialized pointer: it doesn’t point to anything.

DYNAMIC ARRAYS An array name is really just a constant pointer that is allocated at compile time: float a; // a is a const pointer to a block of 20 floats float* const p = new float; // so is p Here, both a and p are constant pointers to blocks of 20 floats. The declaration of a is called static binding because it is allocated at compile time; the symbol is bound to the allocated memory even if the array is never used while the program is running. In contrast, we can use a non-constant pointer to postpone the allocation of memory until the program is running. This is generally called run-time binding or dynamic binding: float* p = new float; An array that is declared this way is called a dynamic array. Compare the two ways of defining an array: float a; // static array float* p = new float; // dynamic array The static array a is created at compile time; its memory remains allocated throughout the run of the program. The dynamic array p is created at run time; its memory allocated only when its declaration executes. Furthermore, the memory allocated to the array p is deallocated as soon as the delete operator is invoked on it: delete [] p; // deallocates the array p Note that the subscript operator [] must be included this way, because p is an array.

EX15 Using Dynamic Arrays
void get(double*& a,int& n) { cout << "Enter number of items: "; cin >> n; a = new double[n]; cout << "Enter " << n << " items,one per line:\n"; for (int i = 0; i < n; i++) { cout << "\t" << i+1 << ": "; cin >> a[i]; } void print(double* a,int n) { for (int i = 0; i < n; i++) cout << a[i] << " "; cout << endl; int main() { double* a; // a is simply an unallocated pointer int n; get(a,n); // now a is an array of n doubles print(a,n); delete [] a; // now a is simply an unallocated pointer again }

EX15 Using Dynamic Arrays
Inside the get() function, the new operator allocates storage for n doubles after the value of n is obtained interactively. So the array is created “on the fly” while the program is running. Before get() is used to create another array for a, the current array has to be deallocated with the delete operator. Note that the subscript operator [] must be specified when deleting an array. Note that the array parameter a is a pointer that is passed by reference: void get(double*& a,i nt& n) This is necessary because the new operator will change the value of a which is the address of the first element of the newly allocated array.

ARRAYS OF POINTERS AND POINTERS TO ARRAYS
The elements of an array may be pointers. Here is an array of 4 pointers to type double: double* p; Its elements can allocated like any other pointer: p = new double( ); p = new double( ); We can visualize this array like this. The next example illustrates a useful application of pointer arrays. It shows how to sort a list indirectly by changing the pointers to the elements instead of moving the elements themselves

EX17 Indirect Bubble Sort
void sort(float* p[],int n) { float* temp; for (int i = 1; i < n; i++) for (int j = 0; j < n-i; j++) if (*p[j] > *p[j+1]) { temp = p[j]; p[j] = p[j+1]; p[j+1] = temp; } On each iteration of the inner loop, if the floats of adjacent pointers are out of order, then the pointers are swapped.

POINTERS TO POINTERS A pointer may point to another pointer. For example, char c = 't'; char* pc = &c; char** ppc = &pc; char*** pppc = &ppc; ***pppc = 'w'; // changes value of c to 'w' We can visualize these variables like this: The assignment ***pppc = 'w' refers to the contents of the address pc that is pointed to by the address ppc that is pointed to by the address pppc.

POINTERS TO FUNCTIONS Like an array name, a function name is actually a constant pointer. We can think of its value as the address of the code that implements the function. A pointer to a function is simply a pointer whose value is the address of the function name. Since that name is itself a pointer, a pointer to a function is just a pointer to a constant pointer. For example, int f(int); // declares function f int (*pf)(int); // declares function pointer pf pf = &f; // assigns address of f to pf We can visualize the function pointer like this: The value of function pointers is that they allow us to define functions of functions. This is done by passing a function pointer as a parameter to another function.

EX18 The Sum of a Function int sum(int (*)(int),int); int square(int);
int cube(int); int main() { cout << sum(square,4) << endl; // cout << sum(cube,4) << endl; // } The call sum(square,4) computes and returns the sum square(1) + square(2) + square(3) + square(4). Since square(k) computes and returns k*k, the sum() function returns = 30 int sum(int (*pf)(int k),int n) { // returns the sum f(0) + f(1) + f(2) f(n-1): int s = 0; for (int i = 1; i <= n; i++) s += (*pf)(i); return s; } int square(int k) { return k*k; int cube(int k) { return k*k*k;

Download ppt "ARRAYS AND POINTERS Although pointer types are not integer types, some integer arithmetic operators can be applied to pointers. The affect of this arithmetic."

Similar presentations