Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: Возвращение указателя на динамический массив
Форум разработчиков электроники ELECTRONIX.ru > Сайт и форум > В помощь начинающему > Программирование
Nikitoc
Всем привет!
Вопросик простой, но для полноты картины происходящего в компиляторе (или в МК) мне нужно этот вопрос разъяснить. Допустим, есть какая-то функция:
Код
WORD *Multiple (BYTE A, BYTE B){
           WORD *result;
           result = (WORD *)malloc(1);
           *result = A * B;
           return result;
       }


Собственно вопросы:

1. Где необходимо удалять эту переменную result (я имею в виду вызов ф-ции free())? Только в той функции, в которой она была выделена (Multiple()) или же можно сделать это в вызывающей функции по полученному указателю?
2. Я правильно понимаю, что возвращать указатель можно потому, что динамически выделенная переменная помещается в кучу, а не в стек?
3. Как называется область оперативной памяти, которая не отводится под кучу или стек? Как она используется компилятором (что туда "ложится")?
ys05
Цитата(Nikitoc @ Mar 1 2012, 18:03) *
1. Где необходимо удалять эту переменную result (я имею в виду вызов ф-ции free())? Только в той функции, в которой она была выделена (Multiple()) или же можно сделать это в вызывающей функции по полученному указателю?
2. Я правильно понимаю, что возвращать указатель можно потому, что динамически выделенная переменная помещается в кучу, а не в стек?
3. Как называется область оперативной памяти, которая не отводится под кучу или стек? Как она используется компилятором (что туда "ложится")?

1. free можно вызывать в вызывающей функции и в другой функции и т.д.
2. да, правильно, куча от стека не зависит
3. вообще обычно под стек память резервируется конкретно, а под рост кучи остается вся остальная; хотя может где-то и по другому
toweroff
ойой
возвращать из функции адрес локальной переменной... help.gif
xemul
Цитата(toweroff @ Mar 1 2012, 20:09) *
ойой
возвращать из функции адрес локальной переменной...

Возвращается адрес блока, выделенного malloc(). Если у ТС есть охота на траблы, почему бы и нет?
toweroff
Цитата(xemul @ Mar 1 2012, 20:20) *
Возвращается адрес блока, выделенного malloc()

да не суть laughing.gif
Сергей Борщ
QUOTE (Nikitoc @ Mar 1 2012, 17:03) *
1. Где необходимо удалять эту переменную result (я имею в виду вызов ф-ции free())? Только в той функции, в которой она была выделена (Multiple()) или же можно сделать это в вызывающей функции по полученному указателю?
В том месте, где вам больше не нужна та память, на которую указывает указатель. Совершенно не обязательно в этой функции. Вся информация, необходимая для освобождения памяти хранится в скрытом виде внутри выделенного malloc() блока и для доступа к ней достаточно лишь указателя, который вернул malloc().
По второму вопросу: совершенно верно.
QUOTE (Nikitoc @ Mar 1 2012, 17:03) *
3. Как называется область оперативной памяти, которая не отводится под кучу или стек? Как она используется компилятором (что туда "ложится")?
Никак не используется. Поэтому либо под стек выделяют всю оставшуюся после резервирования кучи память либо наоборот - под кучу всю оставшуюся после резервирования стека.

QUOTE (xemul @ Mar 1 2012, 18:20) *
Возвращается адрес блока, выделенного malloc(). Если у ТС есть охота на траблы, почему бы и нет?
Какие траблы вы имеете ввиду? И как вы себе представляете другое использование кучи?

QUOTE (toweroff @ Mar 1 2012, 18:40) *
да не суть laughing.gif
Именно что суть. Да вы не стесняйтесь, расскажите нам в чем тут ойой. Посмеемся вместе. Над вами.
toweroff
Цитата(Сергей Борщ @ Mar 1 2012, 22:18) *
Именно что суть

кучу использую крайне редко
отсюда и у меня некоторое, возможно, непонимание использования оной
то есть malloc просматривает при выделении нужную "дырку", в не зависимости, как и где вызван запрос на область памяти?
посему - free освобождает этот кусок, образуя дырку?
и без разницы где это вызывается?
также, выделяя/освобождая как попало куски памяти, получаем вариант, когда суммарно память свободная есть, а выделить ее не можем, потому как нет одной дырки подходящего размера?

Цитата(Сергей Борщ @ Mar 1 2012, 22:18) *
Посмеемся вместе. Над вами.

валяйте
xemul
Цитата(Сергей Борщ @ Mar 1 2012, 22:18) *
Какие траблы вы имеете ввиду? И как вы себе представляете другое использование кучи?

Я не про использование кучи - оно вполне естественно, - а про несвязанные явно выделение и освобождение памяти.
Мне больше по душе, когда "выделил - ... - освободил" очевидно (свернув промежуточные {} при необходимости). Мне и своего склероза памяти хватает sm.gif
Nikitoc
Всем спасибо за ответы. Вопрос Сергею: я спросил про "неназванную" область оперативной памяти (которая не куча и не стек) потому что столкнулся с этим при работе в Keil uVision (я пишу под Cortex-M3, хотя это наверное не важно). В стартаповом файле конфигурируется куча и стек. Из доступных 16кБ я под стек выделил 1кБ, а куче ничего, т.к. не использую динамического выделения памяти в этом проекте. Так вот, в программе у меня определены два буфера по 4кБ (как глобальные переменные). Получается, что расположены эти два буфера не в стеке (слишком мал) и не в куче (т.к. они определены статически да и под кучу памяти не выделено). Я, конечно, понимаю, что частично ответил на свой вопрос, но все равно хочется больше информации в этом направлении. Может быть управление оперативной памятью зависит от платформы (компилятора)?
_Ivana
Это стОит того, чтобы ждать! sm.gif
Так и я пойму даже кое-что про ОЗУ. Хотя, если объем статических переменных ограничен по определению, кучу и стек мы тоже ограничиваем - остальное просто не используется? Тогда хорошо бы что-то (либо кучу либо стек) не ограничивать и использовать на весь оставшийся объем памяти. Просто мысли дилетанта sm.gif
Сергей Борщ
QUOTE (toweroff @ Mar 1 2012, 20:28) *
то есть malloc просматривает при выделении нужную "дырку", в не зависимости, как и где вызван запрос на область памяти?
посему - free освобождает этот кусок, образуя дырку?
Существует множество реализаций менеджеров памяти со своими преимуществами и недостатками. Для понимания их работы можно рассмотреть простейший случай. Куча - отдельный зарезервированный участок памяти. При старте - она одна большая "дырка". При вызове malloc() из нее "откусываются" кусочки запрошенного размера плюс еще немного для служебной информации. При вызове free() эти кусочки становятся небольшими "дырками", а если они с какой-либо стороны соприкасаются с другой "дыркой" - то эти две дырки объединяются в одну "дырку" суммарного размера. Таким образом, если освободить все выделенные malloc() переменные - будет снова одна большая "дырка".
QUOTE (toweroff @ Mar 1 2012, 20:28) *
и без разницы где это вызывается?
Да, естественно. malloc возвращает указатель на "откушенный" участок. Грубо говоря указывать вы можете откуда угодно, пока указываете в это место.
QUOTE (toweroff @ Mar 1 2012, 20:28) *
также, выделяя/освобождая как попало куски памяти, получаем вариант, когда суммарно память свободная есть, а выделить ее не можем, потому как нет одной дырки подходящего размера?
Совершенно верно. Это называется фрагментацией кучи и является главным ее недостатком. Именно из-за нее и стараются не использовать динамические переменные в контроллерах. В основном именно для борьбы с ней и существуют различные менеджеры памяти.
QUOTE (toweroff @ Mar 1 2012, 20:28) *
валяйте
В этой функции не возвращается адрес локальной переменной. Локальная переменная тут одна - result и возвращается ее значение. А вот значение ее является указателем на какую-то точку глобальной кучи (адресом). Так что не ойой, а хихи 08.gif . Ваш ход.
QUOTE (xemul @ Mar 1 2012, 20:54) *
Мне больше по душе, когда "выделил - ... - освободил" очевидно (свернув промежуточные {} при необходимости). Мне и своего склероза памяти хватает sm.gif
А, в этом смысле... Ну тогда все объектно-ориентированное придется отменить.
QUOTE (Nikitoc @ Mar 1 2012, 20:54) *
я спросил про "неназванную" область оперативной памяти (которая не куча и не стек) потому что столкнулся с этим при работе в Keil uVision
А, понял. Глобальные переменные размещаются в первую очередь. А уже что осталось от ОЗУ после них - отдается под стек и кучу. Ну а что и они не использовали - висит без пользы и вносит вклад в глобальное потепление.
QUOTE (Nikitoc @ Mar 1 2012, 20:54) *
Получается, что расположены эти два буфера не в стеке
Переменные бывают:
- глобальные ("видны" во всем проекте, хранят значения от старта до завершения программы)
- глобальные статические ("видны" в пределах единицы компиляции, хранят значения от старта до завершения программы)
- локальные статические ("видны" от точки объявления до конца блока, хранят значения от старта до завершения программы)
- локальные автоматические("видны" от точки объявления до конца блока, хранят значения от точки объявления до конца блока).
- динамические (создаются по malloc(), new(), уничтожаются по free(), delete()).
вроде бы ничего не забыл.
Первые три хранятся в памяти в той самой области, которая резервируется под них в первую очередь. И размер этой области линкер может легко подсчитать - это суммарный размер переменных плюс (при необходимости) некоторое количество "пропусков" для выравнивания адресов переменных. Под четвертые память выделяется на стеке в процессе работы. Под пятые память выделяется в куче.
QUOTE (Nikitoc @ Mar 1 2012, 20:54) *
(слишком мал)
Если стека не хватит - вы узнаете об этом только в процессе работы программы. Ибо расчитать расход стека очень сложно, а иногда и невозможно. Поэтому компилятор/линкер его не считает. Вы резервируете N байт, линкер их размещает. Но ни компилятор, ни линкер не скажут, что этих N байт не хватает для всех ваших локальных автоматических переменных, адресов возврата, контекста прерываний и т.п. Поэтому когда вашим данным не хватит места на стеке, они начнут налезать на данные соседней области - незанятой памяти, кучи, глобальных переменных. Тогда и начнутся чудеса.

QUOTE (_Ivana @ Mar 1 2012, 21:39) *
Тогда хорошо бы что-то (либо кучу либо стек) не ограничивать и использовать на весь оставшийся объем памяти. Просто мысли дилетанта sm.gif
Да, так и делают. Чаще стек, ибо просчитать его сложно, пусть уж растет сколько может. А когда налезет на другие данные - надо будет переписывать программу, ибо памяти взять уже ну неоткуда совсем.
toweroff
Цитата(Сергей Борщ @ Mar 1 2012, 23:44) *
В основном именно для борьбы с ней и существуют различные менеджеры памяти.
В этой функции не возвращается адрес локальной переменной. Возвращается указатель на какую-то точку глобальной кучи

получается, что информация о куче хранится в какой-то реализации связанного списка там же, в области кучи (ну пока другого варианта не вижу)
отсюда и сложность в использовании оной?
Nikitoc
Сергей, спасибо Вам большое. Разжевано предельно понятно cheers.gif Систематизация - полезная штука.
_Ivana
Со стеком имхо ничего не поможет справиться, кроме методов программирования, его минимизирующих. Ограничить стек или не ограничить - все равно если он вылезет за пределы допустимого и налезет на нужную область - то будут действительно чудеса sm.gif
А в asm нет "статических" и "динамических". Но все asm программисты расписывают имеющееся ОЗУ и сами знают, что им надо хранить от начала и до конца, а какие области можно переписывать в результате промежуточных операций. Си и конкретно менеджеры памяти просто берут эту трудоемкую работу на себя. Но даже на asm есть стек, и при многих уровнях вложенных прерываний он тоже весьма сложно прогнозируется...
toweroff
Цитата(_Ivana @ Mar 2 2012, 00:00) *
Со стеком имхо ничего не поможет справиться, кроме методов программирования, его минимизирующих. Ограничить стек или не ограничить - все равно если он вылезет за пределы допустимого и налезет на нужную область - то будут действительно чудеса sm.gif
А в asm нет "статических" и "динамических". Но все asm программисты расписывают имеющееся ОЗУ и сами знают, что им надо хранить от начала и до конца, а какие области можно переписывать в результате промежуточных операций. Си и конкретно менеджеры памяти просто берут эту трудоемкую работу на себя. Но даже на asm есть стек, и при многих уровнях вложенных прерываний он тоже весьма сложно прогнозируется...

почему?
и на С то же самое... все можно сделать и в статике, как я понимаю. Остальные механизмы - удобства для. Но все имеет свой гемор
Сергей Борщ
QUOTE (toweroff @ Mar 1 2012, 21:51) *
получается, что информация о куче хранится в какой-то реализации связанного списка там же, в области кучи (ну пока другого варианта не вижу)
В простейших вариантах - да, именно так и делается. Слышал, что есть более сложные, в которых список хранится отдельно, но не изучал этот вопрос, поэтому врать не буду. Если у процессора есть MMU и он используется менеджером памяти - все должно быть еще запутанней и интересней.
QUOTE (toweroff @ Mar 1 2012, 21:51) *
отсюда и сложность в использовании оной?
Нет, это все делает библиотечный (или самописный) менеджер памяти. Сложность в слабо прогнозируемой дефрагментации, борьбе с ней и "утечками памяти", а их допустить очень просто, именно на них и намекает xemul:
CODE
void eat_my_memory()
{
     int * result = malloc(sizeof int, 10);
     printf(" Я украл у вас немного памяти. Насовсем :-P");
}
поскольку result перестал существовать после выхода из этой функции - выделенный участок кучи вернуть обратно невозможно.
_Ivana
Как интересно-то! sm.gif
Лично я бы стал пользоваться менеджером памяти только если мне нужно в проекте последовательно создавать/уничтожать определенное количество разноформатных данных. Если формат динамических переменных условно одинаковый (или в течение работы программы отличается незначительно), то проще было бы использовать заранее нарезанные статические куски определенных типов, только использовать их для разных целей на каждом этапе.

ЗЫ но конечно на непредсказуемых вещах типа компьютера без динамического выделения никак sm.gif

Цитата
поскольку result перестал существовать после выхода из этой функции - выделенный участок кучи вернуть обратно невозможно.

Если бы я писал менеджер памяти, я бы предусмотрел функцию free_all() которая освобождает всю кучу. Вызывать её в редкие моменты когда там нет нужных переменных - и все потерянные куски возвратятся sm.gif Или я слишком оптимистичен и пустой куча не бывает никогда?
toweroff
То есть "в лоб" - (как уже упомянали) - освобождать там, где и выделили
и выделить байт гораздо накладнее, чем 1КБ sm.gif

Цитата(_Ivana @ Mar 2 2012, 00:17) *
Если бы я писал менеджер памяти, я бы предусмотрел функцию free_all() которая освобождает всю кучу. Вызывать её в редкие моменты когда там нет нужных переменных - и все потерянные куски возвратятся sm.gif Или я слишком оптимистичен и пустой куча не бывает никогда?

снесем все, построим заново, голосуе за... sm.gif ------- ни в коем случае не прАпОгандО!!!
уничтожить можно, я так понимаю, область процесса
если это ОС, похреним сами себя
_Ivana
Ладно, уговорили: free_всё_кроме_заполненного_массива_ещё_нужных_указателей() sm.gif Тогда можно и не освобождать каждый раз по одной области, просто регулярно указывать какие области/переменные ещё нужны sm.gif
Сергей Борщ
QUOTE (_Ivana @ Mar 1 2012, 22:30) *
просто регулярно указывать какие области/переменные ещё нужны sm.gif
А как их найти, такие области? В плюсах есть возможность реализовать "умные указатели", которые считают количество ссылок, и как только количество ссылок равно нулю - освобождают память. Но и их можно "обнануть", да и накладные расходы они добавляют. А в Си только медитирование над исходниками.
toweroff
Цитата(_Ivana @ Mar 2 2012, 00:30) *
Ладно, уговорили: free_всё_кроме_заполненного_массива_ещё_нужных_указателей() sm.gif Тогда можно и не освобождать каждый раз по одной области, просто регулярно указывать какие области/переменные ещё нужны sm.gif

тогда это -- свой собственный обработчик кучи rolleyes.gif
_Ivana
Это было на правах фантазии дилетанта sm.gif Убирать в квартире постоянно или перед приходом гостей...
Никто не мешает сделать эти функции в существующем менеджере памяти. Разумеется, решать что ещё нужно а что нет лучше оставить человеку. Может ему через 10000000 тактов понадобится тот выделенный когда-то кусок и он сделал это осознанно. Просто если он хочет - будет выделять/освобождать каждый кусочек отдельно. А если "забывчивый" - пусть помнит хотя бы (в отдельном месте, массиве нужных временных динамических переменных, самостоятельно) - то что ещё ему нужно sm.gif Просто очень уж грустно и безысходно звучал тезис о потерянном навсегда кусочке памяти, вот я и фантазирую как можно их восстанавливать.

Например, в своем пока единственном проекте на asm с использованием ОЗУ, я планирую расписывать на каждом шаге какие переменные мне нужны, а не какие можно освободить. Это хотя бы даст график требуемого объема ОЗУ от текущего шага задачи и позволит определить максимально нужный объем под "динамические" переменные.
_Pasha
Цитата(xemul @ Mar 1 2012, 21:54) *
Я не про использование кучи - оно вполне естественно, - а про несвязанные явно выделение и освобождение памяти.
Мне больше по душе, когда "выделил - ... - освободил" очевидно (свернув промежуточные {} при необходимости). Мне и своего склероза памяти хватает sm.gif

Тогда alloca() вместо . sm.gif
Посерьезней:
чтобы не было фрагментации, надо писать свой обработчик, заточенный под работу с массивом указателей. Тогда доступ к объекту не напрямую, а через дескриптор - номер элемента массива. Номера у нас остаются неизменными, а указатели могут свободно измениться если автор затеет дефрагментацию средствами своего манагера склероза.
Естественно, это все возможно, если отбросить иллюзию того, что "кол-во выделяемых блоков может быть любым." sm.gif
XVR
Цитата(_Ivana @ Mar 2 2012, 00:17) *
Если формат динамических переменных условно одинаковый (или в течение работы программы отличается незначительно), то проще было бы использовать заранее нарезанные статические куски определенных типов, только использовать их для разных целей на каждом этапе.
Это называется 'пул памяти' (memory pool). Хорошо известная и часто используемая структура динамической памяти. Да и классические кучи (heap'ы) бывают разные. Например вот
_Ivana
XVR спасибо, очень интересно и познавательно!
Idle
Цитата
result = (WORD *)malloc(1);

вылезаешь за границу выделенного блока при записи, выделяешь 1 байт, надо 2 (жду две страницы, что это и так работает - работает sm.gif)

Цитата
result = (WORD *)malloc(sizeof(*result));


упд
и приведение типов здесь излишне
Цитата
result = malloc(sizeof(*result));


Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.