Модераторы: Daevaorn

Поиск:

Ответ в темуСоздание новой темы Создание опроса
> Возврат строки неизвестного размера из функции, работа с выделением памяти в функции 
V
    Опции темы
ama_kid
Дата 6.6.2008, 10:13 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


АСУТП-кодер
***


Профиль
Группа: Комодератор
Сообщений: 1460
Регистрация: 5.3.2007
Где: Москва

Репутация: 2
Всего: 95



Доброго всем времени суток...

Сразу скажу, что при написании топика почитал эту и эту тему, выданные мне форумом как "похожие темы", ответы там не сильно помогли, поиском тоже пользовался - не айс...  между тем вопрос по большому счету не стОит и того количества букв, которые я тут напишу, но тем не менее, постараюсь в красках и примерах на пальцах объяснить суть ментального геморроя...
Итак, недавно пришлось переводить кое-какой код с Дельфи на С++ и столкнулся с интересной для себя теоретической проблемой, которая подспудно грызла меня давно, но как-то не было случая с ней основательно разобраться... Дело в том, что есть, допустим, гипотетический код на дельфи:
Код
function GetString:string;
var 
 s:string;
begin
  s:=...    // Здесь, к примеру, динамическое 
  s:=s+... // формирование строки по какому-то алогритму
  Result:=s; {...some dynamic string...} ;
end;
Как видно из этого примера - размер возвращаемой строки заранее неизвестен, компилятор автоматически выделяет в стеке необходимый буфер под строку, расширяя по необходимости, и затем возвращает её... Теперь пытаюсь сделать то же самое на С++:
Код
char * GetString(void)
{
  char *s;
  ...  // Здесь я использую необходимые new\malloc\realloc\strcpy\strcat для 
  ...  // получения необходимого количества памяти и формирования строки.
  return s; // В итоге - s содержит необходимую мне строку.
}
Возникают следующие вопросы:
1) Очевидно, что если я оставлю функцию в таком виде - я гарантирую себе весёлую жизнь с утечками памяти, ибо delete\free нигде не вызываются и память по выходу из функции утекает. В итоге - закономерный вопрос: где необходимо освобождать память? Выделять память до вызова функции нет смысла, ибо размер буфера неизвестен; освобождать внутри функции - бессмысленно; выделять внутри, а освобождать после выхода - некрасиво (и есть небезосновательное подозрение, что неправильно)...
2) Как вообще правильно действовать в таких случаях? Использование разнообразных классов типа std::string не предлагать, сам знаю, что они для этого и предназначены, но так же помню, что разнообразные библиотечные функции, работающие с char * - работают вполне корректно и возвращают указатели на вполне валидные буферы. Да, я понимаю, что там используется несколько другой подход - передаются указатели на буферы, где необходимо разместить ответ, но не до конца понимаю, где происходит выделение памяти под эти буферы? (Тот же strcpy - как и где выделяет память под char *strDestination?). Единственный более-менее внятный ответ на похожий вопрос от _hunter'а я увидел у RAN, но и там возник вопрос: а что если память под параметр char* param выделена не через new, а допустим, через malloc (при условии, что я не знаю точно)?

В общем, буду признателен, если помимо ответов "ха-ха, ты далпайоп!" и "кури маны, дятел!" (как любит говорить мне мой друг) будут более содержательные разъяснения smile


--------------------
самурай без меча подобен самураю с мечом, но только без меча 
PM MAIL   Вверх
Fazil6
Дата 6.6.2008, 10:37 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Завсегдатай
Сообщений: 1653
Регистрация: 3.5.2006
Где: Минск

Репутация: 35
Всего: 60



Цитата(ama_kid @  6.6.2008,  10:13 Найти цитируемый пост)
Как вообще правильно действовать в таких случаях?

ну если посмотреть апишные функции, то как правило им передаётся буфер и размер, а они возвращают количество записанных символов и, если что, количество символов, которые не влезли.
По большому счету мне непонятны твои сомнения относительно new или malloc. Ведь полюбому буфер надо выделять перед вызовом и функции  побарабану как он получен (хоть массив в стеке). Возвращать из функции имеет смысл если возвращается const char* заранее определененый, а в твоем случае нужно подготовленный буфер передавать и заполнять его.

Добавлено через 5 минут и 2 секунды
если смотреть твой код
Код

char * GetString(void)
{
  char *s;
  ...  // Здесь я использую необходимые new\malloc\realloc\strcpy\strcat для 
  ...  // получения необходимого количества памяти и формирования строки.
  return s; // В итоге - s содержит необходимую мне строку.
}

неправильно это. Плохо делать выделение памяти внутри функции. Так ты делаешь вызывающий код зависимым от реализации этой функции. 
PM MAIL   Вверх
Walker
Дата 6.6.2008, 10:45 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 363
Регистрация: 23.10.2006

Репутация: 1
Всего: 16



Хоть сам ещё нахожусь в процессе познания, принять участие в обсуждении интересно. Может вместе найдём истину. smile 
Сразу оговорюсь, я работаю исключительно с С.
На первый взгляд складывается ощущение, что при грамотном проектировании такой ситуации возникать просто не должно.
Цитата

выделять внутри, а освобождать после выхода - некрасиво (и есть небезосновательное подозрение, что неправильно)...

Правильно понимаете. Это чревато следующей ошибкой, которая ловится, зачастую только под отладчиком. Если Вы передаёте адрес как аргумент, то работаете с локальной копией указателя, и внешний мир ничего не знает о выделенном участке. Это будет бесцельная трата памяти. Если же вы используете адрес в качестве возвращаемого значения, то аргументом передавайте размер, Ваша функция будет выполнять роль оболочки над malloc. Напишите обратную функцию - оболочку над free.
Библиотечные функции и strcpy в том числе используют именно преопределённые буферы, за выделение и освобождение которых отвечаете Вы.
Попробуйте представить иной пример, тогда найдём в открытых исходниках аналог и разберёмся. Пока я такого не встречал. smile 

PS Пока инет глючил, Fazil6 опередил.smile

Это сообщение отредактировал(а) Walker - 6.6.2008, 10:54


--------------------
"От вчерашних побед остаётся усталость, если завтрашний день не сулит ничего..."
PM MAIL   Вверх
Fazil6
Дата 6.6.2008, 10:51 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Завсегдатай
Сообщений: 1653
Регистрация: 3.5.2006
Где: Минск

Репутация: 35
Всего: 60



как вариант сначала запрашивать размер буфера, а потом выделять буфер и вызывать GetString, но честно говоря я бы так не делал. 
По любому используя С++ лучше контейнер (стандартный или самодельный) вместо char* 
PM MAIL   Вверх
Mayk
Дата 6.6.2008, 10:55 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


^аВаТаР^ сообщение>>
****


Профиль
Группа: Участник
Сообщений: 2616
Регистрация: 22.5.2005
Где: за границей разум а

Репутация: 45
Всего: 134



Цитата(ama_kid @  6.6.2008,  14:13 Найти цитируемый пост)
Тот же strcpy - как и где выделяет память под char *strDestination

strcpy не выделяет память. Выделение памяти - это проблемы вызывающего  strcpy кода.

Цитата(ama_kid @  6.6.2008,  14:13 Найти цитируемый пост)

В общем, буду признателен, если помимо ответов "ха-ха, ты далпайоп!" и "кури маны, дятел!" (как любит говорить мне мой друг) будут более содержательные разъяснения

курить std::string (c++)
и realloc  ©


--------------------
 Здесь был кролик. Но его убили.
Человеки < кроликов, йа считаю.
PM MAIL WWW ICQ   Вверх
Fazil6
Дата 6.6.2008, 10:56 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Завсегдатай
Сообщений: 1653
Регистрация: 3.5.2006
Где: Минск

Репутация: 35
Всего: 60



Цитата(ama_kid @  6.6.2008,  10:13 Найти цитируемый пост)
Да, я понимаю, что там используется несколько другой подход - передаются указатели на буферы, где необходимо разместить ответ, но не до конца понимаю, где происходит выделение памяти под эти буферы? (Тот же strcpy - как и где выделяет память под char *strDestination?).


вот те раз... приехали... нигде он ее не выделяет. Оба буфера выделены до вызова. 
PM MAIL   Вверх
Mayk
Дата 6.6.2008, 10:56 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


^аВаТаР^ сообщение>>
****


Профиль
Группа: Участник
Сообщений: 2616
Регистрация: 22.5.2005
Где: за границей разум а

Репутация: 45
Всего: 134



Цитата(ama_kid @  6.6.2008,  14:13 Найти цитируемый пост)
Выделять память до вызова функции нет смысла, ибо размер буфера неизвестен

realloc курить сюда.

зы! опять мои посты не склеились >ОДНАКО<

Это сообщение отредактировал(а) Mayk - 6.6.2008, 10:56


--------------------
 Здесь был кролик. Но его убили.
Человеки < кроликов, йа считаю.
PM MAIL WWW ICQ   Вверх
Andrey44
Дата 6.6.2008, 11:26 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Завсегдатай
Сообщений: 1501
Регистрация: 4.12.2006
Где: На работе

Репутация: 2
Всего: 26



А можно ли вообще возвращать адрес локальной переменной?
Компилятор по-этому поводу предупреждает!


--------------------
????? ??, ??????? ?????.  smile 
PM MAIL WWW ICQ   Вверх
ama_kid
Дата 6.6.2008, 11:29 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


АСУТП-кодер
***


Профиль
Группа: Комодератор
Сообщений: 1460
Регистрация: 5.3.2007
Где: Москва

Репутация: 2
Всего: 95



Цитата(Fazil6 @  6.6.2008,  10:51 Найти цитируемый пост)
используя С++ лучше контейнер (стандартный или самодельный) вместо char*
Оно, конечно, может и лучше, но... фиг знает, иногда бывает необходимо и на чистом С писать, а тогда иметь на вооружении знания о работе с char* оказывается полезным...
Цитата(Fazil6 @  6.6.2008,  10:37 Найти цитируемый пост)
неправильно это. Плохо делать выделение памяти внутри функции. Так ты делаешь вызывающий код зависимым от реализации этой функции.  
Да, это так, но я хочу пока рассмотреть простую ситуацию, где нет переопределения функции new... 
Цитата(Walker @  6.6.2008,  10:45 Найти цитируемый пост)
Библиотечные функции и strcpy в том числе используют именно преопределённые буферы, за выделение и освобождение которых отвечаете Вы.
Цитата(Fazil6 @  6.6.2008,  10:56 Найти цитируемый пост)
вот те раз... приехали... нигде он ее не выделяет. Оба буфера выделены до вызова.  
Да, здесь я возможно просто неправильные примеры привел, сейчас так сходу не рожу что-нить покорректнее, но суть основного вопроса от этого меняется не сильно... Как работать с памятью при такой ситуации, как я описал?

Цитата(Mayk @  6.6.2008,  10:56 Найти цитируемый пост)
realloc курить сюда.
Ага, это если память выделена malloc... А если через new? Делать delete и new заново? Насколько это корректно с точки зрения областей видимости? А если неизвестно - через что выделена память?  smile 
Цитата
    А можно ли вообще возвращать адрес локальной переменной?
Компилятор по-этому поводу предупреждает!
Я написал, что это гипотетический пример и что он явно не рабочий... И вопрос-то как раз в том и стоит - "как мне вернуть динамически сформированную строку"?



--------------------
самурай без меча подобен самураю с мечом, но только без меча 
PM MAIL   Вверх
MAKCim
Дата 6.6.2008, 11:30 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Воін дZэна
****


Профиль
Группа: Экс. модератор
Сообщений: 5644
Регистрация: 10.12.2005
Где: Менск, РБ

Репутация: 52
Всего: 207



Цитата(Andrey44 @  6.6.2008,  11:26 Найти цитируемый пост)
А можно ли вообще возвращать адрес локальной переменной?

можно, но не нужно

Добавлено через 2 минуты и 47 секунд
Цитата(ama_kid @  6.6.2008,  11:29 Найти цитируемый пост)
Оно, конечно, может и лучше, но... фиг знает, иногда бывает необходимо и на чистом С писать, а тогда иметь на вооружении знания о работе с char* оказывается полезным...

два варианта
1. отделить функционал от работы с памятью (выделение/освобождение)
2. использовать статический буфер (TLS-буфер в случае многопоточности)




--------------------
Ах, у елі, ах, у ёлкі, ах, у елі злыя волкі ©

PM MAIL   Вверх
Alek86
Дата 6.6.2008, 11:35 (ссылка)    | (голосов:2) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Завсегдатай
Сообщений: 1299
Регистрация: 30.1.2007
Где: Киев

Репутация: 21
Всего: 25



на плюсах это легко:
Код
std::auto_ptr<char> GetString(void)
{
  char *s;
  s = new char[1000];
  return std::auto_ptr<char>(s);
}


а не ООП тем и хуже, что ты сам обязан за всем следить


--------------------
user posted image    user posted image
PM MAIL   Вверх
ama_kid
Дата 6.6.2008, 11:41 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


АСУТП-кодер
***


Профиль
Группа: Комодератор
Сообщений: 1460
Регистрация: 5.3.2007
Где: Москва

Репутация: 2
Всего: 95



Цитата(Alek86 @  6.6.2008,  11:35 Найти цитируемый пост)
а не ООП тем и хуже, что ты сам обязан за всем следить 
Да я-то и не против следить, мне собственно и нужно знать - КАК следить? Как правильно будет написать возврат строки из функции?
Цитата(MAKCim @  6.6.2008,  11:30 Найти цитируемый пост)
1. отделить функционал от работы с памятью (выделение/освобождение)
 smile для моего случая хотя бы на псевдоалгоритме 


--------------------
самурай без меча подобен самураю с мечом, но только без меча 
PM MAIL   Вверх
Mayk
Дата 6.6.2008, 11:45 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


^аВаТаР^ сообщение>>
****


Профиль
Группа: Участник
Сообщений: 2616
Регистрация: 22.5.2005
Где: за границей разум а

Репутация: 45
Всего: 134



Цитата(ama_kid @  6.6.2008,  15:29 Найти цитируемый пост)
А если неизвестно - через что выделена память? 

См аллокаторы в stl. 


--------------------
 Здесь был кролик. Но его убили.
Человеки < кроликов, йа считаю.
PM MAIL WWW ICQ   Вверх
mes
Дата 6.6.2008, 11:53 (ссылка) |    (голосов:1) Загрузка ... Загрузка ... Быстрая цитата Цитата


любитель
****


Профиль
Группа: Участник Клуба
Сообщений: 7954
Регистрация: 14.1.2006

Репутация: 144
Всего: 250



и в С и в С++ работа по выделению памяти внутри функции не этична
но в С++ можно написать оболочку над буффером (чем с данной точки зрения и является std::string)
"стык" между  С и С++ приводится к сишнему виду, со всеми вытекающими проблемами..
поэтому имхо  вопрос должен относится к чистому С, а не к плюсам


Цитата(Alek86 @  6.6.2008,  11:35 Найти цитируемый пост)
Код

std::auto_ptr<char> GetString(void)
{
  char *s;
  s = new char[1000];
  return std::auto_ptr<char>(s);
}


перекосило от конструкции, хотя отработает она без проблем.
 


--------------------
PM MAIL WWW   Вверх
Alek86
Дата 6.6.2008, 11:55 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Завсегдатай
Сообщений: 1299
Регистрация: 30.1.2007
Где: Киев

Репутация: 21
Всего: 25



Цитата(MAKCim @  6.6.2008,  11:30 Найти цитируемый пост)
отделить функционал от работы с памятью (выделение/освобождение)

думаю имелось в виду сделать 2 функции - получение длины данных и заполнение буфера
тогда

- получил длину
- создал буфер
- заполнил (GetString)
- воспользовался данными
- удалил буфер

а если получение длины неотделимо от GetString, то или выделяй большой буфер и пускай функция выдает ошибку, если его оказалось недостаточно или уж выделяй внутри функции и возвращай
Код
struct String {char* p_str; unsigned length;};


Добавлено @ 11:59
Цитата(mes @  6.6.2008,  11:53 Найти цитируемый пост)
перекосило от конструкции, хотя отработает она без проблем. 

стандартный прием
когда хочется, чтобы функция нужные данные возвращала, и хочется избежать лишних вызовов конструкторов копирования


Цитата(mes @  6.6.2008,  11:53 Найти цитируемый пост)
поэтому имхо  вопрос должен относится к чистому С, а не к плюсам

если функция внутри dll находится, то плюсы тут наравне с сями, ибо реализация auto_ptr не специализирована и даже размеры классов для борланда и мелкософтового компилеров могут отличаться

Это сообщение отредактировал(а) Alek86 - 6.6.2008, 12:00


--------------------
user posted image    user posted image
PM MAIL   Вверх
Ответ в темуСоздание новой темы Создание опроса
Правила форума "С++:Общие вопросы"
Earnest Daevaorn

Добро пожаловать!

  • Черновик стандарта C++ (за октябрь 2005) можно скачать с этого сайта. Прямая ссылка на файл черновика(4.4мб).
  • Черновик стандарта C (за сентябрь 2005) можно скачать с этого сайта. Прямая ссылка на файл черновика (3.4мб).
  • Прежде чем задать вопрос, прочтите это и/или это!
  • Здесь хранится весь мировой запас ссылок на документы, связанные с C++ :)
  • Не брезгуйте пользоваться тегами [code=cpp][/code].
  • Пожалуйста, не просите написать за вас программы в этом разделе - для этого существует "Центр Помощи".
  • C++ FAQ

Если Вам понравилась атмосфера форума, заходите к нам чаще! С уважением, Earnest Daevaorn

 
0 Пользователей читают эту тему (0 Гостей и 0 Скрытых Пользователей)
0 Пользователей:
« Предыдущая тема | C/C++: Общие вопросы | Следующая тема »


 




[ Время генерации скрипта: 0.0886 ]   [ Использовано запросов: 22 ]   [ GZIP включён ]


Реклама на сайте     Информационное спонсорство

 
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности     Powered by Invision Power Board(R) 1.3 © 2003  IPS, Inc.