Массивы

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

Это означает, что, например, пять значений типа intмогут быть объявлены как массив без объявления 5 разных переменных (каждый со своим собственным идентификатором). Вместо этого, используя массив, пять intзначений сохраняются в смежных ячейках памяти, и все пять могут быть доступны с использованием того же идентификатора с соответствующим индексом.

Например, массив, содержащий 5 целых значений intвызываемого типа, fooможет быть представлен как:


где каждая пустая панель представляет собой элемент массива. В этом случае это значения типаint, Эти элементы нумеруются от 0 до 4, являясь 0 первым и 4 последним; В C ++ первый элемент в массиве всегда нумеруется (но не один), независимо от его длины.

Как и регулярная переменная, массив должен быть объявлен до его использования. Типичным объявлением для массива в C ++ является: где допустимый тип (например , …), является допустимым идентификатором и поле (которое всегда заключено в квадратные скобки ), задает длину массива в терминах количество элементов. Следовательно, массив с пятью элементами типа может быть объявлен как:

type name [elements];

typeintfloatnameelements[]

fooint

 
int foo [5];

ПРИМЕЧАНИЕ. elementsПоле в квадратных скобках [], представляющее количество элементов в массиве, должно быть постоянным выражением , так как массивы представляют собой блоки статической памяти, размер которых должен определяться во время компиляции до запуска программы.

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

По умолчанию регулярные массивы локальной области (например, объявленные внутри функции) остаются неинициализированными. Это означает, что ни один из его элементов не установлен в какое-либо конкретное значение; их содержимое не определено в точке, объявленной массивом.

Но элементы в массиве могут быть явно инициализированы определенными значениями, когда они объявлены, путем включения этих начальных значений в фигурные скобки {}. Например:

int foo [5] = { 16, 2, 77, 40, 12071 }; 

Этот оператор объявляет массив, который может быть представлен следующим образом:


количество значений между фигурными скобками {}не должно превышать число элементов в массиве. Например, в приведенном выше примере fooбыло объявлено 5 элементов (как указано числом, заключенным в квадратные скобки []), а фигурные скобки {}содержат ровно 5 значений, по одному для каждого элемента. Если объявлено с меньшим значением, остальные элементы устанавливаются в значения по умолчанию (что для основных типов означает, что они заполнены нулями). Например:

int bar [5] = { 10, 20, 30 }; 

Создает такой массив:


Инициализатор может даже не иметь значений, просто привязки:

int baz [5] = { }; 

Это создает массив из пяти intзначений, каждый из которых инициализируется нулевым значением.


Когда инициализация значений предоставляется для массива, C ++ позволяет оставлять квадратные скобки пустыми []. В этом случае компилятор автоматически примет размер массива, который соответствует числу значений, заключенных между фигурными скобками {}:

int foo [] = { 16, 2, 77, 40, 12071 };

После этого объявления массив fooбудет иметь intдлину 5 , так как мы предоставили 5 значений инициализации.

Наконец, эволюция C ++ привела к принятию универсальной инициализации также для массивов. Следовательно, больше нет необходимости в знаке равенства между объявлением и инициализатором. Оба эти утверждения эквивалентны:

1
2
int foo[] = { 10, 20, 30 };
int foo[] { 10, 20, 30 }; 

Статические массивы и те, которые указаны непосредственно в пространстве имен (вне любой функции), всегда инициализируются. Если явный инициализатор не указан, все элементы инициализируются по умолчанию (с нулями для основных типов).

Доступ к значениям массива

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

name[index]
После предыдущих примеров, в которых fooбыло 5 элементов, и каждый из этих элементов имел тип int, имя, которое может использоваться для обозначения каждого элемента, следующее:


Например, следующий оператор сохраняет значение 75 в третьем элемент foo:

foo [2] = 75;

и, например, следующее копирует значение третьего элемента fooпеременной x:

x = foo[2];

Следовательно, выражение foo[2]само по себе является переменной типа int.

Обратите внимание, что fooуказан третий элемент foo[2], так как первый из них foo[0], второй foo[1], и, следовательно, третий foo[2]. По этой же причине последний элемент foo[4]. Поэтому, если мы пишем foo[5], мы будем обращаться к шестому элементу fooи, следовательно, фактически превышать размер массива.

В C ++ синтаксически корректно превышать допустимый диапазон индексов для массива. Это может создать проблемы, поскольку доступ к элементам вне диапазона не вызывает ошибок при компиляции, но может привести к ошибкам во время выполнения. Причина этого допускается в более поздней главе, когда вводятся указатели.

На этом этапе важно иметь возможность четко различать два использования, которые скобки []связаны с массивами. Они выполняют две разные задачи: один – указать размер массивов при их объявлении; а второй – указывать индексы для конкретных элементов массива при их доступе. Не путайте эти два возможных использования скобок []с массивами.

1
2
int foo[5];         // declaration of a new array
foo[2] = 75;        // access to an element of the array.  

Основное отличие состоит в том, что объявлению предшествует тип элементов, а доступ – нет.

Некоторые другие действительные операции с массивами:

1
2
3
4
foo[0] = a;
foo[a] = 75;
b = foo [a+2];
foo[foo[a]] = foo[2] + 5;

Например:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// arrays example
#include <iostream>
using namespace std;

int foo [] = {16, 2, 77, 40, 12071};
int n, result=0;

int main ()
{
  for ( n=0 ; n<5 ; ++n )
  {
    result += foo[n];
  }
  cout << result;
  return 0;
}
12206

Многомерные массивы

Многомерные массивы можно охарактеризовать как «массивы массивов». Например, двумерный массив можно представить в виде двумерной таблицы, состоящей из элементов, всех из них одного и того же типа данных.


jimmyпредставляет собой двумерный массив из 3 на 5 элементов типа int. Синтаксис C ++ для этого:

 
int jimmy [3][5];

и, например, способ ссылки на второй элемент по вертикали и четверти по горизонтали в выражении:

 
jimmy[1][3]


(помните, что индексы массива всегда начинаются с нуля).

Многомерные массивы не ограничиваются двумя индексами (т. Е. Двумя измерениями). Они могут содержать столько индексов, сколько необходимо. Хотя будьте осторожны: объем памяти, необходимый для массива, экспоненциально увеличивается с каждым измерением. Например:

 
char century [100][365][24][60][60];

объявляет массив с элементом типа charдля каждой секунды в столетии. Это составляет более 3 миллиардов char! Таким образом, это объявление будет потреблять более 3 гигабайт памяти!

В конце концов, многомерные массивы являются просто абстракцией для программистов, поскольку одни и те же результаты могут быть достигнуты с помощью простого массива путем умножения его индексов:

1
2
int jimmy [3][5];   // is equivalent to
int jimmy [15];     // (3 * 5 = 15)  

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

многомерный массив псевдо-многомерный массив

#define WIDTH 5
#define HEIGHT 3

int jimmy [HEIGHT][WIDTH];
int n,m;

int main ()
{
  for (n=0; n<HEIGHT; n++)
    for (m=0; m<WIDTH; m++)
    {
      jimmy[n][m]=(n+1)*(m+1);
    }
}

#define WIDTH 5
#define HEIGHT 3

int jimmy [HEIGHT * WIDTH];
int n,m;

int main ()
{
  for (n=0; n<HEIGHT; n++)
    for (m=0; m<WIDTH; m++)
    {
      jimmy[n*WIDTH+m]=(n+1)*(m+1);
    }
}

Ни один из двух фрагментов кода выше не выводит какой-либо вывод на экране, но оба присваивают значения блоку памяти, называемому jimmy, следующим образом:


Обратите внимание, что код использует определенные константы для ширины и высоты вместо использования непосредственно их числовых значений. Это дает коду лучшую читаемость и позволяет легко изменять код в одном месте.

Массивы как параметры

В какой-то момент нам может понадобиться передать массив функции в качестве параметра. В C ++ невозможно передать весь блок памяти, представленный массивом, функции непосредственно в качестве аргумента. Но то, что может быть принято, – это его адрес. На практике это имеет почти такой же эффект, и это намного быстрее и эффективнее.

Чтобы принять массив как параметр для функции, параметры могут быть объявлены как тип массива, но с пустыми скобками, опуская фактический размер массива. Например:

 
void procedure (int arg[])

Эта функция принимает параметр intвызываемого типа «массив » arg. Чтобы передать этой функции массив, объявленный как:

int myarray [40];

достаточно было бы написать такой вызов:

procedure (myarray);

Здесь у вас есть полный пример:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// arrays as parameters
#include <iostream>
using namespace std;

void printarray (int arg[], int length) {
  for (int n=0; n<length; ++n)
    cout << arg[n] << ' ';
  cout << '\n';
}

int main ()
{
  int firstarray[] = {5, 10, 15};
  int secondarray[] = {2, 4, 6, 8, 10};
  printarray (firstarray,3);
  printarray (secondarray,5);
}
5 10 15
2 4 6 8 10

В приведенном выше коде первый параметр ( int arg[]) принимает любой массив, элементы которого имеют тип int, независимо от его длины. По этой причине мы включили второй параметр, который сообщает функции длину каждого массива, который мы передаем ему в качестве первого параметра. Это позволяет циклу for печатать массив, чтобы знать диапазон для итерации в переданном массиве, не выходя за пределы диапазона.

В объявлении функции также можно включать многомерные массивы. Формат для трехмерного параметра массива:

base_type[][depth][depth]

Например, функция с многомерным массивом в качестве аргумента может быть:

void procedure (int myarray[][3][4])

Обратите внимание, что первые скобки []оставлены пустыми, а следующие определяют размеры для соответствующих размеров. Это необходимо для того, чтобы компилятор мог определить глубину каждого дополнительного измерения.

В некотором смысле передача массива как аргумента всегда теряет измерение. Причина в том, что по историческим причинам массивы не могут быть скопированы напрямую, и, следовательно, то, что действительно передается, является указателем. Это общий источник ошибок для начинающих программистов. Хотя ясное понимание указателей, объясняемое в грядущей главе, очень помогает.

Библиотечные массивы

Рассмотренные выше массивы непосредственно реализуются как языковая функция, унаследованная от языка C. Это отличная функция, но, ограничивая ее копию и легко распадаясь на указатели, они, вероятно, страдают от избытка оптимизации.

Чтобы преодолеть некоторые из этих проблем с помощью встроенных в язык массивов, C ++ предоставляет альтернативный тип массива в качестве стандартного контейнера. Это шаблон типа (фактически шаблон шаблона), определенный в заголовке <array>.

Контейнеры представляют собой библиотечную функцию, выходящую за рамки данного руководства, и, следовательно, этот класс не будет подробно описан здесь. Достаточно сказать, что они работают аналогично встроенным массивам, за исключением того, что они позволяют копировать (фактически дорогостоящая операция, которая копирует весь блок памяти и, следовательно, будет использоваться с осторожностью) и распадаться на указатели только тогда, когда явно сказал сделать это (через своего члена data).

Как пример, это две версии одного и того же примера с использованием встроенного в язык массива, описанного в этой главе, и контейнера в библиотеке:

встроенный язык контейнерная библиотека

#include <iostream>

using namespace std;

int main()
{
  int myarray[3] = {10,20,30};

  for (int i=0; i<3; ++i)
    ++myarray[i];

  for (int elem : myarray)
    cout << elem << '\n';
}

#include <iostream>
#include <array>
using namespace std;

int main()
{
  array<int,3> myarray {10,20,30};

  for (int i=0; i<myarray.size(); ++i)
    ++myarray[i];

  for (int elem : myarray)
    cout << elem << '\n';
}

Как вы можете видеть, оба вида массивов используют один и тот же синтаксис для доступа к его элементам: myarray[i]. Помимо этого, основные отличия заключаются в объявлении массива и включении дополнительного заголовка для массива библиотеки . Обратите внимание также, как легко получить доступ к размеру массива библиотеки .

Залишити відповідь