реклама на сайте
подробности

 
 
> возврат указателя на массив из ф-ии, немного теории
Метценгерштейн
сообщение Jun 6 2017, 07:41
Сообщение #1


Профессионал
*****

Группа: Свой
Сообщений: 1 357
Регистрация: 12-04-05
Из: Петербург
Пользователь №: 4 079



Есть ф-я, где локально объявлен некий массив. И я возвращаю указатель на этот массив. Смущает, что он локальный и на стеке. Он вернется в вызывающую ф-ю? Ничего по пути не потеряется?
Go to the top of the page
 
+Quote Post
2 страниц V   1 2 >  
Start new topic
Ответов (1 - 23)
AlexandrY
сообщение Jun 6 2017, 07:49
Сообщение #2


Ally
******

Группа: Модераторы
Сообщений: 6 232
Регистрация: 19-01-05
Пользователь №: 2 050



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

Ну судя по тому что вы профессионал, то знаете что делаете.
Поэтому могу только предложить идею в начале массива оставить некоторую страховочную область которая может засоряться эпилогами и прологами при вызовах функций.
Go to the top of the page
 
+Quote Post
ViKo
сообщение Jun 6 2017, 08:00
Сообщение #3


Универсальный солдатик
******

Группа: Модераторы
Сообщений: 8 634
Регистрация: 1-11-05
Из: Минск
Пользователь №: 10 362



Когда вернулись из функции, ее локального массива уже нет, если он не статический. Возвращать указатель на ничто? Только профессионалу позволено, потому что реально массив все еще плехается где-то в ОЗУ. :-)
Go to the top of the page
 
+Quote Post
Метценгерштейн
сообщение Jun 6 2017, 08:11
Сообщение #4


Профессионал
*****

Группа: Свой
Сообщений: 1 357
Регистрация: 12-04-05
Из: Петербург
Пользователь №: 4 079



ну а без шуток?
Получается, что массив удаляется. Разве что может повезти, и он еще не успел затереться в ОЗУ?
И по- хорошему, так нельзя делать.
Верно?
Go to the top of the page
 
+Quote Post
jcxz
сообщение Jun 6 2017, 08:40
Сообщение #5


Гуру
******

Группа: Свой
Сообщений: 5 228
Регистрация: 3-07-08
Из: Омск
Пользователь №: 38 713



Цитата(AlexandrY @ Jun 6 2017, 09:49) *
Ну судя по тому что вы профессионал, то знаете что делаете.

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

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

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

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

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

Чтобы вернуть некое содержимое из функции на стеке (в автоматической памяти), обычно такой массив создаётся в вызывающей функции и указатель передаётся в вызываемую.
Go to the top of the page
 
+Quote Post
megajohn
сообщение Jun 6 2017, 08:43
Сообщение #6


Профессионал
*****

Группа: Свой
Сообщений: 1 080
Регистрация: 16-11-04
Из: СПб
Пользователь №: 1 143



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


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

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

p.s. если функция1 вызывает функцию2 а она вызывает функцию3 то компилятор, может единожды сделать StackPointer "туды/сюды", а не трижды


--------------------
Марс - единственная планета, полностью населенная роботами (около 7 штук).
Go to the top of the page
 
+Quote Post
Метценгерштейн
сообщение Jun 6 2017, 08:58
Сообщение #7


Профессионал
*****

Группа: Свой
Сообщений: 1 357
Регистрация: 12-04-05
Из: Петербург
Пользователь №: 4 079



Профессионал- тот, кто не стесняется озвучить непонятный для него момент, пытается закрыть этот вопрос.
Ламер- тот, который пишет как умеет, не пытается задать неудобный вопрос, боится, что его начнут поливать. И код у него соответствующий.
А кто каждый из нас- определяем сами.
За ответы- спасибо.
Go to the top of the page
 
+Quote Post
megajohn
сообщение Jun 6 2017, 09:08
Сообщение #8


Профессионал
*****

Группа: Свой
Сообщений: 1 080
Регистрация: 16-11-04
Из: СПб
Пользователь №: 1 143



Цитата(Метценгерштейн @ Jun 6 2017, 11:58) *
За ответы- спасибо.


такая реакция у публики, потому что это азы
вот "Язык программирования Си" Брайан Керниган, Деннис Ритчи
Каждая локальная переменная функции возникает только в момент обращения к этой функции и исчезает после выхода из нее.


--------------------
Марс - единственная планета, полностью населенная роботами (около 7 штук).
Go to the top of the page
 
+Quote Post
sigmaN
сообщение Jun 6 2017, 10:25
Сообщение #9


I WANT TO BELIEVE
******

Группа: Свой
Сообщений: 2 617
Регистрация: 9-03-08
Пользователь №: 35 751



Голосую за то, что это просто толстый троллинг.


--------------------
The truth is out there...
Go to the top of the page
 
+Quote Post
demiurg_spb
сообщение Jun 6 2017, 10:38
Сообщение #10


неотягощённый злом
******

Группа: Свой
Сообщений: 2 746
Регистрация: 31-01-08
Из: Санкт-Петербург
Пользователь №: 34 643



Цитата(sigmaN @ Jun 6 2017, 13:25) *
Не иначе...


--------------------
“Будьте внимательны к своим мыслям - они начало поступков” (Лао-Цзы)
Go to the top of the page
 
+Quote Post
conan
сообщение Jun 8 2017, 19:35
Сообщение #11


Участник
*

Группа: Участник
Сообщений: 56
Регистрация: 3-11-11
Пользователь №: 68 126



С точки зрения спецификации языка C доступ к локальным переменным функции после её завершения влечет неопределенное поведение. Если копнуть глубже, то поведение зависит от устройства конкретного компилятора и конкретной платформы. На микроконтроллерах и ОС типа DOS данные в локальном массиве после завершения функции могут быть перезаписаны обработчиками аппаратных прерываний. В ОС использующих механизм страничной адресации памяти диапазон массива теоретически вообще может оказаться unmap-ленными и тогда даже попытка чтения приведет к segmenation fault и немедленному завершению программы
Go to the top of the page
 
+Quote Post
AnatolyT
сообщение Jun 8 2017, 19:56
Сообщение #12


Частый гость
**

Группа: Участник
Сообщений: 176
Регистрация: 29-03-10
Пользователь №: 56 269



Не стоит рассчитывать на сохранность локальных переменных вне функции. Поступаю следующим образом, определяю область памяти с помощью malloc() и передаю указатель в функцию, которая в свою очередь тоже может передать и возвратить его, после возврата указателя освобождаю область с помощью free().
Go to the top of the page
 
+Quote Post
one_eight_seven
сообщение Jun 8 2017, 22:11
Сообщение #13


Знающий
****

Группа: Участник
Сообщений: 916
Регистрация: 3-10-08
Из: Москва
Пользователь №: 40 664



Цитата(AnatolyT @ Jun 8 2017, 22:56) *
Не стоит рассчитывать на сохранность локальных переменных вне функции. Поступаю следующим образом, определяю область памяти с помощью malloc() и передаю указатель в функцию, которая в свою очередь тоже может передать и возвратить его, после возврата указателя освобождаю область с помощью free().

Откровенно странный способ, ваш компилятор не поддерживает С11 или C99?

Сообщение отредактировал one_eight_seven - Jun 8 2017, 22:12
Go to the top of the page
 
+Quote Post
conan
сообщение Jun 9 2017, 00:34
Сообщение #14


Участник
*

Группа: Участник
Сообщений: 56
Регистрация: 3-11-11
Пользователь №: 68 126



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

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

Go to the top of the page
 
+Quote Post
one_eight_seven
сообщение Jun 9 2017, 06:54
Сообщение #15


Знающий
****

Группа: Участник
Сообщений: 916
Регистрация: 3-10-08
Из: Москва
Пользователь №: 40 664



Цитата(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.
Go to the top of the page
 
+Quote Post
k155la3
сообщение Jun 9 2017, 08:18
Сообщение #16


Профессионал
*****

Группа: Свой
Сообщений: 1 123
Регистрация: 8-03-09
Из: Днепр
Пользователь №: 45 848



Цитата(AnatolyT @ Jun 8 2017, 22:56) *
Не стоит рассчитывать на сохранность локальных переменных вне функции. Поступаю следующим образом, определяю область памяти с помощью malloc() и передаю указатель в функцию, которая в свою очередь тоже может передать и возвратить его, после возврата указателя освобождаю область с помощью free().


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

Go to the top of the page
 
+Quote Post
novikovfb
сообщение Jun 9 2017, 08:41
Сообщение #17


Знающий
****

Группа: Участник
Сообщений: 518
Регистрация: 29-09-11
Пользователь №: 67 450



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

если нет требований по реентерабельности
Go to the top of the page
 
+Quote Post
sigmaN
сообщение Jun 9 2017, 09:11
Сообщение #18


I WANT TO BELIEVE
******

Группа: Свой
Сообщений: 2 617
Регистрация: 9-03-08
Пользователь №: 35 751



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

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


--------------------
The truth is out there...
Go to the top of the page
 
+Quote Post
one_eight_seven
сообщение Jun 9 2017, 11:00
Сообщение #19


Знающий
****

Группа: Участник
Сообщений: 916
Регистрация: 3-10-08
Из: Москва
Пользователь №: 40 664



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

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

в С11 есть _Thread_local
Go to the top of the page
 
+Quote Post
sigmaN
сообщение Jun 9 2017, 11:08
Сообщение #20


I WANT TO BELIEVE
******

Группа: Свой
Сообщений: 2 617
Регистрация: 9-03-08
Пользователь №: 35 751



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

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

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


--------------------
The truth is out there...
Go to the top of the page
 
+Quote Post
one_eight_seven
сообщение Jun 9 2017, 12:26
Сообщение #21


Знающий
****

Группа: Участник
Сообщений: 916
Регистрация: 3-10-08
Из: Москва
Пользователь №: 40 664



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

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


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

Реентрабельный - не всегда потокобезопасный, но потокобезопасный всегда реентрабельный, поскольку не только позволяет вызывать одну и ту же функцию из нескольких потоков, но ещё и сериализирует доступ к общим данным.
Go to the top of the page
 
+Quote Post
jcxz
сообщение Jun 12 2017, 07:01
Сообщение #22


Гуру
******

Группа: Свой
Сообщений: 5 228
Регистрация: 3-07-08
Из: Омск
Пользователь №: 38 713



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

И каким-же образом потокобезопасность поможет ТСу вернуть данные в вызывающую функцию из вызываемой в автоматической памяти не превратив их в мусор? laughing.gif
Go to the top of the page
 
+Quote Post
sigmaN
сообщение Jun 12 2017, 09:18
Сообщение #23


I WANT TO BELIEVE
******

Группа: Свой
Сообщений: 2 617
Регистрация: 9-03-08
Пользователь №: 35 751



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

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

Хотя опять же это уводит нас немного в сторону от вопроса ТС.....


--------------------
The truth is out there...
Go to the top of the page
 
+Quote Post
novikovfb
сообщение Jun 12 2017, 15:22
Сообщение #24


Знающий
****

Группа: Участник
Сообщений: 518
Регистрация: 29-09-11
Пользователь №: 67 450



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

Никаким. Но использование статического или глобального буфера для возврата данных гарантирует проблемы в многопоточной среде (в том числе вызове функции из прерываний и основного процесса).
Go to the top of the page
 
+Quote Post

2 страниц V   1 2 >
Reply to this topicStart new topic
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0

 


RSS Текстовая версия Сейчас: 21st July 2025 - 13:55
Рейтинг@Mail.ru


Страница сгенерированна за 0.01615 секунд с 7
ELECTRONIX ©2004-2016