We have previously seen that a pointer simply stores an address and an address is simply a 32-bit integer. As such, what does it mean to add a number to an address? Program 1 deomonstrates this.
Program 1. Example of pointer addition.
#include <iostream> using namespace std; int main() { int n = 5; int * ptr = &n; cout << "The address of n is ptr = " << ptr << endl; cout << "However, we find ptr + 1 = " << ( ptr + 1 ) << endl; return 0; } |
If you run Program 1, you will find some interesting results. I get
The address of n is ptr = 0xffbefb24 However, we find ptr + 1 = 0xffbefb28
Thus, ptr + 1 is four greater than ptr. If you recall, the size of an integer is four bytes. Thus, you could deduce that if you had an array, if ptr stored the address of the first entry of the array, then ptr + 1 would be the address of the second entry. If the data type changes, then the jump is modified appropriately. For example, if we were to use double instead of int in Program 1 (try it) then the change in the address is 8, i.e., the size of a double.
The justification for this is that if you use a[n] to calculate the entry in an array, then this is simply translated into the statement *(a + n). This is one reason why C++ (and originally C) arrays begin at 0, as the first entry is accessed through *(a + 0) == *a.
However, in 1970, compilers were not very intelligent, and therefore code such as that seen in Program 2 would be slower than that shown in Program 3.
Program 2. Initializing the entries of an array with a[i].
#include <iostream> using namespace std; int main() { const int N = 100; int a[N]; for ( int i = 0; i < N; ++i ) { a[i] = 0; } return 0; } |
Program 3. Initializing the entries of an array with pointer arithmetic.
#include <iostream> using namespace std; int main() { const int N = 100; int a[N]; for ( int * ptr = a; ptr < a + N; ++ptr ) { *ptr = 0; } return 0; } |
If a pointer is declared to be void * (a pointer not associated with a type) then pointer arithmetic is forbidden, as it is impossible to know what the jump size should be.
Program 4 shows how pointer arithmetic can be used to print out the bytes of a double by using a pointer to a int.
Program 3. Initializing the entries of an array with pointer arithmetic.
#include <iostream> #include <iomanip> using namespace std; int main() { double x = 3.1415926535897932; int * ptr = reinterpret_cast<int *>( & x ); cout << "0x" << setbase(16) << setw( 8 ) << *ptr << *(ptr + 1) << endl; return 0; } |
Because the address has been recast as a pointer to an integer, when you add 1 to ptr, it adds 4 instead of 8. By passing setbase(16) to cout, this indicates that base 16 should be used to print all following integers. By passing setw( 8 ) to cout, this indicates that each integer should be printed with 8 (in this case, hexadecimal) digits.
1. Write a routine which prints a string by printing each character until it finds a \0.
2. Write a routine which determines the order in which a 32-bit integer (actually a word) is stored: big-endian or little endian. For example, in some machines, the 32-bit integer 13532510 = 2109D16 would be stored in the order:
This is called big endian because the most significant byte comes first. The other, little endian, would store this same integer as
that is, the least significant byte comes first. Not that within each byte, the bits are in the same order; it is the order of the bytes which is reversed. Think of little and least significant.
3. If you are not familiar with little endian versus big endian, one benefit is that, for example, if you are interested in taking any number, be it a char, int, short int, or long int, and you wanted the least significant byte (e.g., the number module 256) you only have to take the first byte. With big endian, you must first determine where the least significant byte is, based on the size of the type.
4. If you own a (PowerPC-based) Mac, compare your results with someone with a PC. Otherwise, if you own a PC, compare your results with someone who owns a Mac.
5. Because of the difference pointed out in Question 4, discuss the possiblity of religous wars waging over which is better: little endian or big endian.