Основы программирования
Описание и создание переменных
Описание и создание переменных
Описание и создание переменных
Указатели
Указатели
Операции с указателями
Арифметические операции
Арифметические операции
Операторы для работы с памятью
Операторы для работы с памятью
Операторы для работы с памятью
Статические и динамические массивы
Статические и динамические массивы
Статические и динамические массивы
Обращение к элементам массивов
Пример динамического массива
Пример динамического массива
Указатель на указатель
Двумерный массив и указатели
Двумерный массив и указатели
Двумерный массив и указатели
Двумерный динамический массив
Двумерный динамический массив
Матрица в виде одномерного массива
Матрица в виде одномерного массива
Символы и строки в С
Символы и строки в С
Символы и строки в С
Символы и строки в С
Строки в С++
Области памяти для переменных
Области видимости переменных
Области видимости переменных
Области видимости переменных
Области видимости переменных
Области видимости переменных
245.50K
Категория: ПрограммированиеПрограммирование

04 Указатели и динамические массивы

1. Основы программирования

Указатели и динамические
массивы
1

2. Описание и создание переменных

Данные, которые обрабатывает программа, находятся в
памяти компьютера. Описание любой переменной
подразумевает выполнение несколько операций:
• объявление – задание имени и типа объекта
• определение
памяти)

создание
переменной
(выделение
• инициализация (не во всех случаях!) – присвоение
начального значения переменной.
2

3. Описание и создание переменных

Простое описание числовых переменных:
int k; double x;
После создания глобальные переменные по умолчанию
инициализируются нулем, а для остальных содержимое
выделенной памяти не изменяется.
Явная инициализация при описании:
int k = 4; double x = 3.1415;
Раздельное описание и присвоение начального значения:
int k; double x;
k = 4; x = 3.1415; или cin >> k >> x;
3

4. Описание и создание переменных

Любая переменная занимает определенное ее типом
количество байт, начиная с некоторого адреса. Имя
переменной – это, фактически, скрытая ссылка на
область памяти, где хранится значение переменной.
Но в С/С++ также существует и очень широко
используется
возможность
доступа
к
объектами
непосредственно через их адреса. Реализовать это
позволяют указатели – переменные и константы.
4

5. Указатели

Указатель – это переменная (или константа), значением
которой является адрес области памяти, выделенной для
переменной или значения определенного типа.
Общий формат описания переменной-указателя:
тип *имя_переменной;
5

6. Указатели

Пример:
int *pa; double *px;
pa будет хранить адрес переменной/значения типа int
px – адрес переменной/значения типа double.
pa и px будет выделена память, необходимая для
хранения адреса в памяти (4 или 8 байт), но реальные
адреса по умолчанию не задаются.
Значения переменным-указателям нужно присваивать
явно!
6

7. Операции с указателями

Взятие адреса – унарный префиксный & – извлечение
адреса переменной (константы хранятся в памяти, но
их адреса недоступны в программе):
int a = 2, b, *pa;
double y, x = 3.1415, *px;
pa = &a; px = &x;
Разыменование – унарная префиксная * – ссылка на
объект, на который указывает указатель:
y = *px; b = (3 + *pa) * 10;
cin >> *pa; cout << *px;
*pa *= 5; *px = y * 0.01;
7

8. Арифметические операции

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

9. Арифметические операции

Примеры:
int *pa, a = 2; double x = 3.1415, *px;
pa = &a; px = &x;
px++;
// px указывает на следующий за x в памяти
// элемент double (px увеличивается на 8)
*(pa+5) = 7;
// 5-му после a в памяти элементу int
// (на расстоянии 5*4 байт от a) присваивается
// значение 7, значение pa не изменяется
9

10. Операторы для работы с памятью

Память для явно описанных переменных и статических
массивов выделяется автоматически при сборке или
выполнении программы.
В С++ также существует возможность динамического
выделения и освобождения памяти в процессе работы
программы. Доступ к этой памяти осуществляется с
помощью указателей.
Непосредственно выделением и освобождением
памяти занимается операционная система компьютера,
к
которой
программа
обращается
через
соответствующие операторы.
10

11. Операторы для работы с памятью

Динамическое выделение памяти:
указатель = new тип;
/* выделяется память, необходимая для размещения
переменной заданного типа, адрес начального байта
присваивается указателю */
Освобождение памяти:
delete указатель;
/* освобождается память, выделенная оператором new
для указателя*/
11

12. Операторы для работы с памятью

Примеры:
int *pa;
double *px;
pa = new int;
px = new double;
cin >> *pa;
*px = 3.1415;
cout << *pa * *pa;
*pa = (int)*px;
delete px;
delete pa;
12

13. Статические и динамические массивы

Если в программе описывается статический массив,
например :
int arr[50];
то происходит не только выделение памяти для 50
элементов int, но и создается указатель-константа
arr, хранящий адрес начального байта выделенной
памяти (arr – константа, поэтому размер и положение
массива в памяти изменить нельзя).
13

14. Статические и динамические массивы

Если размер массива заранее не известен, то удобно
использовать динамический массив, выделяя для него
реально необходимую память и освобождая ее, когда
потребуется.
Динамическое выделение памяти для массива:
указатель = new тип[длина]; - выделяется
память, необходимая для длина элементов типа,
адрес начального байта присваивается указателю.
длина - это любое целочисленное выражение,
константа или переменная.
14

15. Статические и динамические массивы

Освобождение памяти, занятой массивом:
delete [] указатель; - освобождается память,
выделенная оператором new для массива.
Освобождение
неиспользуемой
динамически
выделенной
памяти

это
хороший
тон
в
программировании на С/С++.
Если память выделяется, а значение указателя на нее
теряется, то происходит «утечка памяти» - память
занята, но доступа к ней нет. Такие потерянные участки
освобождаются автоматически только при завершении
работы программы.
15

16. Обращение к элементам массивов

Пусть созданы 2 массива - статический и динамический:
int arr[50]; double *mas = new double [100];
arr и mas – это указатели по типу и имена массивов по
сути, поэтому к элементам массивов можно обращаться
либо задавая индекс в квадратных скобках, либо
используя операции с указателями:
mas[0] эквивалентно *mas
arr[i] эквивалентно *(arr+i)
mas[j] эквивалентно *(mas+j)
arr+i – адрес i-го элемента arr
mas+j – адрес j-го элемента mas
16

17. Пример динамического массива

В следующем примере определяется длина n и выделяется
память для динамического вещественного массива arr.
Затем массив заполняется случайными числами в диапазоне
[0.0, 1.0]. Далее массив выводится на экран, при этом
используются не индексы, а изменяемый в цикле указатель
на текущий элемент массива ptr.
После вывода значений динамический массив удаляется
(освобождается выделенная для него память). При этом сама
переменная-указатель arr все еще существует и ее можно
использовать повторно для создания другого одномерного
массива.
17

18. Пример динамического массива

int k, n; double *arr, *ptr;
cout << “Input array length: ”;
cin >> n;
arr = new double[n];
srand(time(0));
for (k = 0; k < n; k++)
arr[k] = (double)(rand()) / RAND_MAX;
for (ptr = arr, k = 0; k < n; k++, ptr++)
cout << *ptr << endl;
delete [] arr;
18

19. Указатель на указатель

Переменная-указатель хранится в памяти, как и все
переменные других типов, т.е. имеет определенный
адрес. Для хранения адреса указателя нужно
использовать переменную типа указатель на указатель
(двойной указатель):
int x = 13, *px, **ppx;
Использование переменных:
px = &x;
// px получает адрес x
ppx = &px;
// ppx получает адрес px
*ppx
// px или ее значение
**ppx, *px
// x или ее значение 13
19

20. Двумерный массив и указатели

Одномерный статический массив
int mas[50];
mas является константой-указателем на начальный
(нулевой) элемент массива.
Двумерный статический массив
int arr[10][20];
arr имеет специальный тип:
int (*)[20] – это указатель на целочисленный
массив длиной 20 (массив массивов).
arr не является указателем на указатель int **.
10 строк по 20 элементов располагаются в памяти
последовательно.
20

21. Двумерный массив и указатели

При создании двумерного динамического массива с n
строками и m столбцами необходимо явно выделить
память для:
• массива длины n указателей на строки
• n массивов длины m для элементов строк.
21

22. Двумерный массив и указатели

Адрес массива указателей на строки должен
сохраняться в переменной типа указатель на указатель,
например, int **x. Адрес i-й строки должен
храниться на i-й позиции в массиве указателей на
строки, т.е. в элементе x[i].
Память для строк выделяется независимо, поэтому:
• строки будут располагаться в памяти произвольно (не
последовательно)
• длины строк, в принципе, могут быть разными (если
это необходимо).

23. Двумерный динамический массив

В следующем примере после описания переменных
сначала создается динамический массив указателей на
строки (n элементов типа int*).
Затем в цикле n раз выделяется память для всех строк
длины m и формируются значения указателей на строки.
После заполнения массива случайными значениями
производится удаление динамического массива с
освобождением всей выделенной памяти: сначала
удаляются строки, а затем массив указателей на строки.
После этого сама переменная x (указатель на указатель)
все еще существует и ее можно использовать повторно для
создания другого двумерного массива.
23

24. Двумерный динамический массив

int n, m, **x, i, j;
cin >> n >> m;
x = new int* [n];
// массив указателей
for (i = 0; i < n; i++)
x[i] = new int [m];
// массивы целых
for (i = 0; i < n; i++)
for (j = 0; j < m; j++)
x[i][j] = rand();
for (i = 0; i < n; i++)
delete [] x[i];
delete [] x;
24

25. Матрица в виде одномерного массива

В некоторых случаях бывает удобно использовать
одномерный массив для представления матрицы.
Если матрица имеет n строк и m столбцов, то
соответствующий одномерный массив будет содержать
n*m элементов. Если считать, что эти элементы
располагаются в памяти последовательно «по строкам»
(m значений строки 0, затем m значений строки 1 и т.д.),
то элементу a[i][j] матрицы соответствует элемент
a[i*m+j] одномерного массива.
25

26. Матрица в виде одномерного массива

Реализация предыдущего примера с использованием
одномерного динамического массива x.
int n, m, *x, i, j;
cin >> n >> m;
x = new int [n*m];
// массив целых
for (i = 0; i < n; i++)
for (j = 0; j < m; j++)
x[i*m+j] = rand();
delete [] x;
26

27. Символы и строки в С

Символьные переменные и константы С имеют тип
char, занимают 1 байт памяти и хранят целое число –
код символа (номер символа в таблице кодировки).
Константы заключаются в одинарные кавычки:
char a, b = ’b’, c = ’4’, *d;
Для
управляющих
и
специальных
символов
используются константы с обратным слэшем:
’\n’ – новая строка
’\’’ – одинарная кавычка
’\”’ – двойная кавычка
’\\’ – обратный слэш
’\xN’ – символ с 16-ричным кодом N
’\0’ – символ с кодом 0 (конец строки)
27

28. Символы и строки в С

При обработке текстов удобно использовать не просто
массивы символов (статические или динамические), а
строки – последовательности символов, которые
заканчиваются символом с кодом 0: ’\0’ (этот символ
не учитывается при определении длины строки).
Для работы со строкой как единым объектом имеются
стандартные функции, все они используют нуль-символ
в качестве ограничителя. При использовании этих
функций в программу нужно включить заголовочный
файл cstring (string.h).
28

29. Символы и строки в С

Строковые константы заключаются в двойные кавычки:
“word", “too many words", "1 2 3 4 5“.
Нуль-символ автоматически добавляется в конец
строковых констант. Например, “too many words"
содержит 15 символов, хотя ее длина равна 14.
Строковая константа
указатель на char.
трактуется
как
константа-
29

30. Символы и строки в С

Примеры:
char stat[20], *dptr, *cptr;
cptr = “too many words“;
// изменение указателя
strcpy_s(stat, 15, “too many words“);
cout << “leng = ” << strlen(stat);
int k = strlen(stat) + 1;
dptr = new char [k];
strcpy_s(dptr,
k, stat);
30

31. Строки в С++

С++ сохраняет все возможности С для работы со
строками с нуль-символом в конце.
Кроме того, в библиотеку стандартных шаблонов С++
(STL) включен специальный тип данных string. Он
предоставляет множество операторов и функций для
более простой и удобной работы с текстами.
31

32. Области памяти для переменных

Перед запуском программы ей выделяется память,
которая включает 4 основные области:
1. Область кода – собственно программа
2. Область данных (data) – глобальные и staticпеременные
3. Стек (stack) – локальные переменные всех блоков,
параметры функций
4. Куча (heap) – область динамически выделяемой
памяти

33. Области видимости переменных

Любая переменная С/С++ имеет свою область
видимости, т.е. часть программы, в которой эту
переменную можно использовать.
Область видимости зависит от того, где была описана
(определена) переменная.
Здесь действуют следующие правила:

34. Области видимости переменных

• глобальные переменные (описанные вне функций)
видны в любом месте программы и существуют, пока
программа не закончит свою работу
• глобальные переменные с модификатором static видны
везде в том файле, где они описаны (программа на С++
может состоять из нескольких файлов), и существуют,
пока не закончится программа

35. Области видимости переменных

• переменные, описанные внутри любого блока {…}
(составного оператора, тела функции), а также
параметры функции, создаются при выполнении
данного блока, видны только в нем и удаляются при
выходе из блока
• переменные, описанные внутри блока с модификатором
static, существуют, пока выполняется программа, но
видны только внутри своего блока

36. Области видимости переменных

• переменные, описанные внутри оператора цикла
создаются и видны только в цикле, и удаляются при
завершении цикла
• если в блоке или цикле создается новая переменная с
таким же именем, как некоторая ранее определенная
старая, то новая «перекрывает» старую, т.е. старая
(даже глобальная) не будет видна, пока существует
новая.

37. Области видимости переменных

int a = 100;
double func(int d, int t)
{ float k; k = a / d + t; return k; }
void main()
{
float a = 3.1415; double *pm;
int d = 123, tt = 456, k = 0;
pm = new double[2];
for (int a = 0; a < 5; a++) k +=a;
pm[0] = k + a;
pm[1] = func(d, tt);
}
English     Русский Правила