Массивы

Основные сведения об одномерных массивах

До сих пор мы рассматривали примеры работы с небольшим количеством данных, для работы с которыми достаточно использовать только простые переменные. Если данных много, то использование одних простых переменных становится явно недостаточным. Гораздо удобнее объединять их в более сложную организацию данных, например, в массивы.

Массив — это совокупность данных одного типа, имеющая одно общее имя. Доступ к отдельному элементу массива осуществляется путем указания имени массива и номера (индекса) этого элемента. До использования массива его необходимо определить, т.е. дать ему имя и указать количество и тип элементов. Индексы в любом массиве на языке C/C++ всегда начинается с 0.

Пусть имеется массив из 5 действительных чисел. Графически этот массив можно изобразить так, как показано на рисунке ниже:


Для того, чтобы начать работать с массивом, необходимо дать его описание. Формальная запись описания массива такова:

Тип_элементов_массива Имя_массива[Количество_элементов];

где

Для нашего массива x описание будет выглядеть так:

double x[5];

После этого можно начинать работать с элементами массива, например, присвоить второму элементу новое значение:

x[2] = 5;

или напечатать сумму значений первых двух элементов массива:

cout << x[0]+x[1] << endl;

Как нетрудно убедиться, элементы массива могут использоваться точно так же, как и простые переменные. Единственное отличие — требуется указывать индексы элементов.

Индекс (номер) элемента массива — это константа, переменная или более сложное выражение целого типа. Можно было бы написать:

int i = 2;

x[i*3-2] = x[i+1] + x[i%5] - x[3];

главное, чтобы индексы (или в более сложном случае — индексные выражения) находились в допустимых пределах. Индексы элементов нашего массива x должны лежать в пределах от 0 до 4, т.е. максимально допустимый индекс на единицу меньше количества элементов массива.

А что произойдёт, если обратиться к элементу с индексом, значение которого выходит за допустимый диапазон? Для нашего примера, запишем так:

x[5] = 12;

Внешних проявлений (например, сообщений об ошибке), как правило, не будет, просто программа обратится к «чужому» участку памяти и «испортит» значение какой-либо переменной.

Выход за допустимые пределы индексов массива — это характерная ошибка в программах на языках С/С++. К сожалению, компилятор тут не помощник — он ни как не контролирует выход за допустимые пределы. Поэтому программисту самому необходимо проявлять повышенную внимательность и аккуратность в работе с массивами. При работе с программой, в которой используются массивы, всегда задавайте себе вопрос: «Не выходит ли где-то индекс массива за допустимые пределы?».

Вернёмся снова к описанию массива. Хороша ли запись

double x[5];

Ответ один — нет, плоха. Почему? Да потому, что при работе с массивами мы постоянно используем просмотр массива, т.е. нам для нашего массива из 5 чисел придётся использовать циклы вида

for(i=0; i < 5; i++) { тело цикла }

А что делать, если количество чисел изменится? Просматривать текст всей программы и «править» все циклы? Глупо как-то, да и ошибиться легко. Нужен какой-то выход, и он есть: количество элементов массива нужно задавать не константой (числом), а именованной константой. Тогда количество элементов массива потребуется изменить только в одном месте, а остальной текст программы останется прежним.

На языке С был только один способ создания константы с именем: с помощью директивы define. Схема программы будет выглядеть так:

#define n 5

// Другие директивы

int main()

{

   double x[n]; // описание массива

   int i; // переменная цикла

   .......................

   // использование массива

   for(i=0; i<n; i++)

   { тело цикла }

   .........................

}

Одна проблема решена, но есть и другая, также весьма неприятная. Что произойдёт, если в директиве define пользователь задаст неверное по смыслу значение для константы, например, другого типа:

#define n 3.14

или наберёт просто какой-то абсурд:

#define n ЮЮЮ

В этом случае препроцессор «честно» выполнит подстановки, и после компиляции (а в ряде случаев — в ходе выполнения исполняемого модуля) в программе появится сообщение об ошибке, но ссылка будет не на строку, где допущена ошибка (директива define), а гораздо дальше — там где она используется, — возможно через десятки или сотни строк. Если для приведённой выше схемы написать

#define n 3.14

то ошибка будет указана в строке

double x[n];

которая совсем не причём.

Для радикального решения проблемы на языке С++ появился более надёжный способ создания константы с именем: с помощью константной переменной. Схема программы будет выглядеть так:

// директивы

int main()

{

   const int n = 5;

   double x[n]; // описание массива

   int i; // переменная цикла

   .......................

   // использование массива

   for(i=0; i<n; i++)

   { тело цикла }

   .........................

}

Достоинство такого подхода в том, что теперь компилятор проверяет правильность оператора, в котором мы задаём размер массива:

const int n = 5;

Если в этом операторе переменной n будет задано что-то неприемлемое:

const int n = ЮЮЮ

то сообщение об ошибке будет дано именно для этого оператора, а не где-то ниже по тексту.

Для закрепления материала рассмотрим задачу.

Пример. Дан массив действительных чисел из n элементов. Найти максимальный по модулю элемент и разделить все элементы массива на полученное значение. Вывести на экран монитора массив после обработки.

Возможный текст программы:

#include <iostream>

using namespace std;

#include <cmath>

int main()

{

   const int n=5;

   double x[n];

   double max;

   int i;

   // Задаём массив из n действительных чисел

   cout << "Input " << n << " numbers:" << endl;

   for(i = 0; i < n; i++)

      cin >> x[i];


   // Поиск максимального по модулю элемента массива


   // Предположим, что x[0] - это и есть максимальный

   // по модулю элемент массива:

   max = fabs(x[0]);


   // А теперь пробуем себя опровергнуть:

   for(i = 1; i < n; i++)

      if(fabs(x[i]) > max)

         max = fabs(x[i]);


   // Максимум найден:

   cout << "max=" << max << endl;


   // Делим все элементы на max

   for(i = 0; i < n; i++)

      x[i] /= max;


   // Распечатка массива

   cout << "Massiv:" << endl;

   for(i = 0; i < n; i++)

      cout << x[i] << endl;


   return 0;

}

Инициализация массивов

Элементы массива можно проинициализировать, т.е. задать им начальные значения на этапе выделения памяти под массив. Делается это почти так же, как для простых переменных. Зададим начальные значения элементов массива в нашем примере (смотри рисунок в начале этой темы):

const int n=5;

double x[n]={2.5, -1.1, 3, 5.7, 1.5};

Если при инициализации задано меньше данных, чем выделяется памяти (у нас — для пяти чисел), то значение элементов массива, которые не получили значение в момент выделения памяти, будет неопределённым.

Можно пойти и дальше: не задавать при инициализации массива его размер. Пусть компилятор сам определяет, сколько байт необходимо выделить под рассматриваемый массив:

double x[]={2.5, -1.1, 3, 5.7, 1.5};

Но как обрабатывать теперь массив? Нам же надо знать количество элементов! Поделим размер массива на размер одного элемента — это и будет искомым значением:

int n = sizeof(x) / sizeof(double);

Здесь sizeof() — это стандартная операция по вычислению размера какого-либо объекта.

Теперь можно применять привычный подход при обработке массива:

for(i = 0; i < n; i++) { тело цикла }



Hosted by uCoz
Назад
На верх
Вперёд
Hosted by uCoz