Chapter 3 - Pointers
Chapter 3 - Pointers
4.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 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.
ptr1 num
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.
1
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,
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,
Memory allocated from the heap does not obey the same scope rules as normal
variables. For example, in
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.
2
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:
Listing 5.1
1 #include <string.h>
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.
3
4.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 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[0]; // 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.
str ptr
str++ ptr++
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
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;
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.
Listing 5.4
1 int HighestTemp (const int *temp, const int rows, const int columns)
2 {
3 int highest = 0;
5
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
Listing 5.5
6
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;
Annotation
1 Binary search is a well-known algorithm for searching through a sorted
list of items. The search list is denoted by table which is an array of strings
of dimension n. The search item is denoted by item.
2 Compare is the function pointer to be used for comparing item against the
array elements.
7 Each time round this loop, the search span is reduced by half. This is
repeated until the two ends of the search span (denoted by bot and top)
collide, or until a match is found.
9 The item is compared against the middle item of the array.
10 If item matches the middle item, the latter’s index is returned.
11 If item is less than the middle item, then the search is restricted to the
lower half of the array.
14 If item is greater than the middle item, then the search is restricted to the
upper half of the array.
16 Returns -1 to indicate that there was no matching item.
The following example shows how BinSearch may be called with strcmp
passed as the comparison function:
char *cities[] = {"Boston", "London", "Sydney", "Tokyo"};
cout << BinSearch("Sydney", cities, 4, strcmp) << '\n';
4.1.4. References
A reference introduces an alias for an object. The notation for defining
references is similar to that of pointers, except that & is used instead of *. For
example,
7
double num1 = 3.14;
double &num2 = num1; // num is a reference to num1
defines num2 as a reference to num1. After this definition num1 and num2 both
refer to the same object, as if they were the same variable. It should be
emphasized that a reference does not create a copy of an object, but merely a
symbolic alias for it. Hence, after
num1 = 0.16;
You can also initialize a reference to a constant. In this case a copy of the
constant is made (after any necessary type conversion) and the reference is set
to refer to the copy.
int &n = 1; // n refers to a copy of 1
The 1 in the first and the 1 in the third line are likely to be the same object
(most compilers do constant optimization and allocate both 1’s in the same
memory location). So although we expect y to be 3, it could turn out to be 4.
However, by forcing x to be a copy of 1, the compiler guarantees that the object
denoted by x will be different from both 1’s.
The most common use of references is for function parameters. Reference
parameters facilitates the pass-by-reference style of arguments, as opposed to
the pass-by-value style which we have used so far. To observe the differences,
consider the three swap functions in Listing 5.6.
Listing 5.6
8
1 void Swap1 (int x, int y) // pass-by-value (objects)
2 {
3 int temp = x;
4 x = y;
5 y = temp;
6 }
4.1.5. Typedefs
Typedef is a syntactic facility for introducing symbolic names for data types.
Just as a reference defines an alias for an object, a typedef defines an alias for
9
a type. Its main use is to simplify otherwise complicated type declarations as
an aid to improved readability. Here are a few examples:
typedef char *String;
Typedef char Name[12];
typedef unsigned int uint;
The effect of these definitions is that String becomes an alias for char*, Name
becomes an alias for an array of 12 chars, and uint becomes an alias for
unsigned int. Therefore:
The typedef introduces Compare as a new type name for any function with the
given prototype. This makes BinSearch’s signature arguably simpler.
10