I l @ ve RuBoard |
C++ allows pointer arithmetic. Addition and subtraction are allowed with pointers. Suppose you have the following: char array[10]; char *array_ptr = &array[0]; This is represented graphically in Figure 15-5. Figure 15-5. Pointers and an arrayIn this example, *array_ptr is the same as array[0] , *(array_ptr+1) is the same as array[1], and so on. Note the use of parentheses. (*array_ptr)+1 is not the same as array[1] . The +1 is outside the parentheses, so it is added after the dereference. Thus (*array_ptr)+1 is the same as array[0]+1 . At first glance this may seem like a complex way of representing simple array indices. You are starting with simple pointer arithmetic. In later chapters you will use more complex pointers to handle more difficult functions efficiently . Pointers are merely variables that contain memory addresses. In an array each element is assigned to consecutive addresses. For example, array[0] may be placed at address 0xff000024 . Then array[1] would be placed at address 0xff000025 and so on. Example 15-3 prints out the elements and addresses of a simple character array. (Note: The I/O manipulators hex and dec are described in Chapter 16. The reinterpret_cast is discussed later in this chapter.) Example 15-2. array-p/array-p.cpp#include <assert.h> #include <iostream> #include <iomanip> const int ARRAY_SIZE = 10; // Number of characters in array // Array to print char array[ARRAY_SIZE] = "012345678"; int main( ) { int index; /* Index into the array */ for (index = 0; index < ARRAY_SIZE; ++index) { std::cout << std::hex; // Trick to print hex numbers assert(index >= 0); assert(index < sizeof(array)/sizeof(array[0])); std::cout << "&array[index]=0x" << reinterpret_cast<int>(&array[index]) << " (array+index)=0x" << reinterpret_cast<int>(array+index) << " array[index]=0x" << static_cast<int>(array[index]) << '\n', std::cout << std::dec; // Another trick to go back to decimal } return (0); } When run, this program prints: &array[index]=0x20090 (array+index)=0x20090 array[index]=0x30 &array[index]=0x20091 (array+index)=0x20091 array[index]=0x31 &array[index]=0x20092 (array+index)=0x20092 array[index]=0x32 &array[index]=0x20093 (array+index)=0x20093 array[index]=0x33 &array[index]=0x20094 (array+index)=0x20094 array[index]=0x34 &array[index]=0x20095 (array+index)=0x20095 array[index]=0x35 &array[index]=0x20096 (array+index)=0x20096 array[index]=0x36 &array[index]=0x20097 (array+index)=0x20097 array[index]=0x37 &array[index]=0x20098 (array+index)=0x20098 array[index]=0x38 &array[index]=0x20099 (array+index)=0x20099 array[index]=0x0 Characters usually take up one byte, so the elements in a character array will be assigned consecutive addresses. A short int takes up two bytes, so in an array of short int s, the addresses increase by two. Does this mean short_array+1 will not work for anything other than characters? No. C++ automatically scales pointer arithmetic so it works correctly. In this case short_array+1 will point to element number 1. C++ provides a shorthand for dealing with arrays. Rather than write: array_ptr = &array[0]; you can write: array_ptr = array; C++ blurs the distinction between pointers and arrays by treating them the same in many cases. Here you used the variable array as a pointer, and C++ automatically did the necessary conversion. Example 15-3 counts the number of elements that are nonzero and stops when a zero is found. No limit check is provided, so there must be at least one zero in the array. Example 15-3. ptr2/ptr2a.cpp#include <assert.h> #include <iostream> int array[10] = {4, 5, 8, 9, 8, 1, 0, 1, 9, 3}; int the_index; int main( ) { the_index = 0; while (true) { assert(the_index >= 0); assert(the_index < sizeof(array)/sizeof(array[0])); if (array[the_index] == 0) break; ++the_index; } std::cout << "Number of elements before zero " << the_index << '\n'; return (0); } Rewriting this program to use pointers gives us Example 15-4. Example 15-4. ptr2/ptr2.cpp#include <iostream> int array[10] = {4, 5, 8, 9, 8, 1, 0, 1, 9, 3}; int *array_ptr; int main( ) { array_ptr = array; while ((*array_ptr) != 0) ++array_ptr; std::cout << "Number of elements before zero " << (array_ptr - array) << '\n'; return (0); }
The first program uses the expression (array[index] != 0) . This requires the compiler to generate an index operation, which takes longer than a simple pointer dereference: ((*array_ptr) != 0) . The expression at the end of this program, array_ptr -array , computes how far array_ptr is into the array. When passing an array to a procedure, C++ will automatically change the array into a pointer. In fact, if you put an & before the array, C++ will issue a warning. Example 15-5 illustrates array passing. Example 15-5. init-a/init-a.cpp#include <assert.h> const int MAX = 10; /******************************************************** * init_array_1 -- Zero out an array * * * * Parameters * * data -- the array to zero * ********************************************************/ void init_array_1(int data[]) { int index; for (index = 0; index < MAX; ++index) { assert(index >= 0); assert(index < MAX); data[index] = 0; } /******************************************************** * init_array_2 -- Zero out an array * * * * Parameters * * data_ptr -- pointer to array to zero * ********************************************************/ void init_array_2(int *data_ptr) { int index; for (index = 0; index < MAX; ++index) *(data_ptr + index) = 0; } int main( ) { int array[MAX]; // one way of initializing the array init_array_1(array); // another way of initializing the array init_array_1(&array[0]); // Similar to the first method but // function is different init_array_2(array); return (0); } 15.3.1 Splitting a C-Style StringSuppose you are given a C-style string (character array) of the form "Last/First". You want to split this into two strings, one containing the first name and one containing the last name . Example 15-6 reads in a single line, stripping the newline character from it. The function strchr is called to find the location of the slash (/). (The function strchr is actually a standard function. I have duplicated it for this example so you can see how it works.) At this point last_ptr points to the beginning character of the last name (with the first tacked on) and first_ptr points to a slash. You then split the string by replacing the slash (/) with an end-of-string (NUL or "\0"). Now last_ptr points to just the last name and first_ptr points to a null string. Moving first_ptr to the next character makes first_ptr point to the beginning of the first name. Graphically what you are doing is illustrated in Figure 15-6. Example 15-6 contains the full program. Figure 15-6. Splitting a stringExample 15-6. split/split.cpp/******************************************************** * split -- split a entry of the form Last/First * * into two parts. * ********************************************************/ #include <iostream> #include <cstring> #include <cstdlib> int main( ) { char line[80]; // The input line char *first_ptr; // pointer we set to point to the first name char *last_ptr; // pointer we set to point to the last name std::cin.getline(line, sizeof(line)); last_ptr = line; // last name is at beginning of line first_ptr = strchr(line, '/'); // Find slash // Check for an error if (first_ptr == NULL) { std::cerr << "Error: Unable to find slash in " << line << '\n'; exit (8); } *first_ptr = '/******************************************************** * split -- split a entry of the form Last/First * * into two parts . * ********************************************************/ #include <iostream> #include <cstring> #include <cstdlib> int main( ) { char line[80]; // The input line char *first_ptr; // pointer we set to point to the first name char *last_ptr; // pointer we set to point to the last name std::cin.getline(line, sizeof(line)); last_ptr = line; // last name is at beginning of line first_ptr = strchr(line, '/'); // Find slash // Check for an error if (first_ptr == NULL) { std::cerr << "Error: Unable to find slash in " << line << '\n'; exit (8); } *first_ptr = '\0'; // Zero out the slash ++first_ptr; // Move to first character of name std::cout << "First:" << first_ptr << " Last:" << last_ptr << '\n'; return (0); } /******************************************************** * strchr -- find a character in a string * * Duplicate of a standard library function, * * put here for illustrative purposes. * * * * Parameters * * string_ptr -- string to look through * * find -- character to find * * * * Returns * * pointer to 1st occurrence of character in string* * or NULL for error * ********************************************************/ char *strchr(char * string_ptr, char find) { while (*string_ptr != find) { // Check for end if (*string_ptr == '\0') return (NULL); // not found ++string_ptr; } return (string_ptr); // Found }'; // Zero out the slash ++first_ptr; // Move to first character of name std::cout << "First:" << first_ptr << " Last:" << last_ptr << '\n'; return (0); } /******************************************************** * strchr -- find a character in a string * * Duplicate of a standard library function, * * put here for illustrative purposes. * * * * Parameters * * string_ptr -- string to look through * * find -- character to find * * * * Returns * * pointer to 1st occurrence of character in string* * or NULL for error * ********************************************************/ char *strchr(char * string_ptr, char find) { while (*string_ptr != find) { // Check for end if (*string_ptr == '/******************************************************** * split -- split a entry of the form Last/First * * into two parts . * ********************************************************/ #include <iostream> #include <cstring> #include <cstdlib> int main( ) { char line[80]; // The input line char *first_ptr; // pointer we set to point to the first name char *last_ptr; // pointer we set to point to the last name std::cin.getline(line, sizeof(line)); last_ptr = line; // last name is at beginning of line first_ptr = strchr(line, '/'); // Find slash // Check for an error if (first_ptr == NULL) { std::cerr << "Error: Unable to find slash in " << line << '\n'; exit (8); } *first_ptr = '\0'; // Zero out the slash ++first_ptr; // Move to first character of name std::cout << "First:" << first_ptr << " Last:" << last_ptr << '\n'; return (0); } /******************************************************** * strchr -- find a character in a string * * Duplicate of a standard library function, * * put here for illustrative purposes. * * * * Parameters * * string_ptr -- string to look through * * find -- character to find * * * * Returns * * pointer to 1st occurrence of character in string* * or NULL for error * ********************************************************/ char *strchr(char * string_ptr, char find) { while (*string_ptr != find) { // Check for end if (*string_ptr == '\0') return (NULL); // not found ++string_ptr; } return (string_ptr); // Found }') return (NULL); // not found ++string_ptr; } return (string_ptr); // Found } This program illustrates how pointers and character arrays may be used for simple string processing. Question 15-1: Example 15-7 is supposed to print out: Name: tmp1 but instead you get: Name: !_@$#ds80 (Your results may vary.) Why does this happen? Would this happen if we used the C++ string class instead of the old C-style strings? Example 15-7. tmp-name/tmp-name.cpp#include <iostream> #include <cstring> /******************************************************** * tmp_name -- return a temporary file name * * * * Each time this function is called, a new name will * * be returned. * * * * Returns * * Pointer to the new file name. * ********************************************************/ char *tmp_name( ) { char name[30]; // The name we are generating static int sequence = 0; // Sequence number for last digit ++sequence; // Move to the next file name strcpy(name, "tmp"); // Put in the sequence digit name[3] = static_cast<char>(sequence + '0'); // End the string name[4] = '#include <iostream> #include <cstring> /******************************************************** * tmp_name -- return a temporary file name * * * * Each time this function is called, a new name will * * be returned. * * * * Returns * * Pointer to the new file name. * ********************************************************/ char *tmp_name( ) { char name[30]; // The name we are generating static int sequence = 0; // Sequence number for last digit ++sequence; // Move to the next file name strcpy (name, "tmp"); // Put in the sequence digit name[3] = static_cast<char>(sequence + '0'); // End the string name[4] = '\0'; return(name); } int main( ) { std::cout << "Name: " << tmp_name( ) << '\n'; return(0); }'; return(name); } int main( ) { std::cout << "Name: " << tmp_name( ) << '\n'; return(0); } |
I l @ ve RuBoard |