Загрузил starlight87

Указатели. Динамические переменные и массивы

реклама
Указатели в C++: адрес и удаление
Всем привет! В этом уроке мы разберём то, что очень тесно связано с памятью в компьютере. С
помощью этого можно улучшать работу своей программы. Как вы догадались с названия урока, это —
указатели.
Адрес переменной в C++
Поголовно у каждой переменной имеется свой индивидуальный адрес. Адрес переменной — это путь,
по которому находится значение самой переменной. Он записывается в шестнадцатеричном виде. Так,
компьютер может записать переменную, как в такой адрес 0x155, так и в такой 0x212.
Давайте приведем аналогию с круизным лайнером. В нем, как и в отеле, имеются номера. Вот,
например, при покупке номера вам могут дать номер — 0x155 (да, мы понимаем, что не в одном
лайнере или отеле не станут записывать номера в шестнадцатеричном виде, но давайте все
таки немного отвлечемся). А друг может оказаться в номере 0x212 — так и с переменными, они могут
получить разный путь. И только сам компьютер при создании переменной знает, где она находится.
Переменные, которые вы создаете в программе, по её завершению автоматически удаляются, чтобы не
нагружать операционную память вашего компьютера.
Пример удаления переменных
В играх присутствует хорошая графика, различные спецэффекты. Например, тот же дым. Все это —
переменная (может не одна!), которой в будущем придётся уничтожиться навсегда. А вот, если бы она
не удалилась, то она бы своей фоновой работой понемножку нагружала бы наш компьютер.
Поэтому в C/C++ присутствует возможность обратиться к переменной, и, если требует ситуация,
удалить и создать её вовсе в другом участке программы, когда это, конечно, нам будет нужно.
Что такое указатели в C++
Указатели — это с самого начала переменные, уже в которых хранится адрес других переменных.
Чтобы пользоваться указателями, вам нужно использовать два оператора:


* — показывает значение переменной по заданному адресу (показывает, кто живет в этом
номере). Если вы используете оператор *, то вы занимаетесь операцией разыменование указателя.
& — показывает адрес переменной (говорит, по какому адресу проживает этот человек).
Как создать указатели в C++
Давайте посмотрим, какую конструкцию нужно использовать, чтобы создать указатели:
1 *<имя переменной> = &<имя другой переменной>
Давайте подробно разберем, как эта она работает:



В самом начале мы ставим оператор * (звездочку). Так мы говорим компилятору, что хотим
использовать тип данных — указатель.
Дальше мы должны указать имя нашей переменной.
После знака равно нам нужно передать указателю адрес какой-то переменной, что мы и делаем с
помощью оператора & (амперсанд).
Чтобы передать адрес какой-то переменной, от одного указателя другому, нужно опускать оператор *
для одного указателя (от которого мы передаем второму):
1
int a = 15 + 5;
2
3
int *ykazatel = &a;
4
int *ykazatel_second = ykazatel; // присвоили адрес переменной a
5
6
cout << *ykazatel_second;
7
В нашем случае мы опустили оператор * для ykazatel.
Используем указатели на примере
Чтобы получше понять указатели, давайте их разберем на примере ниже.
Отец работает программистом в крупной компании, которая находится в 0x145 городе, и ему
предложили поехать в командировку в 0x195 город, которую он так долго ждал. Он смог оповестить
только своего сына о том, что уезжает. Поэтому сыну придется передать это маме самому.
Пример выше мы сейчас реализуем на C++ с помощью указателей.
1 #include <iostream>
2
3 using namespace std;
4
5 int main() {
6 setlocale(0, "");
7
8 int dad_gorod;
9 int *dad_son = &dad_gorod;
10
11 int *mama = dad_son;
12
13 system("pause");
14 return 0;
15 }
Давайте подробно разберем код выше:



В строке 8: создали переменную dad_gorod, в которой находится адрес (город, в который уехал отец).
В строке 9: создали указатель dad_son (это сын), он узнает имя города.
В строке 11: объявили указатель mama, которая уже от сына получает имя города.
Так сын (dad_son) в нашем примере является указателем.
Часто у новичков есть некоторое недопонимание, как работает указатель. Они начинают путать
операторы *, &.
Вам нужно помнить, что:


* — используется, когда вам нужно значение переменной.
& — используется, когда вам понадобилось узнать адрес переменной.
Как передать указатели функциям
Вы, наверное, заметили, передавая функции аргумент и изменяя его в функции, переменная, значение
которой мы передавали, никак не изменятся. И это понятно, потому что мы передаем значение одной
переменной — другой переменной. Вот на примере ниже мы решили изменить значение передаваемой
переменной:
1 #include <iostream>
2
3 using namespace std;
4
5 void func(int func_number) {
6 func_number = 5;
7 }
8
9 int main() {
10 setlocale(0, "");
11
12 int main_number = 10;
13
14 cout << "Значение main_number до использования функции: " << main_number << endl;
15
16 func(main_number);
17
18 cout <<"А это значение main_number после использования функции: "<< main_number;
19
20 system("pause");
21 return 0;
22 }
Давайте запустим эту программу:
ykazately.cpp
Значение main_number до использования функции: 10
А это значение main_number после использования функции: 10
Process returned 0 (0x0) execution time : 0.010 s
Press any key to continue.
Как видно выше, у нас ничего не получилось, что и сразу было понятно. А вот если нам понастоящему нужно изменить значение переменной в функции, нам понадобится оперировать
указателями.
Чтобы изменить значение переменной, нам понадобится:


В аргументах функции создать указатель.
Далее при вызове функции передать адрес переменной, которую мы собираемся изменить.
Вот и все! На примере ниже вы можете увидеть, как это реализовать:
1 #include <iostream>
2
3 using namespace std;
4
5 void func(int *func_number) {
6 *func_number = 5;
7 }
8
9 int main() {
10 setlocale(0, "");
11
12 int main_number = 10;
13
14 func(&main_number);
15
16 cout << main_number;
17
18 system("pause");
19 return 0;
20 }
Что такое динамические переменные
Динамические переменные — это переменные, которые созданы напрямую с помощью указателей.
Для них существует функция удаление (это мы разберем ниже).
Чтобы мы могли полноценно создавать динамические переменные, нам понадобится
изучить конструктор — new, после его использования в оперативной памяти компьютера выделяются
ячейки на тот тип данных, который мы указали.
На каждый тип данных выделяется разное количество ячеек.
Как создать динамические переменные в C++
Для создания динамических переменных нам понадобится применять конструкцию ниже:
1 <тип данных указателя> *<имя указателя> = new <тип данных>(<первоначальное значение>);
Давайте подробно ее разберем:




— указанный тип данных почти ни на что не повлияет. Читайте
ниже.
new — это конструктор, который и будет заключительным звеном для создания нашей
переменной.
<тип данных> — здесь нам понадобится указать тип, какой будет храниться в переменной. Он
необязательно должен совпадать с типом указателя.
<первоначальное значение> — с помощью круглых скобок можно указать значение
переменной еще при ее инициализации. Использование круглых скобок в этой конструкции
необязательно.
<тип данных указателя>
Вы должны знать! Если тип переменной отличается от типа указателя — то эта динамическая
переменная будет весить больше в оперативной памяти, чем такая же переменная с одинаковыми
типами!
Пример использования динамических переменных
Внизу мы решили использовать динамические переменные:
1 #include <iostream>
2
3 using namespace std;
4
5 int main() {
6 setlocale(0, "");
7 int *a = new int;
8
9 int b = 10;
10
11 *a = b;
12
13 cout <<"Теперь переменная a равна "<< *a << endl;
14
15 cout <<"Пришло время удалить эту переменную!";
16
17 system("pause");
18 return 0;
19 }



В строке 7: мы объявили переменную, оперируя конструктором new.
Дальше в строке 11: значение нашей переменной становится равно 10.
И в самом конце, в строке 15: выводим значение нашей переменной на экран.
Важно помнить! Динамические переменные — это указатели, и поэтому перед ними обязательно
должен стоять оператор *.
Удаление динамических переменных
Как мы говорили выше, у нас есть возможность освобождать память переменной или, если понятным
языком, удалять переменную из оперативной памяти ПК.
Конечно, эта переменная и так удалится из оперативной памяти компьютера при завершении
программы. Но если нам захотелось удалить ее еще в середине программы, то это будет возможно
благодаря оператору delete.
Чтобы его использовать, нужно применить конструкцию ниже:
1 delete <имя переменной>;


В самом начале мы используем оператор delete.
Дальше идет имя переменной.
Вы должны обратить внимание на отсутствие оператора * перед именем переменной. Многие
начинающие прогеры забывают про это и в дальнейшем пытаются найти ошибку часами.
Статическое и динамическое объявление переменных
Статическое объявление переменных имеет такой вид: int number;
Использование динамических переменных имеет маленький плюс. Он заключается в освобождении
памяти переменной до завершения программы. Благодаря этому мы можем сначала удалить
переменную, а потом ее снова создать в другом участке программы (когда это нам будет нужно).
Что такое динамические массивы
Мы уже знакомы с миром массивов в C++. Мы не раз создавали их на определенное количество ячеек
и при этом использовали статическое создание массивов.
1 int array[100];
Но еще ни разу не затрагивали их использование с указателями!
Мы создавали массивы на сто тысяч элементов, а то и больше. И не один раз бывало, что большое
количество ячеек оставались неиспользованными. Это является неправильным
применением оперативной памяти в ПК.
Чтобы мы бесполезно не использовали оперативную память в компьютере, нам понадобится
оперировать с указателями в свете массивов.
Нам нужно вспомнить, что для создания статического массива количество ячеек нужно задавать
числовой константой (а не переменной). Это очень неприятно, потому что в программе мы не знаем,
сколько нам может понадобится ячеек.
Например, пользователь захотел вписать 1000 чисел в массив, а мы из-за незнания этого факта сделали
массив всего лишь на 500 ячеек.
Динамический массив — это массив, у которого количество ячеек можно задавать и переменной, и
числовой константой. Это большой плюс перед использованием статического массива.
Как работают динамические массивы
Для работы динамических массивов нам понадобится при инициализации указатель (всего лишь при
инициализации!) и уже знакомый конструктор new.
Как создать динамический массив в C++
Чтобы создать динамический массив мы будем использовать конструкцию ниже:
1 <тип данных> *<имя массива> = new <тип переменных> [<количество ячеек>];



— без разницы какой тип данных тут будет находиться, но лучше тот, который
будет совпадать с типом переменных.
<тип переменных> — указанный сюда тип и будут иметь ячейки массива.
<количество ячеек> — здесь мы задаем размер массива (например [n] или [25]).
<тип данных>
Динамический массив полностью идентичен обычному массиву, кроме:


Своей инициализации
Возможностью своевременно освободить память.
Давайте рассмотрим пример с использованием динамического массива:
1 int main() {
2 setlocale(0, "");
3
4 int n;
5
6 cout << "Введите количество чисел, которое вы хотите ввести: ";
7 cin >> n;
8
9 cout << "Введите " << n << " чисел: ";
10
11 int *dinamich_array = new int [n]; // создаем
12
// динамический массив
13 for (int i = 0; i < n; i++) {
14 cin >> dinamich_array[i]; // считываем числа в ячейки массива
15 }
16
17 cout << "Теперь давайте выведем элементы массива в обратном порядке: ";
18
19 for (int i = n - 1 ; i >= 0; i--) {
20 cout << dinamich_array[i] << " "; // выводим значение всех ячеек
21 }
22
23 cout << endl << "Удаляем массив!";
24
25 delete [] dinamich_array; // удаляем динамический массив
26
27 return 0;
28 }
Вот что будет при выполнении программы:
dinamic_array.cpp
Задайте количество чисел, которое вы хотите ввести: 5
Введите 5 чисел: 2 4 6 8 16
Теперь давайте выведем элементы массива в обратном порядке: 16 8 6 4 2
Удаляем массив!
Process returned 0 (0x0) execution time : 0.010 s
Press any key to continue.
Удаление динамического массива
Для удаления динамического массива нам понадобится уже знакомый оператор — delete.
1 delete [] <имя массива>;
Важно запомнить, что квадратные скобки нужно ставить перед <именем массива>.
Как создать двумерный динамический массив в C++
Для создания двумерного динамического массива мы будем использовать похожую конструкцию (как
и в одномерном динамическом массиве):
1 <тип данных> **<имя массива> = new <тип данных массива>* [<количество ячеек>];
Вам нужно обратить внимание на:

Дополнительный оператор * перед <имя массива> и после <тип данных массива>.
Дальше для каждой ячейки мы должны создать одномерный массив. Чтобы это сделать, нам
понадобится цикл for и конструктор new.
1 for (int i = 0; i < n; i++) {
2
3 <имя массива>[i] = new <тип ячеек> [<количество ячеек>];
4
5}
В <количество ячеек> можно задавать разные значения. Поэтому сначала для первого массива можно
задать длину 1 (new int [1]), потом для второго — длину 2 (new int [2]), как в примере ниже.
Внизу находится пример двумерного динамического массива:
1 #include <iostream>
2
3 using namespace std;
4
5 int main() {
6 setlocale(0, "");
7
8 int **dinamic_array2 = new int* [5]; // создаем
9 for (int i = 0; i < 5; i++) {
// двумерный
10 dinamic_array2[i] = new int [i + 1]; // массив
11 }
// !
12
13 for (int i = 0; i < 5; i++) {
14 cout << "Введите числа" << "(" << i + 1 << ")" << ":";
15 for (int j = 0; j < i + 1; j++) {
16
cin >> dinamic_array2[i][j];
17 }
18 }
19
20 for (int i = 0; i < 5; i++) {
21 int sum = 0;
22 for (int j = 0; j < i + 1; j++) {
23
sum += dinamic_array2[i][j];
24 }
25 cout << "Сумма " << i + 1 << " массива равна " << sum << endl;
26 }
27
28 for (int i = 0; i < 5; i++) {
29 delete [] dinamic_array2[i]; // удаляем массив
30 }
31
32 system("pause");
33 return 0;
34 }




В строках 8 — 11: создали двумерный динамический массив.
В строках 13 — 18: заполнили массив.
В строках 20 — 26: подсчитали и вывели по отдельности на экран сумму всех массивов.
В строках 28 — 30: происходит удаление массива (об этом ниже).
Удаление двумерного динамического массива
Для удаление двумерного динамического массива мы будем использовать уже похожую схему. Но в
ней присутствует цикл for, и после <имя массива> находится индекс того массива который будет
удален.
1 for (int i = 0; i < <количество элементов в массиве>; i++) {
2
3 delete [] <имя массива>[i];
4
5}
Скачать