Массивы в C++ — урок 5
Сегодня мы с поговорим о массивах. Вы уже знаете, что переменная — это ячейка в памяти компьютера, где может храниться одно единственное значение. Массив — это область памяти, где могут последовательно храниться несколько значений.
Возьмем группу студентов из десяти человек. У каждого из них есть фамилия. Создавать отдельную переменную для каждого студента — не рационально. Создадим массив, в котором будут храниться фамилии всех студентов.
- Пример инициализации массива
- Описание синтаксиса
- Вывод элементов массива через цикл
- Объявление массива без инициализации
- Заполнение массива с клавиатуры
Пример инициализации массива
string students[10] = { "Иванов", "Петров", "Сидоров", "Ахмедов", "Ерошкин", "Выхин", "Андеев", "Вин Дизель", "Картошкин", "Чубайс" };
Описание синтаксиса
Массив создается почти так же, как и обычная переменная. Для хранения десяти фамилий нам нужен массив, состоящий из 10 элементов.
Чтобы описать элементы массива сразу при его создании, можно использовать фигурные скобки. В фигурных скобках значения элементов массива перечисляются через запятую. В конце закрывающей фигурной скобки ставится точка с запятой.
Попробуем вывести наш массив на экран с помощью оператора cout
.
#include <iostream> #include <string> int main() { std::string students[10] = { "Иванов", "Петров", "Сидоров", "Ахмедов", "Ерошкин", "Выхин", "Андеев", "Вин Дизель", "Картошкин", "Чубайс" }; std::cout << students << std::endl; // Пытаемся вывести весь массив непосредственно return 0; }
Скомпилируйте этот код и посмотрите, на результат работы программы. Готово? А теперь запустите программу еще раз и сравните с предыдущим результатом. В моей операционной системе вывод был следующим:
- Первый вывод:
0x7ffff8b85820
- Второй вывод:
0x7fff7a335f90
- Третий вывод:
0x7ffff847eb40
Мы видим, что выводится адрес этого массива в оперативной памяти, а никакие не «Иванов» и «Петров».
Дело в том, что при создании переменной, ей выделяется определенное место в памяти. Если мы объявляем переменную типа int
, то на машинном уровне она описывается двумя параметрами — ее адресом и размером хранимых данных.
Массивы в памяти хранятся таким же образом. Массив типа int
из 10 элементов описывается с помощью адреса его первого элемента и количества байт, которое может вместить этот массив. Если для хранения одного целого числа выделяется 4 байта, то для массива из десяти целых чисел будет выделено 40 байт.
Так почему же, при повторном запуске программы, адреса различаются? Это сделано для защиты от атак переполнения буфера. Такая технология называется рандомизацией адресного пространства и реализована в большинстве популярных ОС.
Попробуем вывести первый элемент массива — фамилию студента Иванова.
#include <iostream> #include <string> int main() { std::string students[10] = { "Иванов", "Петров", "Сидоров", "Ахмедов", "Ерошкин", "Выхин", "Андеев", "Вин Дизель", "Картошкин", "Чубайс" }; std::cout << students[0] << std::endl; return 0; }
Смотрим, компилируем, запускаем. Убедились, что вывелся именно «Иванов».
Заметьте, что нумерация элементов массива в C++ начинается с нуля. Следовательно, фамилия первого студента находится в students[0]
, а фамилия последнего — в students[9]
.
В большинстве языков программирования нумерация элементов массива также начинается с нуля.
Попробуем вывести список всех студентов. Но сначала подумаем, а что если бы вместо группы из десяти студентов, была бы кафедра их ста, факультет из тысячи, или даже весь университет? Ну не будем же мы писать десятки тысяч строк с cout
?
Конечно же нет! Мы возьмем на вооружение циклы, о которых был написан предыдущий урок.
Вывод элементов массива через цикл
#include <iostream> #include <string> int main() { std::string students[10] = { "Иванов", "Петров", "Сидоров", "Ахмедов", "Ерошкин", "Выхин", "Андеев", "Вин Дизель", "Картошкин", "Чубайс" }; for (int i = 0; i < 10; i++) { std::cout << students[i] << std::endl; } return 0; }
Если бы нам пришлось выводить массив из нескольких тысяч фамилий, то мы бы просто увеличили конечное значение счетчика цикла — строку for (.
заменили на for (...; i < 10000; ...)
.
Заметьте что счетчик нашего цикла начинается с нуля, а заканчивается девяткой. Если вместо оператора строгого неравенства — i < 10
использовать оператор «меньше, либо равно» — i <= 10
, то на последней итерации программа обратится к несуществующему элементу массива — students[10]
. Это может привести к ошибкам сегментации и аварийному завершению программы. Будьте внимательны — подобные ошибки бывает сложно отловить.
Массив, как и любую переменную можно не заполнять значениями при объявлении.
Объявление массива без инициализации
string students[10]; // или string teachers[5];
Элементы такого массива обычно содержат в себе «мусор» из выделенной, но еще не инициализированной, памяти. Некоторые компиляторы, такие как GCC, заполняют все элементы массива нулями при его создании.
При создании статического массива, для указания его размера может использоваться только константа. Размер выделяемой памяти определяется на этапе компиляции и не может изменяться в процессе выполнения.
int n; cin >> n; string students[n]; /* Неверно */
Выделение памяти в процессе выполнения возможно при работе с динамическими массивами. Но о них немного позже.
Заполним с клавиатуры пустой массив из 10 элементов.
Заполнение массива с клавиатуры
#include <iostream> #include <string> using std::cout; using std::cin; using std::endl; int main() { int arr[10]; // Заполняем массив с клавиатуры for (int i = 0; i < 10; i++) { cout << "[" << i + 1 << "]" << ": "; cin >> arr[i]; } // И выводим заполненный массив. cout << "\nВаш массив: "; for (int i = 0; i < 10; ++i) { cout << arr[i] << " "; } cout << endl; return 0; }
Скомпилируем эту программу и проверим ее работу.
Если у вас возникают проблемы при компиляции исходников из уроков — внимательно прочитайте ошибку компилятора, попробуйте проанализировать и исправить ее. Если вы нашли ошибку в коде — напишите об этом в комментариях к уроку.
Массивы — очень важная вещь в программировании. Автор советует вам хорошо попрактиковаться в работе с ними.
Следующий урок: Функции в C++ →.
Массивы как тип данных. Курс «Kotlin с нуля»
Массив можно представить как сгруппированные вместе данные одного типа. При этом заранее известно количество этих данных-элементов, а также у каждого элемента есть свое место в массиве. На рисунке ниже показан пример одного массива, элементами которого являются целые числа. Каждое число лежит в своей ячейке, а над ячейкой написан ее номер – индекс элемента.
Такая «упаковка» данных обеспечивает удобство их обработки, когда значений слишком много, чтобы присваивать каждое отдельной переменной. Проще присвоить одной переменной целую группу, а к элементам в случае необходимости обращаться по их индексу.
В языке программирования Kotlin тип данных «массив» обозначается словом Array
. Объявление переменных этого типа ничем не отличается от объявления переменных ранее нами изученных, за исключением того, что дополнительно указывается тип элементов массива. Это делается в угловых скобках после слова Array
.
Примеры объявления массивов разных типов:
fun main() { val a: Array<Int>; val b: Array<String>; val c: Array<Double>; }
Другими словами, несмотря на то, что сам массив – Array
– это один тип данных, на самом деле это тип с вариациями, так называемый дженерик – общий. Поэтому переменной, объявленной как массив целых чисел, нельзя присвоить, например, массив строк.
Самый простой способ инициировать значением переменную типа Array
– это вызвать встроенную в Kotlin функцию arrayOf()
, которой в скобках передать через запятую элементы массива. Функция сама посчитает их количество, которое потом мы можем узнать через свойство массива size
.
fun main() { val a: Array<Double> = arrayOf(1.0, 1.3, -1.2) val s: Int = a.size var i = 0 while (i < s) { println(a[i]) i++ } }
В программе на экран выводятся значения элементов массива. К ним мы обращаемся по индексу, который указывается в квадратных скобках после имени массива. В данном случае в цикле переменная i, которой мы назначили роль индекса, принимает значения 0, 1 и 2. Когда i становится равна трем, цикл завершает свою работу, потому что выражение 3 < 3
возвращает ложь (i равна трем и s равна трем).
С помощью квадратных скобок можно не только извлекать значение из массива (на самом деле оно не извлекается, а остается там, мы просто его «берем и смотрим»), но и менять значение в ячейке, обращаясь к ней по индексу. Если после обращения к элементу массива стоит знак присвоения, значит элемент не извлекается, а перезаписывается.
fun main() { val a: Array<Double> = arrayOf(1.0, 1.3, -1.2) a[1] = 2.8 a[0] = a[2] * 2 println(a[0]) println(a[1]) println(a[2]) }
Обратите внимание, что массив объявлен как неизменяемый – через val
. Однако это не мешает нам изменять значение его элементов. На самом деле переменная действительно остается неизменяемой. Ей нельзя присвоить другой массив. Но в самом массиве менять элементы можно.
Заполним массив случайными числами:
import kotlin.random.Random fun main() { val a: Array<Int> = arrayOf(0, 0, 0, 0, 0) var i = 0 while (i < a.size) { a[i] = Random.nextInt(10) print(a[i]) print(" ") i++ } }
Если массив надо инициировать не пятью значениями, а, скажем, сотней, то записать такое количество нулей в вызове функции arrayOf()
будет проблематично.
Есть другой способ создания массива – путем вызова класса Array
. Как уже было сказано, типы данных это и есть классы данных. В Kotlin имя класса, упомянутое после знака присваивания, означает вызов конструктора этого класса. Конструктор создает объект этого типа, то есть экземпляр класса.
В конструктор класса Array
мы должны передать количество элементов массива и так называемую лямбда-функцию, посредством которой будут вычислены значения элементов массива. В простейшем случае, если мы хотим инициировать все элементы одинаковым значением, достаточно передать это значение. Однако заключать его надо в фигурные скобки, так как по сути это сокращенная до нельзя лямбда-функция.
val a: Array<Int> = Array(5, {0})
В Kotlin такой лямбда-аргумент функции принято выносить за скобку:
val a: Array<Int> = Array(5) {0}
На скрине вместо цикла while
используется цикл for
, особенности работы которого мы рассмотрим в следующем уроке.
Практическая работа:
Заполните массив строками, которые пользователь вводит с клавиатуры и затем выведите их на экран в обратном порядке.
Заполните массив случайными целыми числами. Найдите элементы с максимальным и минимальным значением, используя функции-методы массива
min()
иmax()
.
PDF-версия курса с ответами к практическим работам
Массивы C++ (с примерами)
В этом уроке мы научимся работать с массивами. Мы научимся объявлять, инициализировать и получать доступ к элементам массива в программировании на C++ с помощью примеров.
В C++ массив — это переменная, которая может хранить несколько значений одного типа. Например,
Предположим, в классе 27 учеников, и нам нужно сохранить их оценки. Вместо создания 27 отдельных переменных мы можем просто создать массив:
двойной класс[27];
Здесь класс — это массив, который может содержать максимум 27 элементов типа double
.
В C++ размер и тип массива не могут быть изменены после его объявления.
Объявление массива C++
dataType arrayName[arraySize];
Например,
int x[6];
Здесь,
-
int
— тип сохраняемого элемента - x — имя массива
-
6
— размер массива
Элементы доступа в массиве C++
В C++ каждый элемент массива связан с числом. Число известно как индекс массива. Мы можем получить доступ к элементам массива, используя эти индексы.
// синтаксис для доступа к элементам массива массив[индекс];
Рассмотрим массив x , который мы видели выше.
Элементы массива в C++Несколько вещей, которые следует помнить:
- Индексы массива начинаются с
0
. Значение x[0] — это первый элемент, сохраненный по индексу0
. - Если размер массива равен
n
, последний элемент сохраняется с индексом(n-1)
. В этом примере x[5] является последним элементом. - Элементы массива имеют последовательные адреса. Например, предположим, что начальный адрес
x[0]
равен 2120 .Тогда адрес следующего элемента
x[1]
будет 2124 , адресx[2]
будет 2128 и так далее.Здесь размер каждого элемента увеличен на 4 . Это связано с тем, что размер
int
составляет 4 байта.
Инициализация массива C++
В C++ можно инициализировать массив во время объявления. Например,
// объявить и инициализировать и массив intx[6] = {19, 10, 8, 17, 9, 15};Элементы массива C++ и их данные
Другой метод инициализации массива при объявлении:
// объявить и инициализировать массив intx[] = {19, 10, 8, 17, 9, 15};
Здесь мы не упомянули размер массива. В таких случаях компилятор автоматически вычисляет размер.
Массив C++ с пустыми элементами
В C++, если массив имеет размер n
, мы можем хранить до n элементов массива. Однако что произойдет, если мы будем хранить меньше n элементов.
Например,
// хранить только 3 элемента в массиве интервал х[6] = {19, 10, 8};
Здесь массив x имеет размер 6
. Однако мы инициализировали его только тремя элементами.
В таких случаях компилятор присваивает оставшимся местам случайные значения. Часто это случайное значение просто 0
.
Как вставлять и печатать элементы массива?
метка целого числа [5] = {19, 10, 8, 17, 9} // изменить 4-й элемент на 9 отметка[3] = 9; // принимаем ввод от пользователя // сохраняем значение в третьей позиции cin >> метка[2]; // принимаем ввод от пользователя // вставить в i-ю позицию cin >> отметка[i-1]; // печатаем первый элемент массива cout << отметка [0]; // печатаем i-й элемент массива cout >> отметка[i-1];
Пример 1: Отображение элементов массива
#includeиспользование пространства имен std; интервал основной () { числовые числа [5] = {7, 5, 6, 12, 35}; cout << "Числа: "; // Печать элементов массива // использование диапазона на основе цикла for for (const int &n: числа) { cout << п << " "; } cout << "\nЧисла: "; // Печать элементов массива // использование традиционного цикла for для (целое я = 0; я < 5; ++ я) { cout << числа [i] << " "; } вернуть 0; }
Выход
Числа: 7 5 6 12 35 Числа: 7 5 6 12 35
Здесь мы использовали цикл для
для итерации от i = 0
до i = 4
. На каждой итерации мы напечатали чисел[i]
.
Мы снова использовали цикл для
на основе диапазона для вывода элементов массива. Чтобы узнать больше об этом цикле, см. C++ Ranged for Loop.
Примечание: В нашем цикле на основе диапазона мы использовали код const int &n
вместо int n
в качестве объявления диапазона. Однако const int &n
предпочтительнее, потому что:
- Использование
int n
просто копирует элементы массива в переменную n на каждой итерации. Это не эффективно для памяти. Однако&n использует адрес памяти элементов массива для доступа к их данным без их копирования в новую переменную. Это эффективно с точки зрения памяти.
- Мы просто печатаем элементы массива, не изменяя их. Поэтому мы используем
const
, чтобы случайно не изменить значения массива.
Пример 2. Получение входных данных от пользователя и их сохранение в массиве
#includeиспользование пространства имен std; интервал основной () { целые числа[5]; cout << "Введите 5 цифр: " << endl; // сохраняем ввод от пользователя в массив для (целое я = 0; я < 5; ++ я) { cin >> числа[i]; } cout << "Числа: "; // печатаем элементы массива для (int n = 0; n < 5; ++n) { cout << числа [n] << " "; } вернуть 0; }
Выход
Введите 5 цифр: 11 12 13 14 15 Числа: 11 12 13 14 15
Мы снова использовали цикл for
для итерации от i = 0
до i = 4
. На каждой итерации мы получали ввод от пользователя и сохраняли его в числах[i]
.
Затем мы использовали еще один цикл for
для печати всех элементов массива.
Пример 3. Отображение суммы и среднего значения элементов массива с использованием цикла
#includeиспользование пространства имен std; интервал основной () { // инициализируем массив без указания размера двойные числа [] = {7, 5, 6, 12, 35, 27}; двойная сумма = 0; двойной счет = 0; двойная средняя; cout << "Числа: "; // печатаем элементы массива // использование цикла for на основе диапазона for (const double &n: числа) { cout << п << " "; // вычисляем сумму сумма += п; // считать нет. элементов массива ++количество; } // вывести сумму cout << "\nИх сумма = " << sum << endl; // найти среднее среднее = сумма/количество; cout << "Их среднее значение = " << среднее << endl; вернуть 0; }
Выход
Числа: 7 5 6 12 35 27 Их сумма = 92 Их среднее значение = 15,3333
В этой программе:
- Мы инициализировали массив double с именем чисел , но без указания его размера. Мы также объявили три двойных переменных sum , count и Average .
Здесь сумма
= 0
и количество= 0
. - Затем мы использовали диапазон
для цикла
для печати элементов массива. На каждой итерации цикла мы добавляем текущий элемент массива к сумме . - Мы также увеличиваем значение count на
1
на каждой итерации, чтобы мы могли получить размер массива к концу цикла for. - После печати всех элементов мы печатаем сумму и среднее значение всех чисел. Среднее число определяется как
среднее = сумма / количество;
Примечание: Мы использовали диапазон для цикла
вместо обычного цикла для
.
Обычный цикл для
требует, чтобы мы указали количество итераций, которое определяется размером массива.
Но диапазонный цикл для
не требует таких спецификаций.
C++ Array Out of Bounds
Если мы объявим массив размером 10 , то массив будет содержать элементы с индексом 0 до 9 .
Однако, если мы попытаемся получить доступ к элементу с индексом 10 или более 10 , это приведет к Неопределенному Поведению.
Основы работы с массивами
Основы работы с массивамиОпределение
Массив — это индексированная коллекция элементов данных одного типа.1) Indexed означает, что элементы массива пронумерованы (начиная с 0).
2) Ограничение того же типа является важным, потому что массивы хранятся в последовательных ячейках памяти. Каждая ячейка должны быть одного типа (и, следовательно, одного размера).
Объявление массивов:
Объявление массива похоже на форму обычного объявления (typeName variableName), но мы добавляем размер:имя_типа имя_переменной [ размер ];
Это объявляет массив с указанным размером , названным variableName , типа typeName . Массив индексируется с 0 до размер-1 . Размер (в скобках) должен быть целочисленным литералом или постоянная переменная. Компилятор использует размер, чтобы определить, сколько места выделить (т.е. сколько байтов).
Примеры:
целый список[30]; // массив из 30 целых чисел имя персонажа[20]; // массив из 20 символов двойные числа[50]; // массив из 50 знаков после запятой целая таблица[5][10]; // двумерный массив целых чисел
Последний пример иллюстрирует двумерный массив (который мы часто
хотелось бы думать о как о таблице). Мы обычно думаем о первом размере
как строки, а вторые как столбцы, но на самом деле это не имеет значения, пока
как вы последовательны! Итак, мы могли бы подумать о последнем объявлении
например, в виде таблицы с 5 строками и 10 столбцами.
Инициализация массивов:
С обычными переменными мы могли бы объявить их в одной строке, а затем инициализировать в следующий:интервал х; х = 0;
Или мы могли бы просто инициализировать переменную в операторе объявления сам:
интервал х = 0;
Можем ли мы сделать то же самое для массивов? Да, для встроенных типов. Просто перечислите массив значения (литералы) в обозначении множества { } после объявления. Вот некоторые примеры:
целый список[4] = {2, 4, 6, 8}; char letters[5] = {'a', 'e', 'i', 'o', 'u'}; двойные числа [3] = {3,45, 2,39, 9.1}; int таблица[3][2] = {{2, 5}, {3,1}, {4,9}};
Струны в стиле C
Массивы типа char являются особым случаем.- Мы часто используем строки , но нет встроенного типа строки на языке
- Строка в стиле C реализована как массив типа
char, оканчивающийся специальным символом, называемым «null».
характер".
- Нулевой символ имеет значение ASCII 0
- Нулевой символ может быть записан как литерал в коде следующим образом: '\0'
- Каждый строковый литерал (что-то в двойных кавычках) неявно содержит нулевой символ в конце
Поскольку массивы символов используются для хранения строк в стиле C, вы можете инициализировать массив символов строковым литералом (т. е. строкой в двойные кавычки), если вы оставляете место для нулевого символа в выделенное пространство.
имя персонажа[7] = "Джонни";
Обратите внимание, что это будет эквивалентно:
char name[7] = {'J', 'o', 'h', 'n', 'n', 'y', '\0'};
Варианты инициализации
Объявления массива должны содержать информацию о размер массива. Можно не указывать размер [ ] в объявлении до тех пор, пока вы инициализируете массив встроенным, в этом случае массив делается достаточно большим, чтобы захватить инициализирован данные. Примеры:
имя символа [] = "Джонни"; // размер равен 7 список int[] = {1, 3, 5, 7, 9}; // размер 5
Другим ярлыком с наборами инициализаторов является использование меньшего количества элементов, чем размер уточняет. Остальные элементы по умолчанию будут равны 0. Это незаконно. использовать набор из больше элементов, чем выделенный размер.
целый список[5] = {1, 2}; // массив {1, 2, 0, 0, 0} int nums[3] = {1, 2, 3, 4}; // недопустимое объявление.
Примечание. Использование инициализаторов в объявлении, как в приведенных выше примерах,
является
вероятно, не будет так желательно с очень большими массивами.
Еще один распространенный способ инициализации массива — цикл for :
В этом примере массив numList инициализируется {0, 2, 4, 6, 8, 10, 12,
14, 16, 18}.
числовой список [10]; инт я; для (я = 0; я
Использование массивов:
Как только ваши массивы объявлены, вы получаете доступ к элементам в массиве с помощью имя массива и номер индекса в квадратных скобках [ ]. Если массив объявляется как: typeName varName[size] , тогда элемент с индексом n упоминается как varName[n] . Примеры:интервал х, список[5]; // объявление двойные числа[10]; // объявление список[3] = 6; // присваиваем значение 6 элементу массива с индексом 3 coutОднако было бы нецелесообразно использовать индекс массива, за пределами допустимых индексов массива:
список[5] = 10; // неверное утверждение, так как ваши допустимые индексы равны 0 - 4.Однако приведенное выше утверждение синтаксически допустимо . Это работа программиста, чтобы убедиться, что индексы за пределами не используются. Не рассчитывайте на то, что компилятор проверит это за вас — этого не произойдет!
Копирование массивов:
Если у нас есть эти два массива, как нам скопировать содержимое list2 перечислить1?список1[5]; int list2[5] = {3, 5, 7, 9, 11};С переменными мы используем оператор присваивания, так что это будет естественная склонность -- но она неправильная!
список1 = список2; // это НЕ копирует содержимое массиваМы должны копировать между массивами поэлементно. для петли однако делает это легко:
для (целое я = 0; яПростой ввод-вывод со строками:
В особом случае строк (массивы символов с нулем в конце) они могут использоваться как обычные массивы. Доступ к одному элементу массива означает доступ к одному символу.