Chapter 2 - Pointers PDF
Document Details
Uploaded by SensationalRisingAction
Bule Hora University
Tags
Summary
This document describes pointers and how they're used in programming to denote memory locations. It also explains dynamic memory allocation and its differences from static memory allocation. The document includes examples.
Full Transcript
6/30/24, Chapter 2 - Pointers - 2.1.Pointers A pointer is simply the address of a memory location and provides an indirect way of accessing data in memory. A pointer variable is defined to ‘point to’ data of a specific type. For example...
6/30/24, Chapter 2 - Pointers - 2.1.Pointers A pointer is simply the address of a memory location and provides an indirect way of accessing data in memory. A pointer variable is defined to ‘point to’ data of a specific type. For example: int *ptr1; // pointer to an int char *ptr2; // pointer to a char The value of a pointer variable is the address to which it points. For example, given the definitions int num; we can write: ptr1 = # The symbol & is the address operator; it takes a variable as argument and returns the memory address of that variable. The effect of the above assignment is that the address of num is assigned to ptr1. Therefore, we say that ptr1 points to num. Figure 5.Error! Bookmark not defined. illustrates this diagrammatically. Figure: A simple integer pointer. Given that ptr1 points to num, the expression *ptr1 dereferences ptr1 to get to what it points to, and is therefore equivalent to num. The symbol * is the dereference operator; it takes a pointer as argument and returns the contents of the location to which it points. In general, the type of a pointer must match the type of the data it is set to point to. A pointer of type void*, however, will match any type. This is useful for defining pointers which may point to data of different types, or whose type is originally unknown. A pointer may be cast (type converted) to another type. For example, ptr2 = (char*) ptr1; converts ptr1 to char pointer before assigning it to ptr2. 1 BHU 1/10 6/30/24, Chapter 2 - Pointers Regardless of its type, a pointer may be assigned the value 0 (called the null pointer). The null pointer is used for initializing pointers, and for marking the end of pointer-based data structures (e.g., linked lists). 2.1.1. Dynamic Memory In addition to the program stack (which is used for storing global variables and stack frames for function calls), another memory area, called the heap, is provided. The heap is used for dynamically allocating memory blocks during program execution. As a result, it is also called dynamic memory. Similarly, the program stack is also called static memory. Two operators are used for allocating and deallocating memory blocks on the heap. The new operator takes a type as argument and allocated a memory block for an object of that type. It returns a pointer to the allocated block. For example, int *ptr = new int; char *str = new char; allocate, respectively, a block for storing a single integer and a block large enough for storing an array of 10 characters. Memory allocated from the heap does not obey the same scope rules as normal variables. For example, in void Foo (void) { char *str = new char; //... } when Foo returns, the local variable str is destroyed, but the memory block pointed to by str is not. The latter remains allocated until explicitly released by the programmer. The delete operator is used for releasing memory blocks allocated by new. It takes a pointer as argument and releases the memory block to which it points. For example: delete ptr; // delete an object delete [] str; // delete an array of objects 2 BHU 2/10 6/30/24 Chapter 2 - Pointers Note that when the block to be deleted is an array, an additional [] should be included to indicate this. Should delete be applied to a pointer which points to anything but a dynamically-allocated object (e.g., a variable on the stack), a serious runtime error may occur. It is harmless to apply delete to the 0 pointer. Dynamic objects are useful for creating data which last beyond the function call which creates them. Listing 5.1 illustrates this using a function which takes a string parameter and returns a copy of the string. Listing 5.1 1 #include 2 char* CopyOf (const char *str) 3 { 4 char *copy = new char[strlen(str) + 1]; 5 strcpy(copy, str); 6 return copy; 7 } Annotation (analysis) 1 This is the standard string header file which declares a variety of functions for manipulating strings. 4 The strlen function (declared in string.h) counts the characters in its string argument up to (but excluding) the final null character. Because the null character is not included in the count, we add 1 to the total and allocate an array of characters of that size. 5 The strcpy function (declared in string.h) copies its second argument to its first, character by character, including the final null character. Because of the limited memory resources, there is always the possibility that dynamic memory may be exhausted during program execution, especially when many large blocks are allocated and none released. Should new be unable to allocate a block of the requested size, it will return 0 instead. It is the responsibility of the programmer to deal with such possibilities. 2.1.2. Pointer Arithmetic In C++ one can add an integer quantity to or subtract an integer quantity from a pointer. This is frequently used by programmers and is called pointer arithmetic. Pointer 3 BHU 3/10 6/30/24 Chapter 2 - Pointers arithmetic is not the same as integer arithmetic, because the outcome depends on the size of the object pointed to. For example, suppose that an int is represented by 4 bytes. Now, given char *str = "HELLO"; int nums[] = {10, 20, 30, 40}; int *ptr = &nums; // pointer to first element str++ advances str by one char (i.e., one byte) so that it points to the second character of "HELLO", whereas ptr++ advances ptr by one int (i.e., four bytes) so that it points to the second element of nums. Figure 5.1 illustrates this diagrammatically. Figure 5.1 Pointer arithmetic. It follows, therefore, that the elements of "HELLO" can be referred to as *str, *(str + 1), *(str + 2), etc. Similarly, the elements of nums can be referred to as *ptr, *(ptr + 1), *(ptr + 2), and *(ptr + 3). Another form of pointer arithmetic allowed in C++ involves subtracting two pointers of the same type. For example: int *ptr1 = &nums; int *ptr2 = &nums; int n = ptr2 - ptr1; // n becomes 2 Pointer arithmetic is very handy when processing the elements of an array. Listing 5.2 shows as an example a string copying function similar to strcpy. Listing 5.2 1 void CopyString (char *dest, char *src) 2 { 3 while (*dest++ = *src++) 4 ; 5 } Annotation 3 The condition of this loop assigns the contents of src to the contents of dest and then increments both pointers. This condition becomes 0 when the final null character of src is copied to dest. 4 BHU 4/10 6/30/24 Chapter 2 - Pointers In turns out that an array variable (such as nums) is itself the address of the first element of the array it represents. Hence the elements of nums can also be referred to using pointer arithmetic on nums, that is, nums[i] is equivalent to *(nums + i). The difference between nums and ptr is that nums is a constant, so it cannot be made to point to anything else, whereas ptr is a variable and can be made to point to any other integer. Listing 5.3 shows how the HighestTemp function (shown earlier in Listing 5.Error! Bookmark not defined.) can be improved using pointer arithmetic. Listing 5.3 1 int HighestTemp (const int *temp, const int rows, const int columns) 2 { 3 int highest = 0; 4 for (register i = 0; i < rows; ++i) 5 for (register j = 0; j < columns; ++j) 6 if (*(temp + i * columns + j) > highest) 7 highest = *(temp + i * columns + j); 8 return highest; 9 } Annotation 1 Instead of passing an array to the function, we pass an int pointer and two additional parameters which specify the dimensions of the array. In this way, the function is not restricted to a specific array size. 6 The expression *(temp + i * columns + j) is equivalent to temp[i] [j] in the previous version of this function. HighestTemp can be simplified even further by treating temp as a one-dimensional array of row * column integers. This is shown in Listing 5.4. Listing 5.4 1 int HighestTemp (const int *temp, const int rows, const int columns) 2 { 3 int highest = 0; 4 for (register i = 0; i < rows * columns; ++i) 5 if (*(temp + i) > highest) 6 highest = *(temp + i); 7 return highest; 8 } 5 BHU 5/10 6/30/24 Chapter 2 - Pointers 2.1.3. Function Pointers It is possible to take the address of a function and store it in a function pointer. The pointer can then be used to indirectly call the function. For example, int (*Compare)(const char*, const char*); defines a function pointer named Compare which can hold the address of any function that takes two constant character pointers as arguments and returns an integer. The string comparison library function strcmp, for example, is such. Therefore: Compare = &strcmp; // Compare points to strcmp function The & operator is not necessary and can be omitted: Compare = strcmp; // Compare points to strcmp function Alternatively, the pointer can be defined and initialized at once: int (*Compare)(const char*, const char*) = strcmp; When a function address is assigned to a function pointer, the two types must match. The above definition is valid because strcmp has a matching function prototype: int strcmp(const char*, const char*); Given the above definition of Compare, strcmp can be either called directly, or indirectly via Compare. The following three calls are equivalent: strcmp("Tom", "Tim"); // direct call (*Compare)("Tom", "Tim"); // indirect call Compare("Tom", "Tim"); // indirect call (abbreviated) A common use of a function pointer is to pass it as an argument to another function; typically because the latter requires different versions of the former in different circumstances. A good example is a binary search function for searching through a sorted array of strings. This function may use a comparison function (such as strcmp) for comparing the search string against the array strings. This might not be appropriate for all cases. For example, strcmp is case-sensitive. If we wanted to do the search in a non-case-sensitive manner then a different comparison function would be needed. As shown in Listing 5.5, by making the comparison function a parameter of the search function, we can make the latter independent of the former. Listing 5.5 6 BHU 6/10 6/30/24 Chapter 2 - Pointers 1 int BinSearch (char *item, char *table[], int n, 2 int (*Compare)(const char*, const char*)) 3 { 4 int bot = 0; 5 int top = n - 1; 6 int mid, cmp; 7 while (bot