Файл: Interfacing with C plus plus-programing communication with microcontrolers (K. Bentley, 2006).pdf
ВУЗ: Не указан
Категория: Не указан
Дисциплина: Не указана
Добавлен: 14.06.2025
Просмотров: 3622
Скачиваний: 1
7 DRIVING LEDS 167
contents of any element of the array can be assigned to a pointer to an int, such as IntPtr. To obtain the contents of the first element of the IntPointers array, we can de-reference IntPointers. Once de-referenced, it can be assigned to IntPtr as shown in the line below:
IntPtr = *IntPointers; // contents of 1st element
A pointer to an int is stored at the address obtained by de-referencing IntPointers (i.e. *IntPointers). If we need the contents of the location pointed to by this pointer to int, we must de-reference *IntPointers once more to obtain the integer value. Such an integer value can be assigned to an int type variable such as a. Thus:
a = **IntPointers; |
// Same as a = *IntPtr; |
7.5.6 Pointer Arithmetic
As already seen, a pointer variable can be incremented or decremented. Likewise an integer value can be added or subtracted. However, the results produced by pointer arithmetic are different to the results produced by normal arithmetic. Before explaining this further, we should understand where pointer arithmetic is useful.
One-dimensional Arrays
Pointer arithmetic is especially useful for accessing array elements. Consider the example:
int a[5];
Here we have declared an array of 5 integers. The array name a is a constant pointer and it points to the first element of the array. Figure 7-17 shows an example of such an array in memory.
Memory |
Array Element |
||
Address |
and its value |
||
600000 |
|||
a[0] is 4 |
|||
600002 |
a[1] is 9 |
||
600004 |
a[2] is 8 |
||
600006 |
a[3] is 3 |
||
600008 |
a[4] is 5 |
||
Figure 7-17 An example of 5 integers in memory.
168 7 DRIVING LEDS
In the example shown in Figure 7-17, the memory address values change by 2 when we move from one address to the next address. This is because we have assumed each integer occupies two bytes. Thus, the element a[0] occupies the addresses 600000 and 600001. The next element a[1] begins at 600002 and so on.
The value of a is 600000. The pointer a is not a variable, it is a constant. It points to the first element of the array and therefore cannot be assigned another value. It can be de-referenced like other pointers as follows:
*a |
a[0] which is 4 |
|
Although a cannot be changed, we can add an integer value to a to obtain a new value. Thus:
a+1 is a valid expression.
If we interpret this result using normal arithmetic, it evaluates to 600001. However, in pointer arithmetic it evaluates to 600002. Since a is a pointer to an integer, ‘+1’ really means plus one int type data, which is two bytes in our example. The compiler takes into account the size of the data type pointed to by the pointer when evaluating pointer arithmetic expressions. The result a+1 still remains a pointer and points to the next element of the array:
a+1 600002
a+2 600004
a+3 600006
Just like the pointer a, the following three pointers can also be de-referenced:
*(a+1) |
a[1] which is 9 |
|
*(a+2) |
a[2] which is 8 |
|
*(a+3) |
a[3] which is 3 |
|
Use of parentheses is very important in the cases shown above. If the parentheses had not been used, the results would be as follows:
*a+1 |
a[0]+1 which is 5 |
|
*a+2 |
a[0]+2 which is 6 |
|
*a+3 |
a[0]+3 which is 7 |
|
As can be seen from this discussion, pointer arithmetic can be used to access an array element of a one-dimensional array. For the array a, the ith element can be accessed by:
*(a+i)
7 DRIVING LEDS 169
Two-dimensional arrays
Pointer arithmetic is applied slightly differently to two-dimensional arrays. Consider the example:
int a[3][2];
This declares an array of 6 elements stored in sequential memory as shown in Figure 7-18.
Memory |
Array Element |
|||||
Address |
and its value |
|||||
600000 |
||||||
0 |
1 |
a[0][0] is |
6 |
|||
600002 |
a[0][1] is |
7 |
||||
0 |
6 |
7 |
||||
600004 |
a[1][0] is 55 |
|||||
1 |
55 |
9 |
||||
600006 |
a[1][1] is |
9 |
||||
2 |
3 |
33 |
||||
600008 |
a[2][0] is |
3 |
||||
Array a[3][2] |
600010 |
|||||
a[2][1] is 33 |
||||||
Figure 7-18 Two-dimensional array in memory.
As for the case of one-dimensional arrays, the array name a is still a pointer. Its value is 600000. It is a constant and cannot be changed. The difference for twodimensional arrays is that a points to the first row of elements, not the first element of the array. Therefore, it represents a data size of 2 integers as specified by the second subscript of the array declaration. As a result the pointer a represents 2 integers, i.e. points to 4 bytes of memory. With this in mind, pointer arithmetic works as follows.
a+1 |
600004 |
points to the second row |
|
a+2 |
600008 |
points to the third row |
|
Using this notation, a pointer to the ith row can be obtained by adding i to a:
a+i |
points to the ith row |
|
Like any other pointer, these pointers can also be de-referenced. When these pointers are de-referenced, the result is still a pointer:
170 7 DRIVING LEDS
*(a+1) |
600004 |
points to the first element of the second row |
|
*(a+2) |
600008 |
points to the first element of the third row |
|
*(a+i) |
points to the first element of the (i+1)th row |
||
The size of data pointed to by these de-referenced pointers is no longer an entire row. They now point to elements, which are single integers. Now pointer arithmetic will be based on single integers or two bytes. Thus:
*(a+1) |
+ |
1 |
600006 |
points to a[1][1] |
|
*(a+2) |
+ |
1 |
600010 |
points to a[2][1] |
|
Therefore, the pointer that points to the jth element of the ith row is:
*(a+i) + j |
points to a[i][j] |
|
By de-referencing the above pointers, the values of the elements can be accessed:
*(*(a+1) + 1) |
a[1][1] which is 9 |
|
*(*(a+i) + j) |
a[i][j] |
|
The same arguments we presented for two-dimensional arrays can be extended in the same logical manner to higher-dimensional arrays.
7.5.7 Pointers to Functions
Pointers can be declared to point to functions. Just like all the pointers discussed previously, function pointers will also have an address in memory. These addresses point to the first instruction to be executed.
An example of a function pointer declaration is:
int (*CalcFunctionPtr)(int,int);
In the above example, the name of the function pointer is CalcFunctionPtr. This pointer can only point to a particular category of function as specified by the pointer declaration. The declaration specifies that the function to be pointed to by CalcFunctionPtr must receive two integer parameters and must return an integer value. An example of a function that can be pointed to by
CalcFunctionPtr is:
int Add(int a, int b)
{
return (a + b);
}
Another function that can be pointed to by the CalcFunctionPtr is:
int Sub(int a, int b)
{
7 DRIVING LEDS 171
return (a – b);
}
In a similar manner to arrays, where the array name is a constant pointer, function names are also constant pointers for the simple reason that when a program is running the location of a function in memory is fixed.
In the above two functions, Add and Sub are two constant function pointers. Each of them point to the first instruction to be executed in their respective functions. These constant pointer values can be assigned to a declared pointer variable. An example program is given in Listing 7-1.
Listing 7-1 Use of pointers to functions.
#include <iostream.h> #include <conio.h>
int Add(int a, int b)
{
return (a + b);
}
int Sub(int a, int b)
{
return (a - b);
}
void main()
{
int a, b, Result; char key;
int (*CalcFunctionPtr)(int,int);
cout << "Enter two integer values " << endl; cin >> a >> b;
cout << "Press '+' or '-' key" << endl;
key = getch(); // getch() reads the key pressed
switch(key)
{
case '+' : CalcFunctionPtr = Add; break;
case '-' : CalcFunctionPtr = Sub;
}
172 7 DRIVING LEDS
Result = CalcFunctionPtr(a,b);
cout << "The result is " << Result << endl;
}
If the ‘+’ key is pressed, the switch statement will set the CalcFunctionPtr to point to the Add() function. If the ‘-‘ key is pressed, CalcFunctionPtr will be set to point to the Sub() function. The second-last line of the code fragment will execute either the Add() function or the Sub() function depending on the key pressed. This is an example where a function pointer is used to carry out different tasks using the same statement for different cases (Result = CalcFunctionPtr(a,b)).
In a more complex program you may have a large portion of the program written using the function pointer variable. If we need to change the cases, we do not need to change the part of the program that calculates the result. The program in Listing 7-2 shows how we can add another case for multiplication and yet the result will be calculated using the same statement as for Listing 7-1; ‘Result =
CalcFunctionPtr(a,b);’.
Listing 7-2 Adding more functionality to the program in Listing 7-1.
#include <iostream.h> #include <conio.h>
int Add(int a, int b)
{
return (a + b);
}
int Sub(int a, int b)
{
return (a - b);
}
int Mult(int a, int b)
{
return (a * b);
}
void main()
{
int a, b, Result; char key;
int (*CalcFunctionPtr)(int,int);
7 DRIVING LEDS 173
cout << "Enter two integer values " << endl; cin >> a >> b;
cout << "Press '+', '-' or '*' key" << endl;
key = getch(); // getch() reads the key pressed
switch(key)
{
case '+' : CalcFunctionPtr = Add; break;
case '-' : CalcFunctionPtr = Sub; break;
case '*' : CalcFunctionPtr = Mult;
}
Result = CalcFunctionPtr(a,b);
cout << "The result is " << Result << endl;
}
Functions Returning Pointers
Functions returning pointers are discussed here since their declarations are similar. A function with the name AnyFunction that receives two int type parameters and returns a pointer to an int is declared as follows:
int *AnyFunction(int,int);
Compare this declaration with the declaration of FunctionPtr, which is a pointer to a function taking two int type parameters and returning an int type value:
int (*FunctionPtr)(int,int);
In one case a pair of parentheses is present and in the other case the parentheses are omitted. Although only slightly different, these two declarations are completely different. AnyFunction is a function name and therefore is a constant pointer. FunctionPtr is a pointer variable.
7.5.8 Pointers to void
Pointers can also be declared to be of type ‘pointer to void’. These pointers do not have any restrictions as to the type of data or functions they can point to. The following example outlines their use.
int |
a; |
// |
declaration |
of |
an int |
|||
float b; |
// |
declaration |
of |
a float |
||||
void *VoidPtr; |
// |
declaration |
of a void pointer |
|||||
int |
Add(int,int); |
// |
declaration |
of a function |
||||