• Nem Talált Eredményt

One-dimensional arrays

7. Arrays and strings

7.1. C++ array types

7.1.1. One-dimensional arrays

Definition of one-dimensional arrays:

element_typearray_name[size];

The element_type determining the type of the stored elements can be of any type with the exception of void and function types. The size provided between square brackets has to be a constant expression that compilers can calculate. The size defines the number of elements that can be stored in the array. Elements are indexed between 0 and (size-1).

I.15. ábra - Graphical representation of an one-dimensional array

As an example, let's have a look at an integer array having 7 elements. The integer type elements of the array will all have the value of the square of their indexes (I.15. ábra - Graphical representation of an one-dimensional array). It is a good solution to store the size of an array as a constant. According to what has been said so far, the definition of the array square is the following:

const int maxn =7;

int square[maxn];

In order to access the elements of the array in a linear order from its beginning to its end, we use in general a for loop, the variable of which stores the index of the array. (Normally, a correct for-loop runs from 0 to size minus one). Array elements are accessed by the indexing operator ([]).

for (int i = 0; i< maxn; i++) square[i] = i * i;

The size of memory in bytes allocated for the array square is returned by the expression sizeof (square), whereas the expression sizeof (square[0]) returns the size of one element. The number of elements of an array can therefore always be calculated by dividing the two elements by integer division:

int number_of_elements = sizeof(square) / sizeof(square[0]);

It should be noted that C++ carry out no check on array indexing. Trying to access an element at an index that is outside the array bounds can lead to runtime errors, and tracing back these errors can take too much time.

double october [31];

october [-1] = 0; // error ↯ october [31] = 0; // error ↯

In the following example, we calculate the average of float numbers read from the keyboard to an array of five elements, and then print out this average and the difference between each element and this average.

#include <iostream> cout<< endl << "The average is " << average << endl;

We should pay attention to how elements of an array are read from the keyboard. Array elements can only be accessed and set (from the keyboard) one by one.

numbers[0] = 12.23

7.1.1.1. Initializing and assigning values to one-dimensional arrays

C++ language makes possible to give an initial value to array elements. When arrays are defined, the equal sign is followed by the values of the initialization list that are enclosed within curly brackets. These values are associated with the indexes of the array in that order:

element_typearray_name[size] = { initialization list delimited by commas };

Let's see some examples how to initialize vectors:

int primes[10] = { 2, 3, 5, 7, 11, 13, 17, 19, 23, 27 };

char name[8] = { 'I', 'v', 'a', 'n'};

double numbers[] = { 1.23, 2.34, 3.45, 4.56, 5.67 };

In the case of the array primes, we made sure that the number of elements in the list is equal to the size of the array. If the list contained more initial values than needed, compilers would send us an error message.

In the second example, the initialisation list contains less elements than the size of the array. In that case, the first four elements of the array name will have the given values, while the others will be 0. If we benefit from this possibility, we can easily set 0 for all elements of any array by:

int big[2012] = {0};

In the last example, the compiler sets the number of elements of the array numbers based on the number of constants provided in the initialisation list (5). This is a good solution if the number of elements is often changed between different compilations. In that case, the number of the elements can be obtained by the above mentioned method:

double numbers[] = { 1.23, 2.34, 3.45, 4.56, 5.67 };

const int numbers = sizeof(numbers) / sizeof(numbers[0]);

Initialisation lists may contain expressions the values of which are calculated runtime:

double eh[3]= { sqrt(2.3), exp(1.2), sin(3.14159265/4) };

For a more rapid but less secure management of arrays, the Standard Library functions declared in the cstring header file and beginning with ”mem” can also be used. The function memset () makes possible to set all elements of a char array to the same character or to set all elements of an array of any type to bytes with value 0:

char line[80];

memset( line, '=', 80 );

double balance[365];

memset( balance, 0, 365*sizeof(double) );

//or

memset( balance, 0, sizeof(balance) );

The latest example gives rise to the question what operations can be used in C++ for arrays besides the indexing and the sizeof operators. The response is easy: nothing. The reason for that is that C/C++ languages treat array names as constant value pointers that are set by compilers. We benefitted from that when we called memset () since the first parameter of the function should be a pointer.

There are two methods for value assignment between two arrays of the same type and size. In the first case, elements are copied from one to another in a for loop, in the second one, the Library function memcpy () is called.

#include <iostream>

#include <cstring>

using namespace std;

int main() {

const int maxn = 8 ;

int source[maxn]= { 2, 10, 29, 7, 30, 11, 7, 12 };

int destination[maxn];

for (int i=0; i<maxn; i++) {

destination[i] = source[i]; // copying elements }

// or

memcpy(destination, source, sizeof(destination));

}

memcpy () does not always function correctly if there is an overlap between source and destination, for example if only one part of an array has to be copied to free space for a new element. In that case as well, there are two possibilities: a for loop or the memmove () Standard Library function. In the following example, a new element is intended to be inserted in the array ordered at the position with index 1:

#include <iostream>

#include <cstring>

using namespace std;

int main() {

const int maxn = 10 ;

int ordered[maxn]= { 2, 7, 12, 23, 29 };

for (int i=5; i>1; i--) {

ordered[i] = ordered[i-1]; // copying elements }

ordered[1] = 3;

// or

memmove(ordered+2, ordered+1, 4*sizeof(int));

ordered[1] = 3;

}

It should be noted that the address of the destination and source area is provided by using pointer arithmetic:

ordered+2, ordered+1.

7.1.1.2. One-dimensional arrays and the typedef

As we have already mentioned, the readability of program code is increased if more complex type names are replaced by synonymous names. For that reason, typedef should be used for derived types.

Let's look an example where we aim at calculating the vector multiplication of two vectors of 3 integer elements and to place the new vector in a third vector. The general definition of vector multiplication:

The arrays necessary to solve the problem can be created in two ways:

int a[3], b[3], c[3];

or

typedef int vector3[3];

vector3 a, b, c;

The vectors a and b are initialized with constants:

typedef int vector3[3];

vector3 a = {1, 0, 0}, b = {0, 1, 0}, c;

c[0] = a[1]*b[2] - a[2]*b[1];

c[1] = -(a[0]*b[2] - a[2]*b[0]);

c[2] = a[0]*b[1] - a[1]*b[0];

typedef is also useful to reference an array containing 12 elements of type double with a pointer. The first idea should be to type

double *xp[12];

to create the type. When interpreting type expressions, the priority table of operators is useful (Appendix 7.

szakasz - Precedence and associativity of C++ operations). On the basis of that, xp is an array of 12 elements and the array name is preceded by the type of its elements. That is why, xp is a pointer array with 12 elements.

The order of the interpretation can be modified by parentheses:

double (*xp)[12];

In that case, xp is a pointer and the type of the referenced data is double[12], that is a double array of 12 elements. Now we are done! However, it is more rapid and safe to use the keyword typedef:

typedef double dvect12[12];

dvect12 *xp;

double a[12];

xp = &a;

(*xp)[0]=12.3;

cout << a[0]; // 12.3