int a new int n что это значит
Динамические массивы в C++
Обычно, объем памяти, необходимый для той или иной переменной, задается еще до процесса компиляции посредством объявления этой переменной. Если же возникает необходимость в создание переменной, размер которой неизвестен заранее, то используют динамическую память. Резервирование и освобождение памяти в программах на C++ может происходить в любой момент времени. Осуществляются операции распределения памяти двумя способами:
Функция malloc резервирует непрерывный блок ячеек памяти для хранения указанного объекта и возвращает указатель на первую ячейку этого блока. Обращение к функции имеет вид:
void *malloc(size);
Здесь size — целое беззнаковое значение, определяющее размер выделяемого участка памяти в байтах. Если резервирование памяти прошло успешно, то функция возвращает переменную типа void *, которую можно привести к любому необходимому типу указателя.
Функция — calloc также предназначена для выделения памяти. Запись ниже означает, что будет выделено num элементов по size байт.
void *calloc (nime, size);
Эта функция возвращает указатель на выделенный участок или NULL при невозможности выделить память. Особенностью функции является обнуление всех выделенных элементов.
Функция realloc изменяет размер выделенной ранее памяти. Обращаются к ней так:
char *realloc (void *p, size);
Здесь p — указатель на область памяти, размер которой нужно изменить на size. Если в результате работы функции меняется адрес области памяти, то новый адрес вернется в качестве результата. Если фактическое значение первого параметра NULL, то функция realloc работает также, как и функция malloc, то есть выделяет участок памяти размером size байт.
Для освобождения выделенной памяти используется функция free. Обращаются к ней так:
void free (void *p size);
Здесь p — указатель на участок памяти, ранее выделенный функциями malloc, calloc или realloc.
Операторы new и delete аналогичны функциям malloc и free. New выделяет память, а его единственный аргумент — это выражение, определяющее количество байтов, которые будут зарезервированы. Возвращает оператор указатель на начало выделенного блока памяти. Оператор delete освобождает память, его аргумент — адрес первой ячейки блока, который необходимо освободить.
Динамический массив — массив переменной длины, память под который выделяется в процессе выполнения программы. Выделение памяти осуществляется функциями calloc, malloc или оператором new. Адрес первого элемента выделенного участка памяти хранится в переменной, объявленной как указатель. Например, следующий оператор означает, что описан указатель mas и ему присвоен адрес начала непрерывной области динамической памяти, выделенной с помощью оператора new:
int *mas=new int[10];
Выделено столько памяти, сколько необходимо для хранения 10 величин типа int.
Фактически, в переменной mas хранится адрес нулевого элемента динамического массива. Следовательно, адрес следующего, первого элемента, в выделенном участке памяти — mas+1, а mas+i является адресом i-го элемента. Обращение к i-му элементу динамического массива можно выполнить, как обычно mas[i], или другим способом *(mas +i). Важно следить за тем, чтобы не выйти за границы выделенного участка памяти.
Когда динамический массив (в любой момент работы программы) перестает быть нужным, то память можно освободить с помощью функции free или оператора delete.
Предлагаю рассмотреть несколько задач, закрепляющих данный урок:
Задача 1
Найти сумму вещественных элементов динамического массива.
int * array = new int[n]; что на самом деле делает эта функция?
Я смущен тем, как создать динамический определенный массив:
Я понятия не имею, что это делает. Я могу сказать, что он создает указатель с именем array, который указывает на новый объект / массив int? Кто-нибудь объяснит?
8 ответов
новая выделяет объем памяти, необходимый для хранения объектов/массив, который вы запрашиваете. В этом случае n чисел int.
указатель хранит адрес этого блока памяти.
но будьте осторожны, этот выделенный блок памяти не будут освобождены, пока вы не скажете Это так писать!—4—>
в противном случае, ваша программа будет утечка памяти не менее sizeof(int) * n байт (возможно больше, в зависимости от распределения стратегии реализации).
оператор в основном делает следующее:
Он выделяет пространство в куче, равное целочисленному массиву размером N, и возвращает указатель на него, который присваивается указателю типа int* под названием «array»
он выделяет столько места в соответствии со значением n, и указатель будет указывать на массив i.e 1-й элемент массива
в C/C++ указатели и массивы (почти) эквивалентны. int *a; a[0]; вернутся *a и a[1]; вернутся *(a + 1)
но массив не может изменить указатель, на который он указывает, в то время как указатель может.
new int[n] выделит некоторые пробелы для «массива»
это создает смарт-указатель на блок памяти, достаточно большой для n целые числа, которые автоматически удаляются, когда они выходят за рамки. Эта автоматическая очистка важна, потому что она позволяет избежать сценария, когда ваш код завершает работу рано и никогда не достигает вашего delete [] array; заявление.
другой (вероятно, предпочтительный) вариант будет использовать std::vector если вам нужен массив, способный динамически изменять размер. Это хорошо, когда вам нужно неизвестное количество пространства, но у него есть некоторые недостатки (непостоянное время для добавления/удаления элемента). Вы можете создать массив и добавить к нему элементы с помощью чего-то вроде:
общая форма new, применяемая к одномерным массивам, выглядит следующим образом:
Урок №85. Динамическое выделение памяти
Обновл. 13 Сен 2021 |
Язык С++ поддерживает три основных типа выделения (или «распределения») памяти, с двумя из которых, мы уже знакомы:
Статическое выделение памяти выполняется для статических и глобальных переменных. Память выделяется один раз (при запуске программы) и сохраняется на протяжении работы всей программы.
Автоматическое выделение памяти выполняется для параметров функции и локальных переменных. Память выделяется при входе в блок, в котором находятся эти переменные, и удаляется при выходе из него.
Динамическое выделение памяти является темой этого урока.
Динамическое выделение переменных
Как статическое, так и автоматическое распределение памяти имеют два общих свойства:
Размер переменной/массива должен быть известен во время компиляции.
Выделение и освобождение памяти происходит автоматически (когда переменная создается/уничтожается).
В большинстве случаев с этим всё ОК. Однако, когда дело доходит до работы с пользовательским вводом, то эти ограничения могут привести к проблемам.
Если нам нужно объявить размер всех переменных во время компиляции, то самое лучшее, что мы можем сделать — это попытаться угадать их максимальный размер, надеясь, что этого будет достаточно:
Это плохое решение, по крайней мере, по трем причинам:
Во-первых, теряется память, если переменные фактически не используются или используются, но не все. Например, если мы выделим 30 символов для каждого имени, но имена в среднем будут занимать по 15 символов, то потребление памяти получится в два раза больше, чем нам нужно на самом деле. Или рассмотрим массив rendering : если он использует только 20 000 полигонов, то память для других 20 000 полигонов фактически тратится впустую (т.е. не используется)!
Во-вторых, память для большинства обычных переменных (включая фиксированные массивы) выделяется из специального резервуара памяти — стека. Объем памяти стека в программе, как правило, невелик: в Visual Studio он по умолчанию равен 1МБ. Если вы превысите это значение, то произойдет переполнение стека, и операционная система автоматически завершит выполнение вашей программы.
В Visual Studio это можно проверить, запустив следующий фрагмент кода:
Лимит в 1МБ памяти может быть проблематичным для многих программ, особенно где используется графика.
В-третьих, и самое главное, это может привести к искусственным ограничениям и/или переполнению массива. Что произойдет, если пользователь попытается прочесть 500 записей с диска, но мы выделили память максимум для 400? Либо мы выведем пользователю ошибку, что максимальное количество записей — 400, либо (в худшем случае) выполнится переполнение массива и затем что-то очень нехорошее.
К счастью, эти проблемы легко устраняются с помощью динамического выделения памяти. Динамическое выделение памяти — это способ запроса памяти из операционной системы запущенными программами по мере необходимости. Эта память не выделяется из ограниченной памяти стека программы, а выделяется из гораздо большего хранилища, управляемого операционной системой — кучи. На современных компьютерах размер кучи может составлять гигабайты памяти.
Для динамического выделения памяти одной переменной используется оператор new:
В примере, приведенном выше, мы запрашиваем выделение памяти для целочисленной переменной из операционной системы. Оператор new возвращает указатель, содержащий адрес выделенной памяти.
Для доступа к выделенной памяти создается указатель:
Затем мы можем разыменовать указатель для получения значения:
Вот один из случаев, когда указатели полезны. Без указателя с адресом на только что выделенную память у нас не было бы способа получить доступ к ней.
Как работает динамическое выделение памяти?
На вашем компьютере имеется память (возможно, большая её часть), которая доступна для использования программами. При запуске программы ваша операционная система загружает эту программу в некоторую часть этой памяти. И эта память, используемая вашей программой, разделена на несколько частей, каждая из которых выполняет определенную задачу. Одна часть содержит ваш код, другая используется для выполнения обычных операций (отслеживание вызываемых функций, создание и уничтожение глобальных и локальных переменных и т.д.). Мы поговорим об этом чуть позже. Тем не менее, большая часть доступной памяти компьютера просто находится в ожидании запросов на выделение от программ.
Когда вы динамически выделяете память, то вы просите операционную систему зарезервировать часть этой памяти для использования вашей программой. Если ОС может выполнить этот запрос, то возвращается адрес этой памяти обратно в вашу программу. С этого момента и в дальнейшем ваша программа сможет использовать эту память, как только пожелает. Когда вы уже выполнили с этой памятью всё, что было необходимо, то её нужно вернуть обратно в операционную систему, для распределения между другими запросами.
В отличие от статического или автоматического выделения памяти, программа самостоятельно отвечает за запрос и обратный возврат динамически выделенной памяти.
Освобождение памяти
Когда вы динамически выделяете переменную, то вы также можете её инициализировать посредством прямой инициализации или uniform-инициализации (в С++11):
Когда уже всё, что требовалось, выполнено с динамически выделенной переменной — нужно явно указать для С++ освободить эту память. Для переменных это выполняется с помощью оператора delete:
Оператор delete на самом деле ничего не удаляет. Он просто возвращает память, которая была выделена ранее, обратно в операционную систему. Затем операционная система может переназначить эту память другому приложению (или этому же снова).
Хотя может показаться, что мы удаляем переменную, но это не так! Переменная-указатель по-прежнему имеет ту же область видимости, что и раньше, и ей можно присвоить новое значение, как и любой другой переменной.
Обратите внимание, удаление указателя, не указывающего на динамически выделенную память, может привести к проблемам.
Висячие указатели
Язык C++ не предоставляет никаких гарантий относительно того, что произойдет с содержимым освобожденной памяти или со значением удаляемого указателя. В большинстве случаев, память, возвращаемая операционной системе, будет содержать те же значения, которые были у нее до освобождения, а указатель так и останется указывать на только что освобожденную (удаленную) память.
Указатель, указывающий на освобожденную память, называется висячим указателем. Разыменование или удаление висячего указателя приведет к неожиданным результатам. Рассмотрим следующую программу:
Процесс освобождения памяти может также привести и к созданию нескольких висячих указателей. Рассмотрим следующий пример:
Есть несколько рекомендаций, которые могут здесь помочь:
Во-первых, старайтесь избегать ситуаций, когда несколько указателей указывают на одну и ту же часть выделенной памяти. Если это невозможно, то выясните, какой указатель из всех «владеет» памятью (и отвечает за её удаление), а какие указатели просто получают доступ к ней.
Во-вторых, когда вы удаляете указатель, и, если он не выходит из области видимости сразу же после удаления, то его нужно сделать нулевым, т.е. присвоить значение 0 (или nullptr в С++11). Под «выходом из области видимости сразу же после удаления» имеется в виду, что вы удаляете указатель в самом конце блока, в котором он объявлен.
Правило: Присваивайте удаленным указателям значение 0 (или nullptr в C++11), если они не выходят из области видимости сразу же после удаления.
Оператор new
При запросе памяти из операционной системы в редких случаях она может быть не выделена (т.е. её может и не быть в наличии).
Во многих случаях процесс генерации исключения оператором new (как и сбой программы) нежелателен, поэтому есть альтернативная форма оператора new, которая возвращает нулевой указатель, если память не может быть выделена. Нужно просто добавить константу std::nothrow между ключевым словом new и типом данных:
В примере, приведенном выше, если оператор new не возвратит указатель с динамически выделенной памятью, то возвратится нулевой указатель.
Разыменовывать его также не рекомендуется, так как это приведет к неожиданным результатам (скорее всего, к сбою в программе). Поэтому наилучшей практикой является проверка всех запросов на выделение памяти для обеспечения того, что эти запросы будут выполнены успешно и память выделится:
Поскольку не выделение памяти оператором new происходит крайне редко, то обычно программисты забывают выполнять эту проверку!
Нулевые указатели и динамическое выделение памяти
Нулевые указатели (указатели со значением 0 или nullptr ) особенно полезны в процессе динамического выделения памяти. Их наличие как бы сообщаем нам: «Этому указателю не выделено никакой памяти». А это, в свою очередь, можно использовать для выполнения условного выделения памяти:
Удаление нулевого указателя ни на что не влияет. Таким образом, в следующем нет необходимости:
Вместо этого вы можете просто написать:
Если ptr не является нулевым, то динамически выделенная переменная будет удалена. Если значением указателя является нуль, то ничего не произойдет.
Утечка памяти
Динамически выделенная память не имеет области видимости, т.е. она остается выделенной до тех пор, пока не будет явно освобождена или пока ваша программа не завершит свое выполнение (и операционная система очистит все буфера памяти самостоятельно). Однако указатели, используемые для хранения динамически выделенных адресов памяти, следуют правилам области видимости обычных переменных. Это несоответствие может вызвать интересное поведение, например:
Здесь мы динамически выделяем целочисленную переменную, но никогда не освобождаем память через использование оператора delete. Поскольку указатели следуют всем тем же правилам, что и обычные переменные, то, когда функция завершит свое выполнение, ptr выйдет из области видимости. Поскольку ptr — это единственная переменная, хранящая адрес динамически выделенной целочисленной переменной, то, когда ptr уничтожится, больше не останется указателей на динамически выделенную память. Это означает, что программа «потеряет» адрес динамически выделенной памяти. И в результате эту динамически выделенную целочисленную переменную нельзя будет удалить.
Это называется утечкой памяти. Утечка памяти происходит, когда ваша программа теряет адрес некоторой динамически выделенной части памяти (например, переменной или массива), прежде чем вернуть её обратно в операционную систему. Когда это происходит, то программа уже не может удалить эту динамически выделенную память, поскольку больше не знает, где выделенная память находится. Операционная система также не может использовать эту память, поскольку считается, что она по-прежнему используется вашей программой.
Утечки памяти «съедают» свободную память во время выполнения программы, уменьшая количество доступной памяти не только для этой программы, но и для других программ также. Программы с серьезными проблемами с утечкой памяти могут «съесть» всю доступную память, в результате чего ваш компьютер будет медленнее работать или даже произойдет сбой. Только после того, как выполнение вашей программы завершится, операционная система сможет очистить и вернуть всю память, которая «утекла».
Хотя утечка памяти может возникнуть и из-за того, что указатель выходит из области видимости, возможны и другие способы, которые могут привести к утечкам памяти. Например, если указателю, хранящему адрес динамически выделенной памяти, присвоить другое значение:
объясните грамотно как это работает
Файл: Считать из файла посимвольно текст и вывести его на экран. Объясните, как это работает.
Дали нам в вузе (1 курс) такой вот код, чтобы считать с файла посимвольно текст и вывести его //.
Объясните как это работает
Разбирал чужие решения задачи на словари и возникли затруднения с пониманием пары строк. Вот.
1. 0 и NULL одно и тоже.
Есть. Визуальная при чтении исходника.
Добавлено через 1 минуту
msdn’ом меня не напугаешь. :gigi:
Цитата из Стандарта.
Конечно, возможно, это специфично для Microsoft, но тем не менее.
А это специфично для всего языка, так что тем более (((:
Конечно, возможно, это специфично для Microsoft, но тем не менее.
Насколько я понимаю, согласно указанной выдержке из документации, для микрософтовского компилятора и библиотек, установленных под виндой, гарантируется, что значение NULL равно нулю. Но тем не менее вручную значение «0» писать не стОит. Хотя бы потому, что визуально код с NULL понятнее
> 2)можно менять int * arr = NULL на int * arr = <0>или int * arr = 0
Объясните не понимаю как это работает
Мне нужно написать программу на турбо паскале\фри паскале, но это программа должна быть в.
Объясните пожалуйста, как работает это приложение
Когда я писал свой шифратор, мне предложили взять за основу этот шифратор с паролем, ничего не.
Обращение к классу (объясните как это работает )
у меня есть 2 класса как мне из первого класса передать значения во второй класс
int *array = new int[n]; what is this function actually doing?
I am confused about how to create a dynamic defined array:
I have no idea what this is doing. I can tell it’s creating a pointer named array that’s pointing to a new object/array int? Would someone care to explain?
8 Answers 8
new allocates an amount of memory needed to store the object/array that you request. In this case n numbers of int.
The pointer will then store the address to this block of memory.
But be careful, this allocated block of memory will not be freed until you tell it so by writing
Otherwise, your program will leak memory of at least sizeof(int) * n bytes (possibly more, depending on the allocation strategy used by the implementation).
The statement basically does the following:
It allocates space on the heap equal to an integer array of size N, and returns a pointer to it, which is assigned to int* type pointer called «array»
It allocates that much space according to the value of n and pointer will point to the array i.e the 1st element of array
The general form of new as it applies to one-dimensional arrays appears as follows:
But array can’t change the pointer it points to while pointer can.
new int[n] will allocate some spaces for the «array»
As of C++11, the memory-safe way to do this (still using a similar construction) is with std::unique_ptr :
This creates a smart pointer to a memory block large enough for n integers that automatically deletes itself when it goes out of scope. This automatic clean-up is important because it avoids the scenario where your code quits early and never reaches your delete [] array; statement.
Another (probably preferred) option would be to use std::vector if you need an array capable of dynamic resizing. This is good when you need an unknown amount of space, but it has some disadvantages (non-constant time to add/delete an element). You could create an array and add elements to it with something like: