Outline
Three advanced pointer topics are discussed on this page:
Pointer Math
NOTE: PLEASE REVIEW POINTERS AND ARRAYS SECTION BEFORE READING THIS SECTION
Pointer arithmetic starts with understanding the size of data types. Although this will vary by architecture and platform, a 32-bit OSX format is below in Figure 1. To find the specific size of a datatype on your device use the sizeof() function.
Pointer arithmetic starts with understanding the size of data types. Although this will vary by architecture and platform, a 32-bit OSX format is below in Figure 1. To find the specific size of a datatype on your device use the sizeof() function.
The reason data type sizes are important to pointer arithmetic is because when you increment or decrement a pointer, it is actually doing this by a multiplier of the size of your data type.
For example, consider an integer array of 4 numbers 1, 2, 3, 4, and a pointer that points to the first element in the array. Let us say the pointer has a value of xFF00, which is 65280 in decimal. If this value was of any other datatype except for a pointer an increment by 1 would result in 0xFF01, or 65281. However, in terms of a pointer this operation would not make any sense, since it would grab the lower three bytes of the first integer and the upper byte of the second integer in the list when dereferencing the address. Therefore, when incrementing a pointer by one, it only makes sense to increment its value by the size of its data type. So incrementing the pointer by 1 would result in a value of 0xFF04, or 65284. This would correctly point to the next integer in the array.
Finally one must understand the order of operations when it comes to pointers. There is a very major difference between
*ptr++ and *(ptr++)
The first one will dereference the pointer and increment the value inside; while the second one will increment the memory address and then dereference that address. Knowing that the dereference operator comes before all arithmetic operators is key to understanding pointer arithmetic. Now that you know the fundamentals of pointer arithmetic, let’s do some practice examples.
For example, consider an integer array of 4 numbers 1, 2, 3, 4, and a pointer that points to the first element in the array. Let us say the pointer has a value of xFF00, which is 65280 in decimal. If this value was of any other datatype except for a pointer an increment by 1 would result in 0xFF01, or 65281. However, in terms of a pointer this operation would not make any sense, since it would grab the lower three bytes of the first integer and the upper byte of the second integer in the list when dereferencing the address. Therefore, when incrementing a pointer by one, it only makes sense to increment its value by the size of its data type. So incrementing the pointer by 1 would result in a value of 0xFF04, or 65284. This would correctly point to the next integer in the array.
Finally one must understand the order of operations when it comes to pointers. There is a very major difference between
*ptr++ and *(ptr++)
The first one will dereference the pointer and increment the value inside; while the second one will increment the memory address and then dereference that address. Knowing that the dereference operator comes before all arithmetic operators is key to understanding pointer arithmetic. Now that you know the fundamentals of pointer arithmetic, let’s do some practice examples.
Examples
int amounts = {200, 400, -50, 3000}; int *amountp = amounts; amounts[2] = *(amountp+2) + 100; // sets amounts[2] = 50 amounts[1] = *amountp-3 + 100; // sets amounts[1] = 297 *amountp++; // increments amounts[0] by 1 *(amountp++); // increments the amountp pointer, and then dereferences the new memory address
Pointer to a Pointer
Normally, we define a pointer as storing the address of a regular variable.
But we could just as easily define a pointer to store the address of another pointer (see code snippet and Figure 2).
Here, c is a pointer to a pointer. It stores the address of b. Dereferencing c once, *c, gives us the value of b. This is also the address of z. Dereferencing c twice, **c, gives the value of z. This is equal to dereferencing b once: *b. Creating a pointer that points to a pointer is referred to as multiple indirection.
Example 1
Below is a code snippet that demonstrates multiple indirection.
In Figure 3, the first set of print statements all print the value of x using increasing indirection. The second set prints the address of x, which is stored in ptrx and can be accessed by dereferencing pptrx (i.e. *pptrx). The third set prints the address of ptrx which is stored in pptrx. This code and a bit more are included in the download below.
Example 2
Just as the pointer can represent a 1-dimensional array of characters, a pointer to a pointer can represent an array of arrays of characters or an array of words. The following function shuffles the array of words stored in sent.
In Figure 4, the top is the original sentence and the bottom is the shuffled sentence. Below is a link to an example program using this function.
Dynamic memory allocation
Often, we want to allocate memory for an array, but don’t know how much space will be needed (i.e. the number of elements in the array depends on a variable). One way we can solve this is to define a very large array and use some number of elements of this array. Problems arise when we don’t want to use so much memory or if we incorrectly define the maximum number of elements we need. These problems can be addressed through dynamic memory allocation using pointers. To allocate memory, use the malloc() function, found in stdlib.h.
malloc() allocates a block of memory and returns the first address of the reserved memory. The return type is of type void* or a generic memory address and must be cast to the type of pointer it will be used as. For example:
malloc() allocates a block of memory and returns the first address of the reserved memory. The return type is of type void* or a generic memory address and must be cast to the type of pointer it will be used as. For example:
This allocates 4 bytes of memory for a character array. Below is a code snippet example that uses this and C strings to construct a string using malloc.
Example 1
The first printf prints the location in memory reserved for the 4 bytes. The second shows the result of assigning values to the allocated memory.
Chars are defined to be 1 byte, so allocating the correct amount of space is simple, but other data types have varying size depending on the system. To get the correct size of a data type, use the sizeof() operator.
sizeof(<type>) returns the size in bytes of 1 unit of that data type on the current system. Therefore, sizeof(int) returns the size in bytes that the system uses for an int type.
To allocate a n-element integer array called arr:
sizeof(<type>) returns the size in bytes of 1 unit of that data type on the current system. Therefore, sizeof(int) returns the size in bytes that the system uses for an int type.
To allocate a n-element integer array called arr:
Example 2
The following code segment creates an array based on user input and fills it with random integer values.
|
Looking for more discussion of pointers and their applications? Check out the additional resources page:
|