|
возврат указателя на массив из ф-ии, немного теории |
|
|
2 страниц
1 2 >
|
 |
Ответов
(1 - 23)
|
Jun 6 2017, 08:40
|
Гуру
     
Группа: Свой
Сообщений: 5 228
Регистрация: 3-07-08
Из: Омск
Пользователь №: 38 713

|
Цитата(AlexandrY @ Jun 6 2017, 09:49)  Ну судя по тому что вы профессионал, то знаете что делаете. Да - профессионал в написании сообщений в форуме  Человека, делающего подобное: Цитата(Метценгерштейн @ Jun 6 2017, 09:41)  Есть ф-я, где локально объявлен некий массив. И я возвращаю указатель на этот массив. Смущает, что он локальный и на стеке. не то что профессионалом, а и просто знающим - язык не поворачивается назвать...  Цитата(Метценгерштейн @ Jun 6 2017, 10:11)  И по- хорошему, так нельзя делать. Если хотите заложить сюрпризов в ПО, чтобы насолить работодателю, уволившему Вас с работы, то можно. Чтобы вернуть некое содержимое из функции на стеке (в автоматической памяти), обычно такой массив создаётся в вызывающей функции и указатель передаётся в вызываемую.
|
|
|
|
|
Jun 6 2017, 08:43
|

Профессионал
    
Группа: Свой
Сообщений: 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 штук).
|
|
|
|
|
Jun 8 2017, 19:35
|
Участник

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

|
С точки зрения спецификации языка C доступ к локальным переменным функции после её завершения влечет неопределенное поведение. Если копнуть глубже, то поведение зависит от устройства конкретного компилятора и конкретной платформы. На микроконтроллерах и ОС типа DOS данные в локальном массиве после завершения функции могут быть перезаписаны обработчиками аппаратных прерываний. В ОС использующих механизм страничной адресации памяти диапазон массива теоретически вообще может оказаться unmap-ленными и тогда даже попытка чтения приведет к segmenation fault и немедленному завершению программы
|
|
|
|
|
Jun 9 2017, 00:34
|
Участник

Группа: Участник
Сообщений: 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-библиотек. Есть набор функций, которые выделяют память и возвращают указатель и есть отдельная функция, которая по этим указателям память освобождает. Такой подход, например, позволяет подключать библиотеки, которые собраны другим компилятором (или тем же, но с другими настройками, которые влияют на распределитель памяти)
|
|
|
|
|
Jun 9 2017, 06:54
|
Знающий
   
Группа: Участник
Сообщений: 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.
|
|
|
|
|
Jun 9 2017, 08:18
|
Профессионал
    
Группа: Свой
Сообщений: 1 123
Регистрация: 8-03-09
Из: Днепр
Пользователь №: 45 848

|
Цитата(AnatolyT @ Jun 8 2017, 22:56)  Не стоит рассчитывать на сохранность локальных переменных вне функции. Поступаю следующим образом, определяю область памяти с помощью malloc() и передаю указатель в функцию, которая в свою очередь тоже может передать и возвратить его, после возврата указателя освобождаю область с помощью free(). Вместо динамически - локальной, для этой цели можно использовать static, и спокойно возвращать указатель. Если, конечно, позволяет объем памяти.
|
|
|
|
|
Jun 9 2017, 11:00
|
Знающий
   
Группа: Участник
Сообщений: 916
Регистрация: 3-10-08
Из: Москва
Пользователь №: 40 664

|
Цитата При чем тут С11/C99???? Во-первых, они позволяют создавать массивы, длина которых вычисляется во время выполнения программы, а не во время компиляции. И во-вторых: Цитата если нет требований по реентерабельности в С11 есть _Thread_local
|
|
|
|
|
Jun 9 2017, 11:08
|

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

|
Цитата Во-первых, они позволяют создавать массивы, длина которых вычисляется во время выполнения программы, а не во время компиляции. Классно! К топику это как-то относится? )))) Если всё равно этот массив будет на стеке! Цитата в С11 есть _Thread_local Не надо путать теплое с мягким. Т.е. реентерабельность с потокобезопасностью(thread safe)
--------------------
The truth is out there...
|
|
|
|
|
Jun 9 2017, 12:26
|
Знающий
   
Группа: Участник
Сообщений: 916
Регистрация: 3-10-08
Из: Москва
Пользователь №: 40 664

|
Цитата К топику это как-то относится? )))) Если всё равно этот массив будет на стеке! К вопросу в топике относится. И в чём проблема что на стеке? Вы используете только кучу и регистры, без стека работаете и настаиваете на этом? Или в честь чего смайловая истерика? Цитата Не надо путать теплое с мягким. Т.е. реентерабельность с потокобезопасностью(thread safe) Реентрабельный - не всегда потокобезопасный, но потокобезопасный всегда реентрабельный, поскольку не только позволяет вызывать одну и ту же функцию из нескольких потоков, но ещё и сериализирует доступ к общим данным.
|
|
|
|
|
Jun 12 2017, 09:18
|

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

|
Цитата И в чём проблема что на стеке? Вы используете только кучу и регистры, без стека работаете и настаиваете на этом? Или в честь чего смайловая истерика? Обратите внимание на вопрос ТСа. Он именно возвращает указатель на локальную переменную которая на стеке. Я и обратил Ваше внимание на то, что прекрасная новая фича с размером массива, который может зависеть от параметра функции, к сути топика не имеет особого отношения... По поводу _Thread_local: Эта магия сработает в случае, когда выполнение функции прервано прерыванием и из этого прерывания вызвана та-же функция. C11 разберется, что это уже новый thread и нужно использовать другую копию этой переменной? )) Я не проверял, но сомневаюсь. Хотя опять же это уводит нас немного в сторону от вопроса ТС.....
--------------------
The truth is out there...
|
|
|
|
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0
|
|
|