Массивы. Основы

Хранение данных

Предположим, что вы разработали компьютерную игру под названием "Враждебный пользователь", в которой игроки состязаются с замысловатым и недружественным компьютерным интерфейсом. Теперь вам необходимо написать программу, которая отслеживает ежемесячные объемы продаж этой игры в течение пятилетнего периода. Или, скажем, вам нужно провести инвентаризацию торговых карт героев-хакеров.
Очень скоро вы придете к выводу, что для накопления и обработки информации вам требуется нечто большее, чем простые базовые типы. Язык Си предлагает это нечто большее, а именно — составные типы.
 
Составные типы - это типы, состоящие из базовых целочисленных типов и типов с плавающей точкой.

Массив, например, может хранить множество значений одного и того же типа.
Отдельный вид массива может хранить строки, которые являются последовательностями символов.
Структуры могут хранить по нескольку значений разных типов.
Кроме того, есть еще указатели, которые представляют собой переменные, сообщающие компьютеру местонахождение данных в памяти.
Все эти составные формы данных мы цель нашего изучения.

Массивы. Введение

Массив – это группа ячеек памяти одинакового типа, расположенных рядом и имеющих общее имя. Каждая ячейка в группе имеет уникальный номер.

Например, массив может содержать 60 значений типа int, которые представляют информацию об объемах продаж за 5 лет, 12 значений типа short, представляющих количество дней в каждом месяце, или 365 значений типа float, которые указывают ежедневные расходы на питание в течение года. Каждое значение сохраняется в отдельном элементе массива, и компьютер хранит все элементы массива в памяти последовательно — друг за другом.
При работе с массивами надо научиться решать три задачи:
• выделять память нужного размера под массив
• записывать данные в нужную ячейку
• читать данные из ячейки

1) Объявление массива

Чтобы использовать массив необходимо его объявить - выделить место в памяти.
Объявление массива должно описывать три аспекта:
• тип значений каждого элемента;
• имя массива;
• количество элементов в массиве.
int A[10], B[20]; // 2 массива на 10 и 20 целых чисел
float C[12]; // массив из 12 вещественных чисел

Oбщая форма объявления массива

ТипЭлементов имяМассива[размер Массива]; 
Для повышения универсальности программы размер массива лучше определять через константу. В этом случае изменить программу для массива другого размера достаточно легко, необходимо будет только поменять значение этой константы.
#include<stdio.h>
main()
{
const int N = 20; // константа
  int A[N]; // размер массива задан через константу
  ... 
} 
В таблице показаны примеры правильного и неправильного объявления массива.
Правильно Неправильно
int A[20]; размер массива указан явно int A[]; размер массива неизвестен
const int N = 20;
int A[N];
размер массива –
постоянная величина
int N = 20;
int A[N];
 
размер массива не может быть переменной
 

Иногда необходимо вводить размер массива с клавиатуры. В этом случае в программе нужно заранее выделить в памяти массив наибольшего размера, соответствующее условию задачи. Это очень часто применяется в автоматических системах проверки задач, таких как silvertests
В этом случае объявление массива можно оформить следующим образом:
#include<stdio.h>
main()
{
  const int Nmax = 100; // Выделяем место в памяти под максимально возможный размер массива (обычно это значение указано в условии задачи) 
  int A[Nmax], N; 
  scanf("%d",&N); //рабочее число элементов массива задаем с клавиатуры. Это позволяет проверить работу программы на массивах разной длины.
  ... 
} 

Обращение к элементу массива

Большая часть пользы от массивов определяется тем фактом, что к его элементам можно обращаться индивидуально.
Способ, который позволяет это делать, заключается в использовании индекса для нумерации элементов.

ЗАПОМНИТЕ!
НУМЕРАЦИЯ МАССИВОВ В СИ НАЧИНАЕТСЯ С НУЛЯ.

(Это является обязательным — вы должны начинать с нуля. Это особенно важно запомнить)

Примеры обращения к массиву A:
x = (A[3] + 5)*A[1]; // прочитать значения A[3] и A[1]
A[0] = x + 6; // записать новое значение в A[0]
Разберем программу работы с элементами массива.
#include <iostream>
using namespace std;
main()
 {
   int i=1, A[5]; 
   A[0] = 23;  //в каждый из 5 элементов массива (индексы от 0 до 4)
   A[1] = 12;  //записываем определенное значение
   A[2] = 7;   
   A[3] = 43;
   A[4] = 51;
   A[2] = A[i] + 2*A[i-1] + A[2*i]; //изменим значение элемента с индексом 2, на результат выражения
                                    //т.к i=1, то подставляя значение переменной i в выражение получим
                                    //следующее выражение  A[2] = A[i] + 2*A[0] + A[2];
   cout << A[2]+A[4];
}
В результате выполнения данной программы на экране появится значение элемента массива с индексом 2 равное 116 Как видно из примера, мы можем обращаться к любому элементу массива. А также вычислять необходимый номер элемента по различным формулам (например, как в программе A[i-1] или A[2*i], в данных случаях индексы элементов будут вычислены и зависят от значения i.)

Разберем пример программы
#include<iostream>
using namespace std;
main()
{
  const int N = 5; 
  int A[N]; 
  x = 1;
  cout << A[x-3];   //обращение к элементу A[-2]
  A[x+4]=A[x]+A[2*(x+1)];  //после подстановки х в выражения и вычислений получаем следующую строку: A[5] = A[1]+A[4];
  ... 
} 

Т.к. массив объявлен из 5 элементов, значит элементы будут иметь нумерацию с 0 до 4. Видим что, программа обращается к несуществующим элементам: A[-2] и A[5]
Получается, что программа вышла за границы массива

Выход за границы массива - это обращение к элементу с индексом, который не существует в массиве.

В таких случая программы обычно завершается аварийно с ошибкой run-time error

Давайте попробуем самостоятельно поработать с элементами массива. Выполните задание
 

При работе с массивами обычно приходится работать сразу со всеми элементами массива.
Перебор элементов: просматриваем все элементы массива и, если нужно, выполняем с каждым из них некоторую операцию.
Для этого используется чаще всего цикл с переменной, которая изменяется от 0 до N-1 (где N количество элементом массива.
...
const int N = 10;
int A[N]; 
for (i=0; i<N; i++)
  {
     //здесь работаем с A[i] 
  }
...
В указанном циклы переменная i будет принимать значения 0, 1, 2, ...,N-1.  То есть на каждом шаге цикла мы обращаемся к конкретному элементу массива с номером i
Таким образом, достаточно описать, что нужно сделать с одним элементом массива A[i] и эти действия поместить внутрь такого цикла.

Напишем программу, которая заполняет массив первыми N натуральными числа. т.е. по окончанию программы элементы массива должбы стать равными
A[0] = 1; 
A[1] = 2;
A[2] = 3;
...
A[N-1] = N;
Нетрудно заметить закономерность: значение элемента массива должно быть больше на 1, чем индекс элемента.
Цикл будет выглядеть следующим образом
for (i=0; i<N; i++)
  {
     A[i] = i+1; 
  }
Выполните задание.