Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате |
Форум программистов > C/C++: Для новичков > [FAQ] Динамические массивы |
Автор: bsa 26.12.2007, 22:07 | ||||||||||||||||||||||||||||||||||||||||||||||||||||
Так как язык С++ добавляет свои средства работы с массивами к имеющимся в языке Си, то рассмотрим сначала то, чем располагает Си, а затем уже то, что привносит С++. Динамические массивы в Си Массив - это набор однотипных данных, доступ к которым может быть осуществлен по индексу, т.е. по номеру элемента. В Си принято нумеровать элементы с нуля, поэтому первый элемент всегда имеет индекс 0, а последний - (N-1), где N - это размер массива (количество элементов). Встроенные средства языка программирования Си позволяют создавать статические массивы - массивы, число элементов которых задается на этапе компиляции и в процессе работы программы не меняется. Но как только сложность задач выходит за банальное "Hello, world!", возникает необходимость менять размеры массива в процессе работы программы... Что представляет собой массив на уровне ЭВМ? Это непрерывный кусок памяти, в который помещаются все элементы массива. Таким образом, для изменения количества элементов в массиве нужно менять размер этого куска. Но для начала его нужно выделить... Последняя версия стандарта языка Си позволяет создавать автоматические динамические массивы. Они существуют только в пределах текущей области видимости и при выходе из нее уничтожаются. Синтаксис записи аналогичен статическим массивам, только в качестве указателя размера выступает не числовая константа, а переменная:
Стандартная библиотека Си предоставляет средства для работы с динамической памятью, такие как: http://www.cplusplus.com/reference/clibrary/cstdlib/malloc.html, http://www.cplusplus.com/reference/clibrary/cstdlib/calloc.html - выделение памяти http://www.cplusplus.com/reference/clibrary/cstdlib/free.html - освобождение памяти http://www.cplusplus.com/reference/clibrary/cstdlib/realloc.html - изменение размера выделенной области памяти (может замещать собой и malloc, и free) Для их использования надо подключить заголовочный файл http://www.cplusplus.com/reference/clibrary/cstdlib/:
Функция calloc() производит выделение памяти под набор элементов одинакого размера и его зануление. Первым параметром указывается количество необходимых элементов, а вторым размер элемента в байтах:
Функция realloc() позволяет изменять размер области памяти, которая выделена функциями realloc(), malloc() или calloc() (на самом деле, она еще может выделять память, как malloc, и освобождать, как free):
Вот пример использования динамического массива:
Как нетрудно заметить, в конце функции идет освобождение памяти, выделенной под массив. Чтобы избежать утечек памяти, нужно всегда освобождать память, как только она перестала быть нужной. Динамические массивы нельзя копировать путем присваивания указателей:
Стоит иметь в виду, что указатели на внутренние элементы динамического массива использовать не очень безопасно. Так как после очередного изменения размера массива они могут стать неверны, что приведет к возникновению трудно отслеживаемых ошибок. Указатели на внутренние элементы стоит применять только там, где не предполагается изменений размеров массива. Создать полностью динамический многомерный массив стандартными средствами языка Си невозможно (как создать частично динамический массив будет сказано ниже). Но можно сымитировать одним из двух способов: 1. через массив массивов 2. через одномерный массив Рассмотрим первый вариант реализации многомерных динамических массивов на примере двумерного. Для этого создадим указатель на указатель на, например, int:
Для второго способа нужно создать одномерный массив, число элементов которого равно произведению всех размерностей:
Как вариант можно создать многомерный массив, у которого только одна размерность динамическая, при этом скорость работы с ним аналогична статическому массиву (это не относится ко времени выделения памяти). Динамической может быть только первая размерность (например, для двумерного массива это количество строк). Для этого надо создать указатель на массив, размерность которого на один меньше необходимой (например, чтобы получить динамический двумерный массив, нужно создать указатель на одномерный массив с нужным числом столбцов). Например:
После определения указателя нужно выделить под массив память (далее для простоты рассматривается двумерный массив):
--------------------------------------------------------------------------------------------------------------------- Динамические массивы в Си++ Массив - это набор однотипных данных, доступ к которым может быть осуществлен по индексу, т.е. по номеру элемента. В Си++, так же как и вв Си, принято нумеровать элементы с нуля, поэтому первый элемент всегда имеет индекс 0, а последний - (N-1), где N - это размер массива (количество элементов). Средства языка программирования Си++ позволяют создавать статические массивы - массивы, число элементов которых задается на этапе компиляции и в процессе работы программы не меняется. Но как только сложность задач выходит за банальное "Hello, world!", возникает необходимость менять размеры массива в процессе работы программы... Что представляет собой массив на уровне ЭВМ? Это непрерывный кусок памяти, в который помещаются все элементы массива. Таким образом, для изменения количества элементов в массиве нужно менять размер области памяти. В языке С++ есть встроенные средства, для работы с динамической памятью - new[] и delete[], позволяющие выделять память под массивы и освобождать ее. Чтобы выделить память с помощью new нужно указать тип элемента массива после ключевого слова new и их количество в квадратных скобках. Чтобы освободить память, занимаемую выделенным таким образом массивом необходимо вызвать delete[]. В отличие от free(), delete[] может принимать в качестве аргумента и нулевой указатель, в этом случае он ничего не сделает. Пример:
Так как vector это шаблон, то нужно указать тип объекта, которые будут храниться в массиве. Это делается с помощью угловых скобок: std::vector<int>. Вот небольшой пример использования шаблона vector:
Создать полностью динамический многомерный массив стандартными средствами языка Си++ невозможно (как создать частично динамический массив будет сказано ниже). Но можно сымитировать одним из двух способов: 1. через массив массивов 2. через одномерный массив Рассмотрим первый вариант реализации многомерных динамических массивов на примере двумерного:
Для второго способа нужно создать одномерный массив, число элементов которого равно произведению всех размерностей:
Можно совместить два этих способа:
Как вариант можно создать многомерный массив, у которого только одна размерность динамическая, при этом скорость работы с ним аналогична статическому массиву. Динамической может быть только первая размерность (например, для двумерного массива это количество строк). Для этого надо создать указатель на массив, размерность которого на один меньше необходимой (например, чтобы получить динамический двумерный массив, нужно создать указатель на одномерный массив с нужным числом столбцов). Например:
После определения указателя нужно выделить под массив память (далее для простоты рассматривается двумерный массив):
http://forum.vingrad.ru/index.php?show_type=forum&showtopic=269794&kw=faq-c++ |
Автор: archimed7592 26.12.2007, 22:35 |
bsa, а где-же new/delete? ![]() Ещё, прошу добавить ссылки: 1. На википедию(массив) 2. На cppreference.com или куда-нибудь ещё(malloc, free, realloc и т.д.) |
Автор: Damarus 27.12.2007, 01:21 |
bsa, можно ещё написать про многомерные динамические массивы. |
Автор: archimed7592 27.12.2007, 01:35 |
Damarus, согласен - а я проглядел ![]() |
Автор: archimed7592 3.1.2008, 12:39 | ||
Внимательно прочитал статью. Всё отлично. Есть только одно замечание.
Зависит от того, что именно понимать под многомерным дин. массивом. Есть способ динамически создать многомерный массив у которого только лишь одна из размерностей будет переменной. В статье этот момент нужно проговорить(возможно, привести пример). |
Автор: bsa 3.1.2008, 13:36 | ||
archimed7592, ты имеешь в виду что-то типа:
Так что этот пункт прошу проверить отдельно и с пристрастием. |
Автор: archimed7592 3.1.2008, 14:35 | ||
Да. На С++ это будет выглядеть так:
Теория тут следующая: 1. Скорость доступа [почти] такая же как у статических массивов. 2. Скорость создания такая же, как у одномерного динамического массива. 3. arr[i][j][k] == *((int *)arr + (i * 5 + j) * 6 + k) (могу ошибаться, но вроде так) - компилятор сам делает эти вычисления(как и со статическими массивами). 4. При создании такого массива, динамической может быть только первая(крайняя левая) размерность(остальные константы). На самом деле обо всём этом писать не обязательно(на твоё усмотрение). Я лишь хотел бы, чтобы либо не было фразы "создать динамический многомерный массив средствами языка невозможно", либо эта фраза была уточнена в виду упомянутого способа. |
Автор: archimed7592 3.1.2008, 15:29 |
Лучше показать, что эта величина может быть переменной(к примеру, считать её из потока стандартного ввода). |
Автор: JackYF 3.1.2008, 16:33 | ||
Извиняюсь за занудность, но я же назначался вычитщиком? ![]() Массив - это набор а последний - (N-1) таким образом, для достаточно для организации Разве что, sizeof(p) ... И, чтобы избежать утечек памяти, нужно
... не происходило, нужно ... запятая не нужна сЫмитировать У меня всё, сама статья понравилась. Добавлено через 2 минуты и 43 секунды P.S. а кто главред? |
Автор: Alexeis 3.1.2008, 16:55 |
Хотелось бы еще про динамические строки, как частный случай массивов переменной длинны. Напрашивается ГлавВред ![]() |
Автор: archimed7592 3.1.2008, 17:01 | ||
Ты ![]()
Только если отдельной статьёй, а то слишком много получится(разве о них не написано в прикреплённой теме "указатели, строки, классы"?) |
Автор: bsa 3.1.2008, 17:14 | ||||
Не. Разве что разместить ссылку на статью про строки, как частный вариант массивов. Но не более того. А то мне уже самому страшно смотреть на то, что получилось - ОЧЕНЬ МНОГО! Добавлено @ 17:16
archimed7592: Оффтоп про alloca перенесён в отдельную http://forum.vingrad.ru/index.php?showtopic=190823. |
Автор: Fazil6 3.1.2008, 17:48 | ||
хм... почему это? Вообще тема new/delete нераскрыта... Лучше статья будет читаться, если разбить ее хотябы на подразделы с собственными подзаголовками причем раз уж пытаешься объять необъятное, то четко разделить C и С++, а вообще как и раньше мои претензии по объему. Опять же я, например, так и не понял почему рассмотрение началось с функции realloc , а не calloc и резонный вопрос почему для выделения памяти существуют 2 функции |
Автор: archimed7592 10.1.2008, 22:51 | ||||||
Ты уверен, что это эквивалентно? ;) Могу заблуждаться, но, IIRC, помимо всего прочего, calloc инициализирует выделенную область памяти нулями.
У меня будет маленькая просьба, относительно realloc: либо из статьи вообще убрать упоминания(как в тексте, так и в коде) о том, что с помощью realloc можно и выделять и освобождать память, либо вынести это в примечание. Для этого есть несколько причин: 1. Информация "излишняя" т.е., воспользовавшись этой информацией, новичек не сможет сделать ничего такого чего он не мог бы сделать с помощью malloc+free. 2. Использовать realloc как универсальную ф-цию нужно в достаточно редких случаях и отличить эти случаи от тех, где надо бы воспользоваться malloc+free сможет не каждый новичек, а, как следствие, статья некоторых новичков статья научит плохому стилю, чего хотелось бы не допускать. И ещё одна просьба: насколько я понял ты отделил Си от С++(я чесслово уже не помню как было прежде). Дык вот, информация о многомерных массивах осталась в "смешанном" виде - нужно разделить. Потом посмотрим, но учти, что, скорее всего, будем делить на две статьи(одна для Си, другая для плюс-плюс). Добавлено @ 22:53 Кстати, если про calloc не заблуждаюсь, то неплохо было бы об этом написать.
|
Автор: bsa 10.1.2008, 23:30 | ||
Оказалось, что не заблуждаешься. Все замечания постараюсь исправить. |
Автор: bsa 11.1.2008, 12:04 |
Кстати, если статью разделять на С и С++, то, получается, придется копировать теорию, которая в целом общая. |
Автор: JackYF 11.1.2008, 14:28 |
хм... а может, давайте оставим одну статью, но сделаем у неё три больших заголовка: "Общая теория", "Применение в С", "Применение в С++". И структирированно будет, и материалы по одному вопросу в одном месте. |
Автор: archimed7592 11.1.2008, 14:40 | ||||
Не обязательно. Просто в С++ дать ссылку на С(мол, не понимаете о чём речь, прочтите сначала "это").
Идея интересная, но я вот боюсь представить, что будет, если вдруг захочется добавить к этим динамическим массивам что-нибудь ещё - я и так устаю, пока скрол прокручиваю ![]() Может быть просто разделить 1. одномерные 2. двухмерные 3. что-то ещё(уверен, что в скором будущем понадобится добавлять). и внрутри этих, уже более маленьких статей делать три мега-заголовка? Просто динамические массивы сами по себе тема достаточно обширная(для новичков) ![]() |
Автор: bsa 11.1.2008, 17:06 | ||
Во-первых, смысла писать про двумерные нет никакого. Думаю, надо написать про многомерные на примере двумерных. Во-вторых, читать статью про многомерные массивы бесполезно, не зная, как создаются одномерные и как с ними работать. |
Автор: archimed7592 11.1.2008, 17:10 | ||||
Ну да, я хотел написать многомерные ![]()
Ок, а с чего ты взял, что читающий не знает как создаются одномерные? ;) Ведь часто встречаются вопросы "вот так одномерные, а двумерные как?". Ну а если читающий действительно не знает, то ничто не мешает дать ссылку на одномерные(в самом начале). |
Автор: xvr 25.1.2008, 16:49 |
Хочу добавить свои 5 коп (если еще не поздно). Было бы неплохо упомянуть где-нибудь (желательно поближе к началу), что крайне не рекомендуется получать указатели на элементы динамических массивов (как созданных через malloc/realloc, так и к их STL братьям vector), т.к. адреса как собственно массивов так и их элементов могут меняться при изменении размера массивов. А указатели на элементы, сохраненные где-то в стороне приводят к труднонаходимым ошибкам. По этим граблям ходят довольно много народу ![]() |
Автор: JackYF 25.1.2008, 17:26 | ||
Да, поддерживаю. |
Автор: archimed7592 25.1.2008, 17:28 |
Это конечно всё очень хорошо, но, куда же подевался bsa :'( . |
Автор: bsa 28.1.2008, 18:31 |
archimed7592, я тут. А что? |
Автор: archimed7592 28.1.2008, 18:54 | ||
![]() |
Автор: bsa 29.1.2008, 11:33 |
Вообще-то там все в едином стиле. Т.е. сначала капелька теории, затем Си и в конце Си++. Просто про многомерные массивы там 3 варианта написано. Вот и чередуются. Может мне просто сразу разделить статьи на две? |
Автор: archimed7592 29.1.2008, 12:01 |
![]() |
Автор: bsa 29.1.2008, 15:27 |
Разделил статью. Проверяйте - у меня уже глаза не смотрит. |
Автор: JackYF 29.1.2008, 16:04 |
У меня тоже, только причина другая. Просмотрел навскидку - всё вроде логично и разделено получилось. |
Автор: bsa 29.1.2008, 20:24 | ||
Откуда знаешь? может у меня та же ![]() |
Автор: JackYF 29.1.2008, 20:48 |
![]() ![]() |
Автор: archimed7592 29.1.2008, 20:51 |
Ребят, кончай флеймить ![]() bsa, молодец. Подробно посмотреть смогу только завтра... |
Автор: JackYF 29.1.2008, 22:01 |
один фиг, кроме нас пока эту тему никто не читает ![]() |
Автор: bsa 30.1.2008, 11:40 |
JackYF, не - я ничего не праздновал. Просто уже опух от компов. ![]() |
Автор: archimed7592 30.1.2008, 14:56 | ||||||||||
Си:
Эммм... В чём идея применения do..while(0)?
Мне кажется что нужно убрать эту информацию: на начальном этапе новичку не нужно знать этой информации, ибо это может привести к мыслям "а всё равно в конце само по себе освободится", а на продвинутом этапе программист так или иначе сам узнаёт эту информацию без помощи ФАКа ![]()
Мне не совсем понятен смысл слова "широкого" - "узкое" использование безопасно что ль? ![]()
Почему это вдруг скорость сократилась? ![]()
![]() Добавлено через 2 минуты и 54 секунды Учитывая то, что она прибита к потолку, я бы хорошенько подумал, прежде чем делать такие выводы ![]() |
Автор: bsa 30.1.2008, 16:58 |
Поправил. |