Найти в Дзене

Выделение памяти и область видимости в С++

Указатели - это непростая тема. Во всяком случае мне она всегда давалась как-то нелегко. Вроде всё понятно, а начнёшь разбираться с конкретным примером и запутаешься. Вот и сегодня я решил вам подкинуть такой примерчик. Проверьте свою нервную систему на прочность - если вы новичок, то с первого раза понять всё будет сложно. Это касается не только С++, но, поскольку пример будет на этом языке, то и заголовок такой. Начинающие программисты на С++ из-за неполного понимания работы с указателями (хотя я и сам не обладаю полным пониманием))) могут столкнуться с труднонаходимыми ошибками в программе. Пример: Всё это прекрасно компилируется, но в ходе выполнения программы могут возникнуть неожиданности. Дело в том, что переменная LocalVar объявлена внутри функции. То есть является локальной. И поэтому в момент возврата адреса этой переменной из функции, самой переменной уже не существует и адрес ссылается на память, которая в это время может быть использована для каких-то других задач. И это в
Указатели - это непростая тема. Во всяком случае мне она всегда давалась как-то нелегко. Вроде всё понятно, а начнёшь разбираться с конкретным примером и запутаешься. Вот и сегодня я решил вам подкинуть такой примерчик. Проверьте свою нервную систему на прочность - если вы новичок, то с первого раза понять всё будет сложно.

Это касается не только С++, но, поскольку пример будет на этом языке, то и заголовок такой.

Начинающие программисты на С++ из-за неполного понимания работы с указателями (хотя я и сам не обладаю полным пониманием))) могут столкнуться с труднонаходимыми ошибками в программе. Пример:

Всё это прекрасно компилируется, но в ходе выполнения программы могут возникнуть неожиданности. Дело в том, что переменная LocalVar объявлена внутри функции. То есть является локальной. И поэтому в момент возврата адреса этой переменной из функции, самой переменной уже не существует и адрес ссылается на память, которая в это время может быть использована для каких-то других задач.

И это всё непредсказуемо. Поэтому программа может долго работать правильно, но в какой-то момент произойдёт сбой.

Компилятор нас об этом, кстати, честно предупреждает:

-2
[Warning] address of local variable 'LocalVar' returned [-Wreturn-local-addr]
[Предупреждение] возвращен адрес локальной переменной 'localVar' [-Wreturn-local-addr]

Но это не является синтаксической ошибкой, поэтому компиляция будет выполнена. Однако, поскольку память освобождается автоматически, то при повторном вызове функции, скорее всего, будет выделен тот же участок памяти. И если перед этим туда что-то будет записано, то и результат будет трудно предсказуем (неизвестно, кто и что туда перед этим запишет, ведь повторно функция может быть вызвана не сразу, а через длительное время.

Ошибки области видимости возникают потому, что С++ выделенную для локальных переменных память освобождает автоматически. И чтобы избежать таких неприятностей, необходим блок памяти, который будет управляться программистом. В этом блоке можно выделять память под переменные и удалять их независимо от того, что по этому поводу думает С++. Такой блок памяти называется кучей (heap).

Память в куче можно выделить, используя оператор new. Пример:

-3

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

Кроме того, хотя и не обязательно, но желательно ПОСЛЕ освобождения памяти присвоить указателю значение NULL или 0. В этом случае если программист ошибётся и попытается записать данные по несуществующему адресу (память-то уже освобождена), произойдёт ошибка времени выполнения и программа остановится аварийно. А это всё-таки лучше, чем неправильная работа программы из-за описанной выше ситуации. И найти такую ошибку намного проще.

И обратите внимание, что в первом примере оба вызова функции вернули один и тот же адрес. Поэтому, собственно, и по адресу в указателе Р1, и по адресу в указателе Р2 одно и то же значение.

Во втором примере мы при каждом вызове выделяем память вручную, и она не освобождается автоматически. Поэтому у нас получилось два блока памяти в каждом из которых разные значения.

На этом всё. Подписывайтесь на канал, чтобы ничего не пропустить.