Last Updated:

Arrays in C++ : In best practice

As practice has shown, novice coders have many questions when solving problems on the topic "Arrays". This article addresses issues that apply only to arrays in the classical sense. Working with STL containers is a separate topic.

As a rule, the tasks are reduced to the following: fill the array, perform some operations with the elements of the array, print the result. Already in the formulation of the problem, the logical blocks of its solution are guessed. Next, I will try to show the typical "bricks" from which you can put together a solution to the problem - that is, the program.

Array organization

 

Memory for the array can be allocated automatically or dynamically.

Automatic memory allocation is used when the size of the array is known at compile time (that is, when writing code).

Dynamic memory allocation is used when the size of the array is unknown at compile time (for example, requested from the user).

Both types of arrays can be both global (defined outside functions) and local (defined inside a function or block). There is one subtlety here for automatic arrays. Memory for the local auto array is allocated on the stack. Therefore, the size of such an array should be small. Otherwise, you may get a stack overflow and, as a result, a program crash. Stack overflow can also be obtained with small local auto array sizes, with multiple recursive function calls. So when you define an automatic array in a function, you have to know exactly what you're doing.

Global automatic arrays are safe for stack overflow. But they will be visible in all code, lexicographically located after the declaration of arrays, which can provoke their use directly, bypassing their transfer to functions through parameters. This will lead to side effects of the functions, which makes debugging difficult and makes programs less reliable. This use of global arrays should be avoided.

For arrays that use dynamic memory allocation, memory is allocated from a heap. A heap is the memory allocated to a program by the operating system for use by that program. The heap size is typically much larger than the stack size, and for operating systems that support the virtual memory paradigm, the heap size can theoretically only be limited by the bit depth of the application.

Using Automatic Arrays

Automatic arrays are used when the size of the array is known at compile time.

It is strongly recommended that you specify the size of an array in your code by using a named constant. This is useful for several reasons:

  1. the name of the constant should indicate the scope of its application - self-documentation of the code;
  2. if you need to change the size of the array in the code, you will need to make an edit in only one place;
  3. the size of the array is usually used in array pass cycles, boundary checking, etc., so using a symbolic name will eliminate the need for careful checking and editing of all code when changing the size of the array.

The type of constant expression to determine the size (number of elements) of an automatic array must be integer: , , , , etc.charintunsigned intlong

The memory allocated for automatic arrays is freed when the array variable leaves the scope. For local arrays, this is a function or block. Global arrays are destroyed when you exit the program.

Example of defining a global automatic array with a length of 10 elements of type:int

const unsigned int ARRSIZE = 10;

int ary[ARRSIZE];

int main(void) { ... }

Example of defining a local automatic array with a length of 10 elements of type:int

const unsigned int ARRSIZE = 10;

int main(void) {
    int ary[ARRSIZE];
    ...
}

Using Arrays with Dynamic Memory Allocation

Dynamically allocated arrays are used when the size of the array is not known at compile time. The actual size of the array can be calculated in the program or entered by the user , it does not matter.

Memory for an array is allocated by an operator in the form .newnew type[number of elements]

The type of expression that determines the size (number of elements) of the array must be integer. Also, this expression can be constant.

When the array is finished, the memory allocated for the array must be freed. This is done by using an operator in the form . After the memory is freed, you cannot work with the array.deletedelete [] variable_name

An example of using an array with dynamic memory allocation:

int main(void) {

     unsigned int arr_size;

     // here should be getting the size of the array arr_size

     int *ary = new int[arr_size];

     // ...

     delete[]ary;

     // ...
}

Populate an array with values

 

When solving training problems, it is usually proposed to fill the array with values either entered from the keyboard or random values from a certain range. Let's start with the second case, as a simpler one (Paradox? No, the truth of life).

Populating an array with random numbers

 

First, you need a random number generator. The following is the code for one of the simplest implementations:

#include <cstdlib>

using namespace std;

// function to generate a random number from the range from range_min to range_max inclusive
int rrand(int range_min, int range_max) {
     return rand() % (range_max - range_min + 1) + range_min;
}

However, without additional body movements, the standard function will generate the same sequence of random numbers every time you start the program (by the way, this is very convenient when debugging!). In order to obtain a unique sequence of random numbers each time the program is launched, the function must be "overclocked" with the initial random value. This is done using functions and .rand()rand()srand()time()

Filling the array with values, of course, is done in a loop. Remember that array elements in C/C++ are numbered with 0. Therefore, the last element of the array has an index one less than the size of the array.

The example shows how to populate a global automatic array of 10 elements of type with random values from the range from −100 to 100 inclusive:int

#include <cstdlib>
#include <time.h>

using namespace std;

// function to generate a random number from the range from range_min to range_max inclusive
int rrand(int range_min, int range_max) {
     return rand() % (range_max - range_min + 1) + range_min;
}

const unsigned int ARRSIZE = 10;
const int ABSLIMIT = 100;

intary[ARRSIZE];

int main(void) {

     srand(static_cast<unsigned int>(time(NULL)));

     // array initialization with random values from the range -ABSLIMIT..ABSLIMIT
     for (unsigned int i = 0; i < ARRSIZE; i++) {
         ary[i] = rrand(-ABSLIMIT, ABSLIMIT);
     }
     return 0;
}

Pay attention to the inclusion of header files!

Populate an array with numbers entered by the user

Oddly enough, this is a more complicated case. The fact is that firstly, the presence of a person can always lead to incorrect data entry (errors), secondly, it is necessary to provide some kind of interface for a person, and thirdly, the STL streaming input-output system has its unpleasant features.

So, using the previous example, let's try to write a fragment responsible for entering array values from the keyboard. Let's add a header file to the beginning of the code, and instead of initializing the array with random values, let's write something like:#include <iostream>

for (unsigned int i = 0; i < ARRSIZE; i++) {
     cout << "Enter value for array element " << i << ": ";
     cin >> ary[i];
}

It seems to work, but if you try to enter 1111111111111111111111111111111111 or 11q as a number (of course, accidentally!), then, depending on the compiler, you will be able to observe some interesting effects of your program.

Therefore, you have to write more complex code:

// array initialization with input from cin
bool fail = false;
for (unsigned int i = 0; i < ARRSIZE; i++) {
     do {
         fail=false;
         cout << "Enter value for array element " << i << ": ";
         cin >> ary[i];
         if (cin.fail()) {
             cout << "*** Incorrect value entered. Please re-enter." <<endl;
             fail=true;
         }
         cin.clear();
         cin.ignore();
     } while (fail);

}

A detailed discussion of this passage is beyond the scope of this article. But those who are interested can disassemble it, armed, for example, with the famous book by G. Schildt.

Output values from an array to the console

Output of array values to the console is implemented elementarily. In light of the above, there is not even much to add:

// display in cout values of array elements
for (unsigned int i = 0; i < ARRSIZE; i++) {
     cout << "Array element value [" << i << "] = " << ary[i] << endl;
}

Working with values from an array

Everything that was written above was like an auxiliary element of the program. Next, let's look at some examples of array processing.

Finding the maximum/maximum value in an array

Below is the complete code of the program for finding the minimum value in the array and its index. The program uses a global automatic array. Array values are obtained by means of a random number generator.

#include <iostream>
#include <cstdlib>
#include <time.h>

using namespace std;

// function to generate a random number from the range from range_min to range_max inclusive
int rrand(int range_min, int range_max) {
    return rand() % (range_max - range_min + 1) + range_min;
}

const unsigned int ARRSIZE = 10;
const int ABSLIMIT = 100;

intary[ARRSIZE];

int main(void) {

    setlocale(LC_ALL, "English");

    srand(static_cast<unsigned int>(time(NULL)));

    // array initialization with random values ​​from the range -ABSLIMIT..ABSLIMIT
    for (unsigned int i = 0; i < ARRSIZE; i++) {
        ary[i] = rrand(-ABSLIMIT, ABSLIMIT);
    }

    // display in cout values ​​of array elements
    for (unsigned int i = 0; i < ARRSIZE; i++) {
        cout << "Array element value [" << i << "] = " << ary[i] << endl;
    }


    // search for the maximum value in the array and its index
    // if there are multiple minimum values, find the first one
    int min_val = ary[0];
    unsigned int min_idx = 0;
    for (unsigned int i = 1; i < ARRSIZE; i++) {
        if (min_val > ary[i]) {
            min_val = ary[i];
            min_idx = i;
        }
    }
    cout << "Minimum value " << min_val << ", element index " << min_idx << endl;

    system("pause");
    return 0;
}

This code can be optimized, but I did not do this, so that the very "bricks" from which it is assembled were better visible.

As can be seen from the comments, the last fragment of the program is responsible for finding the minimum value and its index.

Two variables are defined, one containing the minimum value and the other containing the index of the item with the minimum value. These variables are initialized by the first (zero) element of the array and zero, respectively. Further, in the loop, each subsequent value of the array element is compared with the already found smallest value and, if the current value is less than the remembered one, then the current value and its index are remembered.

It is clear that the search for the maximum value is carried out in a completely similar way, with an accuracy of the "more" / "less" signs, the output of the string to the user and the name of the variables.

Find a specific value in an array

 

The search for a certain value in an unordered array is carried out using a linear search algorithm. This simplest algorithm consists in sequentially iterating through the elements of the array and comparing them with the desired value.

Array lookup tasks can take two forms:

  1. find the first (last) occurrence of the desired value
  2. find all occurrences

Search for the first occurrence:

// search in the array for the first occurrence of a specific value
intpattern = 8; // lookup value
unsigned int i;
for (i = 0; i < ARRSIZE; i++) {
     if (ary[i] == pattern) {
         break;
     }
}
if (i >= ARRSIZE) {
     cout << "Element not found" << endl;
}
else {
     cout << "Index of first occurrence of lookup value: " << i << endl;
}

Search for the last occurrence:

// search the array for the last occurrence of a specific value
intpattern = 8; // lookup value
int i;
for (i = ARRSIZE - 1; i >= 0; i--) {
     if (ary[i] == pattern) {
         break;
     }
}
if (i < 0) {
     cout << "Element not found" << endl;
}
else {
     cout << "Index of last occurrence of lookup value: " << i << endl;
}  

Note the following:

  1. The loop variable is described before the loop. Thus, this variable continues to exist after the end of the loop, and its value can be used.i

  2. If the desired element is found, the loop ends ahead of schedule with the operator: it makes no sense to view the rest of the array - the task has already been completed.break

  3. In the second case, the variable has a signed type . The negative value is used as a flag that the entire array is viewed and the value is not found. iint

Search for all occurrences:

// search the array for all occurrences of a specific value
int pattern = 2; // lookup value
unsigned int i;
bool found = false;
for (i = 0; i < ARRSIZE; i++) {
     if (ary[i] == pattern) {
         cout << "Index of element found: " << i << endl;
         found = true;
     }
}
if (!found) {
     cout << "Element not found" << endl;
}

Here the cycle is not interrupted. The array is viewed in its entirety.

Sum/product of negative array elements

// Sum of negative array elements
long sum = 0;
for (unsigned int i = 0; i < ARRSIZE; i++) {
     if (ary[i] < 0) {
         sum += ary[i];
     }
}
cout << "The sum of the negative elements of the array is " << sum << endl;
// Product of negative array elements
long product = 1;
for (unsigned int i = 0; i < ARRSIZE; i++) {
     if (ary[i] < 0) {
         product *= ary[i];
     }
}
cout << "The product of the negative array elements is " << product << endl;

Sum of array elements with even/odd indexes

// Sum of array elements with even indexes
long sum = 0;
for (unsigned int i = 0; i < ARRSIZE; i += 2) {
     sum += ary[i];
}
cout << "The sum of even-indexed array elements is " << sum << endl;
// Sum of array elements with odd indices
long sum = 0;
for (unsigned int i = 1; i < ARRSIZE; i += 2) {
     sum += ary[i];
}
cout << "The sum of the array elements with odd indices is " << sum << endl;

Working with arrays using functions

Almost all the code fragments given above can be formatted as functions, and the array can be passed through parameters. As an example, I will give a program for finding the sum of the elements of an array with even indexes, in which a dynamic array is used (for the sake of variety).

The array is passed to the function as a pointer. And it does not matter what kind of array it is: automatic or an array with dynamic memory allocation. Also, it is usually necessary to pass the array size (number of elements) to the function, since in general, having only a pointer, it is impossible to determine the size of the array. (There are special cases where the size can be determined using a marker value; for example, strings in the C&#x2011;style must end with a null character.)

#include <iostream>
#include <cstdlib>
#include <time.h>

using namespace std;

// function to generate a random number from the range from range_min to range_max inclusive
int rrand(int range_min, int range_max) {
    return rand() % (range_max - range_min + 1) + range_min;
}

const unsigned int ARRSIZE = 12;
const int ABSLIMIT = 5;

void set_random_values(int *ar, unsigned int ar_size) {
    // array initialization with random values ​​from the range -ABSLIMIT..ABSLIMIT
    for (unsigned int i = 0; i < ar_size; i++) {
        ar[i] = rrand(-ABSLIMIT, ABSLIMIT);
    }
}

void print_values(int *ar, unsigned int ar_size) {
    for (unsigned int i = 0; i < ar_size; i++) {
        cout << "Array element value [" << i << "] = " << ar[i] << endl;
    }
}

long summ_even(int *ar, unsigned int ar_size) {
    // Sum of array elements with even indexes
    long sum = 0;
    for (unsigned int i = 0; i < ar_size; i += 2) {
        sum += ar[i];
    }
    returnsum;
}

int main(void) {

    setlocale(LC_ALL, "English");

    int *ary = new int[ARRSIZE]; // allocate memory for the array

    set_random_values(ary, ARRSIZE);
    print_values(ary, ARRSIZE);
    cout << "The sum of even-indexed array elements is " << summ_even(ary, ARRSIZE) << endl;

    delete[]ary; // deallocate memory

    system("pause");
    return 0;
}

Note that the allocation of memory for the array and its release occurs in one function (in this case, in . Allocating memory in one function and releasing memory in another is a bad idea fraught with errors.main()

Conclusion

This article discusses only the most basic techniques for working with arrays, which will help (I hope!) a novice coder to understand the principles of working with arrays.