Файл: Interfacing with C plus plus-programing communication with microcontrolers (K. Bentley, 2006).pdf

ВУЗ: Не указан

Категория: Не указан

Дисциплина: Не указана

Добавлен: 14.06.2025

Просмотров: 3594

Скачиваний: 1

ВНИМАНИЕ! Если данный файл нарушает Ваши авторские права, то обязательно сообщите нам.

7 DRIVING LEDS 159

for(int i = 0; i < 10; i++) a[i] = 0;

Alternatively, the array elements can be initialised when the array is declared. The values for each element to be initialised need to be listed within braces and separated by commas as shown in the following line:

int a[8] = {2,3,7,4}; // 8 elements

In this example the array a is partially filled with the values listed between the braces; element a[0]=2, a[1]=3, a[2]=7, and a[3]=4. The remaining elements (a[4] to a[7]) that have not been explicitly initialised are initialised by default with values of zero. Therefore, to declare and initialise an array with all zero values can be done as follows:

int a[8] = {};

Accessing array elements

Individual elements of the array can be accessed by using a subscript. In the following example the array subscripts range from 0 to 7 (8 elements).

int

a[8];

//

declare

a

to be 8 elements

int

Result;

//

declare

a

variable named Result

a[0] = 2; // access and set a[0] to 2 a[1] = 3; // access and set a[1] to 3

Result = a[0]*a[1];

// 2*3 = 6

Two-Dimensional Arrays

A two dimensional array can be viewed as an array of one-dimensional arrays. Two-dimensional arrays have two sizes specified. The total number of elements is the product of the two sizes. For example, a two-dimensional array can be declared as follows:

number of rows

int b[2][5];

number of elements per row

Figure 7-12 Declaring a 2-D array.

Two subscripts are used to access each element of the array. In array b, one of the array dimensions ranges from 0 to 1 and the other dimension from 0 to 4. The array can be thought of as the arrangement shown in Figure 7-13. Note that elements are stored in consecutive memory locations in row-major fashion. That is, the first row

160 7 DRIVING LEDS

is stored first, immediately followed by the second row, and so forth. Thus, the element b[0][4] is immediately followed by the element b[1][0].

b[0][0]

b[0][1]

b[0][2]

b[0][3]

b[0][4]

b[1][0]

b[1][1]

b[1][2]

b[1][3]

b[1][4]

Figure 7-13 2-D array schematic representation.

The array elements can be individually initialised during program execution by assigning each element a value. For example, the following code fragment sets the values of all elements to zero.

int i, j;

for(i = 0; i < 2; i++) for(j = 0; j < 5; j++)

b[i][j] = 0;

Alternatively the array elements can be initialised when the array is declared. This is shown in the following line.

int b[2][5] = {{0,0,0,0,0};{0,0,0,0,0}};

Each row of initialised elements is enclosed by inner braces, and separated from adjacent rows by a semicolon.

7.5 Pointers

A pointer is an address of an entity that resides in memory. Examples of entities that reside in memory are; class objects, fundamental data type objects such as int, float, char, long, etc., arrays of objects (a group of objects of the same type), and functions. A pointer in C++ will hold where the object is and most of the time the pointer will know the type of object. For example, a pointer to an integer knows that the data type is integer and it will also know where the integer is, but it does not know the value of the integer.

Pointers play an important role in helping to make C++ programs very efficient. There are three major uses for pointers that offer distinct advantages: passing large objects to functions, dynamic memory allocation, and using virtual functions. In the most common case when passing a parameter to a function, we replace the parameter by a copy of the actual argument. If the actual argument is very large, the program will need to consume a large amount of memory when it creates a copy of the argument. It is more efficient to make a copy of where the large object


7 DRIVING LEDS 161

is than copy the entire object. This is done by using a pointer which occupies a small amount of memory in order to store the address of the object.

Dynamic memory allocation involves the provision of storage space at run-time. Normally, dynamic memory allocation is a need-based process – i.e. if during program operation there is a need for more memory it can be requested and will be granted depending on availability. The dynamic memory allocation process returns a pointer indicating the location in memory where the allocation has been made. This pointer can then be used to manipulate the data in the allocated memory area.

Perhaps the most obscure use of pointers is in association with virtual functions which will be described in detail in Chapter 8. The following sections describe general use of pointers in the C++ language.

Two unary operators are used closely with pointers. As mentioned before, a unary operator takes only one argument. These operators are given in Table 7-1.

Table 7-1 Unary operators used with pointers.

Operator Name

&address of operator

*indirection operator

The address of operator can be used to find the address of an object in memory.

The indirection operator can be used to obtain the contents of a location given its memory address. This is also known as de-referencing.

7.5.1 Declaration of Pointer Variables

As you know, there is a dedicated data type named int to represent integers and many other fundamental data types. Programmers can also create their own data types such as DAC created in Chapter 6. However, there is no unique data type named pointer. Since all memory addresses are integers, all pointer data types carry integer values. The locations pointed to by these addresses can contain all types of data or functions.

The asterisk identifies the identifier as a pointer variable

data type *identifier;

To be replaced by a data type such as int, char or an object class type such as DAC

Figure 7-14 Syntax of a pointer declaration.

162 7 DRIVING LEDS

When declaring a pointer variable, the C++ language requires us to specify the type of data or function pointed to by the pointer variable. We will see the significance of knowing the data type pointed to by the pointer variables when pointer arithmetic is explained in section 7.5.6. In the simplest of cases, the syntax for declaring pointer variables takes the form shown in Figure 7-14. Pointers to different entities are each declared differently as described ahead.

7.5.2 Pointers to Scalar Quantities

A single item is referred to as a scalar quantity. If a pointer variable is declared to point to one solitary integer, then that pointer is said to point to a scalar quantity. This is in contrast to pointers that point to arrays. Examples of declarations of ordinary variables and declaration of pointers to scalar quantities are shown in Table 7-2.

Table 7-2 Declaration of scalar identifiers and pointers to scalar identifiers.

Declaration – scalar identifiers

Declaration – pointers to scalar identifiers

int a;

int a;

int *IntPtr = &a;

int b = 0;

int b

=

0;

*IntPtr

= b;

float p = 0.0;

float

p

= 0.0;

float

*FltPtr = &p

The following line is a combined declaration and initialisation of a pointer variable to an int:

int *IntPtr = &a;

Declaration part

int *IntPtr = &a;

Initialisation part

The same effect can be achieved with the following two lines:

int *IntPtr; IntPtr = &a;


7 DRIVING LEDS 163

The first statement declares a pointer to an int. The second statement uses the ‘address of’ operator ‘&’ to obtain the address of the integer variable a which is assigned to the pointer variable IntPtr. Note that the int type variable a must be declared before assigning its address to IntPtr.

The statement:

*IntPtr = b;

carries out a de-referencing and an assignment operation. The expression *IntPtr reads as ‘the contents of the location pointed to by IntPtr’. This is known as de-referencing. Therefore, the entire expression reads as ‘the contents of the location pointed to by IntPtr is assigned the value of b’. Since IntPtr already points to the location of a, the effect is same as:

a = b;

An example of declaring a pointer to a float type variable and assigning it a value is given in Table 7-2.

NOTE

Given the following two declarations;

int a=0;

float* FltPtr;

An assignment of the form;

FltPtr = &a; // Illegal!

is illegal. The pointer FltPtr is expected to carry an address of a float type

object. However, &a is an address of an integer object. These two do not match

and therefore it is an illegal assignment.

7.5.3 Pointers to Class Objects

Pointers to class objects are declared in a similar manner to pointers to scalar quantities. An example is given below:

ParallelPort *PortPtr;

Here the data type is ParallelPort and the pointer variable is PortPtr. A pointer to an object of the DAC class can be declared as follows:

DAC *DACPtr;

An object of type DAC can be declared as follows:

DAC Dac;


164 7 DRIVING LEDS

Then the following assignment is valid:

DACPtr = &Dac;

Membership Access Operators

If we use the object Dac, we can call the SendData() function as follows using the dot operator (.), also known as the membership access operator:

Dac.SendData(255);

We can also use a pointer variable such as DACPtr to call the SendData() function, although the syntax is different. In this case the membership pointer operator is used (->), formed by combining the minus sign (-) and the right angle bracket (>):

DACPtr->SendData(255);

Pointers to Base Class Objects can point to Objects of Derived Classes

This is one of the most useful and important concepts in object-oriented programming. In earlier sections it was explained that a pointer pointing to a float type variable cannot point to a location containing an int. This rule does not apply to base classes and derived classes. Although the two objects are different, a pointer to a base class object can point to an object of a derived class:

ParallelPort *PortPtr;

DAC Dac;

PortPtr = &Dac;// is allowed!

We are yet to discuss the advantages of using this type of pointer assignment. Its major use is associated with virtual functions and will be explained in Sections 8.5 and 8.6.

7.5.4 Pointers to Arrays

Pointers to One-Dimensional Arrays

When an array is declared to be equivalent to that shown in Figure 7-15, the address of the array will be a (no subscripts) which points to the first element of the array. Therefore, a and &a[0] are equivalent and both point to the first element. The important thing to note is that a is a pointer constant. It cannot be incremented, decremented or assigned any other values. Since the array has been stored in a specific memory space, the address value is fixed.

a[0] a[1] a[2] a[3] a[4] a[5] a[6] a[7] a[8] a[9]

Figure 7-15 Schematic of a one-dimensional array.

7 DRIVING LEDS 165

The following statements are all valid:

int a[10];

int *ElementPtr;

int b = 0;

// same as ElementPtr = &a[0];

ElementPtr = a;

*a = b;

// the value of b is deposited

// in a[0]

Some of the statements shown below are illegal:

int a[10]; float *FltPtr;

int b = 0;

// illegal – type mismatch

FltPtr = a;

a = &b;

// illegal – a is constant

Pointers to Two-Dimensional Arrays

As mentioned earlier, a two-dimensional array can be viewed as an array of onedimensional arrays. An example two-dimensional array can be declared as:

number of rows

int a[5][5];

number of elements per row

Recall that elements are stored in consecutive memory locations. For example, the element a[1][0] is stored next to a[0][4].

Unlike the case for one-dimensional arrays, the array name a is a pointer to the entire row starting at a[0][0] and ending at a[0][4]. The pointer a is still a constant.

a[0][0] a[0][1] a[0][2] a[0][3] a[0][4]

a[1][0] a[1][1] a[1][2] a[1][3] a[1][4]

.

.

.

a[4][0] a[4][1] a[4][2] a[4][3] a[4][4]

Figure 7-16 Schematic of a two-dimensional array.


166 7 DRIVING LEDS

A pointer to a row of five elements can be declared as follows:

int (*RowPtr)[5];

Note the subtle difference between the presence and absence of the parentheses in the above declaration. Compare this declaration to the declaration of an array of pointers discussed earlier.

If a is de-referenced, the result will be a pointer to the first element of the first row, i.e. &a[0][0]. This resulting pointer is still a constant. To access the value of a[0][0], the pointer a must be de-referenced twice. The following statements illustrate this:

int

*ElementPtr;

int

b;

int

a[5][5];

int

(*RowPtr)[5];

// pointer

to the

first row

RowPtr = a;

*a;

ElementPtr =

// pointer

to the

first element

b =

**a;

// of the first row

a;

// same as

b = *ElementPtr;

ElementPtr =

// Illegal

– type

mismatch

RowPtr = *a;

// Illegal

– type

mismatch

*a = &b;

// Illegal

- *a is constant

An important observation is that when an array name is de-referenced, it points to the next lower level entity. For example, if the name of a two-dimensional array is de-referenced, it will point to a one-dimensional array. If the name of a onedimensional array is de-referenced it will evaluate to be the contents of the first element of the array. Any further de-referencing is illegal.

7.5.5 Arrays of Pointers

It is also possible to declare arrays of pointers. In such an array, each element itself is also a pointer. An example of a pointer array declaration is given as follows:

int *IntPointers[20];

In this declaration, IntPointers is a constant pointer. It points to the first element of the array of pointers. If we use the de-referencing operator as shown below we will obtain the contents of the first element, which itself is a pointer to an int. Therefore it must be assigned to a compatible pointer variable. Consider the following declaration:

int a;

int *IntPtr;

int *IntPointers[20];

IntPointers is the start address of the array of pointers to int. In other words, it holds a memory address. This location contains a pointer to an int. Thus, the