Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: возврат указателя на массив из ф-ии
Форум разработчиков электроники ELECTRONIX.ru > Сайт и форум > В помощь начинающему > Программирование
Метценгерштейн
Есть ф-я, где локально объявлен некий массив. И я возвращаю указатель на этот массив. Смущает, что он локальный и на стеке. Он вернется в вызывающую ф-ю? Ничего по пути не потеряется?
AlexandrY
Цитата(Метценгерштейн @ Jun 6 2017, 10:41) *
Есть ф-я, где локально объявлен некий массив. И я возвращаю указатель на этот массив. Смущает, что он локальный и на стеке. Он вернется в вызывающую ф-ю? Ничего по пути не потеряется?

Ну судя по тому что вы профессионал, то знаете что делаете.
Поэтому могу только предложить идею в начале массива оставить некоторую страховочную область которая может засоряться эпилогами и прологами при вызовах функций.
ViKo
Когда вернулись из функции, ее локального массива уже нет, если он не статический. Возвращать указатель на ничто? Только профессионалу позволено, потому что реально массив все еще плехается где-то в ОЗУ. :-)
Метценгерштейн
ну а без шуток?
Получается, что массив удаляется. Разве что может повезти, и он еще не успел затереться в ОЗУ?
И по- хорошему, так нельзя делать.
Верно?
jcxz
Цитата(AlexandrY @ Jun 6 2017, 09:49) *
Ну судя по тому что вы профессионал, то знаете что делаете.

Да - профессионал в написании сообщений в форуме biggrin.gif

Человека, делающего подобное:
Цитата(Метценгерштейн @ Jun 6 2017, 09:41) *
Есть ф-я, где локально объявлен некий массив. И я возвращаю указатель на этот массив. Смущает, что он локальный и на стеке.

не то что профессионалом, а и просто знающим - язык не поворачивается назвать... smile3046.gif

Цитата(Метценгерштейн @ Jun 6 2017, 10:11) *
И по- хорошему, так нельзя делать.

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

Чтобы вернуть некое содержимое из функции на стеке (в автоматической памяти), обычно такой массив создаётся в вызывающей функции и указатель передаётся в вызываемую.
megajohn
Цитата(Метценгерштейн @ Jun 6 2017, 11:11) *
ну а без шуток?
Получается, что массив удаляется. Разве что может повезти, и он еще не успел затереться в ОЗУ?


да пройдись в дизасме, и все увидишь.

должно быть примерно так:
перед вызовом функции StackPointer = 1000
StackPointer уменьшается на 100 и стал 900 ( 100 это сколько нужно для размещения твоего массива, прочего ( используемые регистры тоже будут сохранены в стек ) в процессе работы функции )
по выходу из функции StackPointer + 100 и равен 1000
дальнейшие команды могу похерить твой массив, а могут и не похерить - но никто специально не стирает

p.s. если функция1 вызывает функцию2 а она вызывает функцию3 то компилятор, может единожды сделать StackPointer "туды/сюды", а не трижды
Метценгерштейн
Профессионал- тот, кто не стесняется озвучить непонятный для него момент, пытается закрыть этот вопрос.
Ламер- тот, который пишет как умеет, не пытается задать неудобный вопрос, боится, что его начнут поливать. И код у него соответствующий.
А кто каждый из нас- определяем сами.
За ответы- спасибо.
megajohn
Цитата(Метценгерштейн @ Jun 6 2017, 11:58) *
За ответы- спасибо.


такая реакция у публики, потому что это азы
вот "Язык программирования Си" Брайан Керниган, Деннис Ритчи
Каждая локальная переменная функции возникает только в момент обращения к этой функции и исчезает после выхода из нее.
sigmaN
Голосую за то, что это просто толстый троллинг.
demiurg_spb
Цитата(sigmaN @ Jun 6 2017, 13:25) *
Не иначе...
conan
С точки зрения спецификации языка C доступ к локальным переменным функции после её завершения влечет неопределенное поведение. Если копнуть глубже, то поведение зависит от устройства конкретного компилятора и конкретной платформы. На микроконтроллерах и ОС типа DOS данные в локальном массиве после завершения функции могут быть перезаписаны обработчиками аппаратных прерываний. В ОС использующих механизм страничной адресации памяти диапазон массива теоретически вообще может оказаться unmap-ленными и тогда даже попытка чтения приведет к segmenation fault и немедленному завершению программы
AnatolyT
Не стоит рассчитывать на сохранность локальных переменных вне функции. Поступаю следующим образом, определяю область памяти с помощью malloc() и передаю указатель в функцию, которая в свою очередь тоже может передать и возвратить его, после возврата указателя освобождаю область с помощью free().
one_eight_seven
Цитата(AnatolyT @ Jun 8 2017, 22:56) *
Не стоит рассчитывать на сохранность локальных переменных вне функции. Поступаю следующим образом, определяю область памяти с помощью malloc() и передаю указатель в функцию, которая в свою очередь тоже может передать и возвратить его, после возврата указателя освобождаю область с помощью free().

Откровенно странный способ, ваш компилятор не поддерживает С11 или C99?
conan
Цитата(one_eight_seven @ Jun 9 2017, 01:11) *
Откровенно странный способ, ваш компилятор не поддерживает С11 или C99?

Почему странный? Стандартный способ.
Два основных подхода:
1. Вызывающая сторона выделяет память и передает адрес буфера в функцию в качестве аргумента. Функция заполняет буфер результатом работы. За освобождение памяти отвечает вызывающая сторона. Иногда, когда заранее неизвестно сколько надо будет памяти, чтобы поместился результат, функция вызывается два раза. Первый раз -- с 0 в качестве указателя на буфер и тогда, функция не генерирует результат, а только вычисляет и возвращает размер необходимого буфера. Потом вызывающая сторона готовит буфер нужного размера и вызывает функцию повторно, но уже с указателем на буфер. Такой под-подход встречается, например, в WinAPI.
2. Память выделяет сама функция и возвращает указатель на этот блок, заполненный результатом работы. Не кошерно, если потом память освобождается прямым вызовом free. По-хорошему должна быть отдельная функция которая просто освобождает память:
Data *doSomething();
void free(Data *);
Именно так устроено большинство C-библиотек. Есть набор функций, которые выделяют память и возвращают указатель и есть отдельная функция, которая по этим указателям память освобождает. Такой подход, например, позволяет подключать библиотеки, которые собраны другим компилятором (или тем же, но с другими настройками, которые влияют на распределитель памяти)

one_eight_seven
Цитата(conan @ Jun 9 2017, 03:34) *
Почему странный? Стандартный способ.
Два основных подхода:
1. Вызывающая сторона выделяет память и передает адрес буфера в функцию в качестве аргумента. Функция заполняет буфер результатом работы. За освобождение памяти отвечает вызывающая сторона. Иногда, когда заранее неизвестно сколько надо будет памяти, чтобы поместился результат, функция вызывается два раза. Первый раз -- с 0 в качестве указателя на буфер и тогда, функция не генерирует результат, а только вычисляет и возвращает размер необходимого буфера. Потом вызывающая сторона готовит буфер нужного размера и вызывает функцию повторно, но уже с указателем на буфер. Такой под-подход встречается, например, в WinAPI.
2. Память выделяет сама функция и возвращает указатель на этот блок, заполненный результатом работы. Не кошерно, если потом память освобождается прямым вызовом free. По-хорошему должна быть отдельная функция которая просто освобождает память:
Data *doSomething();
void free(Data *);
Именно так устроено большинство C-библиотек. Есть набор функций, которые выделяют память и возвращают указатель и есть отдельная функция, которая по этим указателям память освобождает. Такой подход, например, позволяет подключать библиотеки, которые собраны другим компилятором (или тем же, но с другими настройками, которые влияют на распределитель памяти)

Conan, этот метод стандартный для C89. Если на момент вызова функции, которая должна только заполнить буфер, размер буфера уже известен, а у вас в пункте 1 так, то с этим в C99/С11 прекрасно справляется автоматическая модель памяти, нет нужды рисковать с использованием ручного управления памятью.
Я понимаю, что лучший инструмент - это не тот, который лучше, а тот, который вы знаете, и это нормально. Но советовать такое в 2017 - это ретроградство, и даже немножко вредительство, насколько мне изветсно, даже Будда терял самообладание, когда ему приходилось пользоваться malloc'ом.

По пункту 2. Врапперы вокруг free и malloc полностью поддерживаю там, где без malloc и free не обойтись. Но в полседнее время мало встречаю необходимости в malloc. А вот free нужна гораздо чаще.

Как по мне, malloc нужен там, где потом будет использоваться realloc, но, повторюсь, нужды в этом сейчас куда меньше, чем об этом написано в книжках 80-х и 90-х годов.

P.S. Не знаком с WinApi, но Win в начале позволяет говорить о том, что это microsoft'овское, а у них компилятор не поддерживает C99/C11. По крайней мере, отказ о поддержке этих стандартов языка был официально объявлен, когда я последний раз рассматривал возможность прикоснуться к Visual Studio.
k155la3
Цитата(AnatolyT @ Jun 8 2017, 22:56) *
Не стоит рассчитывать на сохранность локальных переменных вне функции. Поступаю следующим образом, определяю область памяти с помощью malloc() и передаю указатель в функцию, которая в свою очередь тоже может передать и возвратить его, после возврата указателя освобождаю область с помощью free().


Вместо динамически - локальной, для этой цели можно использовать static, и спокойно возвращать указатель.
Если, конечно, позволяет объем памяти.

novikovfb
Цитата(k155la3 @ Jun 9 2017, 12:18) *
Вместо динамически - локальной, для этой цели можно использовать static, и спокойно возвращать указатель.
Если, конечно, позволяет объем памяти.

если нет требований по реентерабельности
sigmaN
В функции winAPI совершенно не обязательно передавать указатель полученный через malloc();
Туда спокойно можено передать указатель на локальную переменную ВЫЗЫВАЮЩЕЙ функции.
malloc() потребуется только если winAPI вызов асинхронный и возврат произойдет немедленно. После чего произойдет возврат из вызывающей функции и тогда снова висящий поинтер.
Но такие ситуация не так часто встречается. И глупо в таких случаях использовать менеджер памяти. Как минимум это медленно!

При чем тут С11/C99????
one_eight_seven
Цитата
При чем тут С11/C99????

Во-первых, они позволяют создавать массивы, длина которых вычисляется во время выполнения программы, а не во время компиляции.
И во-вторых:
Цитата
если нет требований по реентерабельности

в С11 есть _Thread_local
sigmaN
Цитата
Во-первых, они позволяют создавать массивы, длина которых вычисляется во время выполнения программы, а не во время компиляции.

Классно!
К топику это как-то относится? )))) Если всё равно этот массив будет на стеке!

Цитата
в С11 есть _Thread_local
Не надо путать теплое с мягким. Т.е. реентерабельность с потокобезопасностью(thread safe)
one_eight_seven
Цитата
К топику это как-то относится? )))) Если всё равно этот массив будет на стеке!

К вопросу в топике относится. И в чём проблема что на стеке? Вы используете только кучу и регистры, без стека работаете и настаиваете на этом? Или в честь чего смайловая истерика?


Цитата
Не надо путать теплое с мягким. Т.е. реентерабельность с потокобезопасностью(thread safe)

Реентрабельный - не всегда потокобезопасный, но потокобезопасный всегда реентрабельный, поскольку не только позволяет вызывать одну и ту же функцию из нескольких потоков, но ещё и сериализирует доступ к общим данным.
jcxz
Цитата(one_eight_seven @ Jun 9 2017, 14:26) *
Реентрабельный - не всегда потокобезопасный, но потокобезопасный всегда реентрабельный, поскольку не только позволяет вызывать одну и ту же функцию из нескольких потоков, но ещё и сериализирует доступ к общим данным.

И каким-же образом потокобезопасность поможет ТСу вернуть данные в вызывающую функцию из вызываемой в автоматической памяти не превратив их в мусор? laughing.gif
sigmaN
Цитата
И в чём проблема что на стеке? Вы используете только кучу и регистры, без стека работаете и настаиваете на этом? Или в честь чего смайловая истерика?
Обратите внимание на вопрос ТСа. Он именно возвращает указатель на локальную переменную которая на стеке.
Я и обратил Ваше внимание на то, что прекрасная новая фича с размером массива, который может зависеть от параметра функции, к сути топика не имеет особого отношения...

По поводу _Thread_local:
Эта магия сработает в случае, когда выполнение функции прервано прерыванием и из этого прерывания вызвана та-же функция. C11 разберется, что это уже новый thread и нужно использовать другую копию этой переменной? ))
Я не проверял, но сомневаюсь.

Хотя опять же это уводит нас немного в сторону от вопроса ТС.....
novikovfb
Цитата(jcxz @ Jun 12 2017, 11:01) *
И каким-же образом потокобезопасность поможет ТСу вернуть данные в вызывающую функцию из вызываемой в автоматической памяти не превратив их в мусор? laughing.gif

Никаким. Но использование статического или глобального буфера для возврата данных гарантирует проблемы в многопоточной среде (в том числе вызове функции из прерываний и основного процесса).
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2024 Invision Power Services, Inc.