Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > C/C++: Общие вопросы > Возврат строки неизвестного размера из функции


Автор: ama_kid 6.6.2008, 10:13
Доброго всем времени суток...

Сразу скажу, что при написании топика почитал http://forum.vingrad.ru/index.php?showtopic=181096 и http://forum.vingrad.ru/index.php?showtopic=191122 тему, выданные мне форумом как "похожие темы", ответы там не сильно помогли, поиском тоже пользовался - не айс...  между тем вопрос по большому счету не стОит и того количества букв, которые я тут напишу, но тем не менее, постараюсь в красках и примерах на пальцах объяснить суть ментального геморроя...
Итак, недавно пришлось переводить кое-какой код с Дельфи на С++ и столкнулся с интересной для себя теоретической проблемой, которая подспудно грызла меня давно, но как-то не было случая с ней основательно разобраться... Дело в том, что есть, допустим, гипотетический код на дельфи:
Код
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 * - работают вполне корректно и возвращают указатели на вполне валидные буферы. Да, я понимаю, что там используется несколько другой подход - передаются указатели на буферы, где необходимо разместить ответ, но не до конца понимаю, где происходит выделение памяти под эти буферы? (Тот же http://msdn.microsoft.com/en-us/library/kk6xf663.aspx - как и где выделяет память под char *strDestination?). Единственный более-менее внятный ответ на http://forum.vingrad.ru/index.php?showtopic=16429 от _hunter'а я увидел у http://forum.vingrad.ru/index.php?showtopic=16429&view=findpost&p=109698, но и там возник вопрос: а что если память под параметр char* param выделена не через new, а допустим, через malloc (при условии, что я не знаю точно)?

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

Автор: Fazil6 6.6.2008, 10:37
Цитата(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 содержит необходимую мне строку.
}

неправильно это. Плохо делать выделение памяти внутри функции. Так ты делаешь вызывающий код зависимым от реализации этой функции. 

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

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

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

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

Автор: Fazil6 6.6.2008, 10:51
как вариант сначала запрашивать размер буфера, а потом выделять буфер и вызывать GetString, но честно говоря я бы так не делал. 
По любому используя С++ лучше контейнер (стандартный или самодельный) вместо char* 

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

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

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

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

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

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


вот те раз... приехали... нигде он ее не выделяет. Оба буфера выделены до вызова. 

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

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

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

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

Автор: ama_kid 6.6.2008, 11:29
Цитата(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 
Цитата
    А можно ли вообще возвращать адрес локальной переменной?
Компилятор по-этому поводу предупреждает!
Я написал, что это гипотетический пример и что он явно не рабочий... И вопрос-то как раз в том и стоит - "как мне вернуть динамически сформированную строку"?

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

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

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

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


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


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

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

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

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

Автор: mes 6.6.2008, 11:53
и в С и в С++ работа по выделению памяти внутри функции не этична
но в С++ можно написать оболочку над буффером (чем с данной точки зрения и является 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);
}


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

Автор: Alek86 6.6.2008, 11:55
Цитата(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 не специализирована и даже размеры классов для борланда и мелкософтового компилеров могут отличаться

Автор: ama_kid 6.6.2008, 11:59
Цитата(Mayk @  6.6.2008,  11:45 Найти цитируемый пост)
См аллокаторы в stl
Опять же - поднимаемся до уровня библиотеки шаблонов С++. Оно, конечно, введено в стандарт и все такое, но проблема в том, что во многих компиляторах С, применяемых, допустим, в промышленных контроллерах, она попросту отсутствует. Поэтому интересно узнать решение средствами языка, а не каких-то библиотек (даже если это и трудоёмко). Или наоборот - получить подтверждение, что это невозможно...  smile

Добавлено через 2 минуты и 32 секунды
Цитата(Alek86 @  6.6.2008,  11:55 Найти цитируемый пост)
а если получение длины неотделимо от GetString, то или выделяй большой буфер и пускай функция выдает ошибку, если его оказалось недостаточно или уж выделяй внутри функции и возвращай
Код
struct String {char* p_str; unsigned length;};
Во-во, про второй способ поподробнее пожалуйста... На что должен указывать char* p_str?

Автор: Alek86 6.6.2008, 12:03
Цитата(ama_kid @  6.6.2008,  11:59 Найти цитируемый пост)
На что должен указывать char* p_str?

на начало строки...

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

delete [] != delete.

Цитата(ama_kid @  6.6.2008,  15:59 Найти цитируемый пост)
Опять же - поднимаемся до уровня библиотеки шаблонов С++. Оно, конечно, введено в стандарт и все такое, но проблема в том, что во многих компиляторах С, применяемых, допустим, в промышленных контроллерах, она попросту отсутствует. Поэтому интересно узнать решение средствами языка, а не каких-то библиотек (даже если это и трудоёмко). Или наоборот - получить подтверждение, что это невозможно..

Ну так вместо шаблонов передавай указатели на malloc/realloc/free  если так заботишся о обобщенности, делов то.

Автор: ama_kid 6.6.2008, 12:07
Цитата(Alek86 @  6.6.2008,  12:03 Найти цитируемый пост)
на начало строки... 
А память под строку выделена\освобождена где?

Автор: Mayk 6.6.2008, 12:09
Цитата(ama_kid @  6.6.2008,  16:07 Найти цитируемый пост)
А память под строку выделена\освобождена где?

ДО вызова/а если требутеся - то ВО время. 

Автор: Lazin 6.6.2008, 12:11
можно передавать в функцию не только массив и его размер, но и указатель на функцию изменяющую размер массива...
Код

//тип функции для перераспределения памяти
typedef void* (*realloc_t)(void*, size_t);

char* get_string(char* buf/*out*/, size_t len, realloc_t realloc_)
{
   if (строка buf достаточной длинны)
   {
      записать строку в  buf, 
      return buf;
   }
   в buf слишком мало памяти
   buf = reallock_(buf, новый размер буфера);
   записать строку в buf
   return buf;
}


использовать можно как с динамическим массивом
Код

{
  char* buffer = (char*)realloc(0, 1024);
  buffer = get_string(buffer, 1024, &realloc);
}


так и с массивом размещенным в стеке
Код

void* dummy(void*, size_t)//функция - заглушка
{
 throw std::runtime_error("can't resize array in the stack");
}

{
 char buffer[1024];
 char *p = get_string(buffer, 1024, &dummy);
}

Автор: Alek86 6.6.2008, 12:11
Цитата(Mayk @  6.6.2008,  12:05 Найти цитируемый пост)
delete [] != delete.

точно
интересно, какой хороший человек решил их разделять...

Цитата(ama_kid @  6.6.2008,  12:07 Найти цитируемый пост)
А память под строку выделена\освобождена где?

внутри функции
но это только в том случае, если никак не можешь отделить подсчет необходимой длины от GetString (или он просто будет долго работать)

Автор: ama_kid 6.6.2008, 12:12
Цитата(Mayk @  6.6.2008,  12:05 Найти цитируемый пост)
Ну так вместо шаблонов передавай указатели на malloc/realloc/free  если так заботишся о обобщенности, делов то.
Хорошо, а на что передавать указатели в случае new\delete? Аналогом realloc, кстати, в этом случае что у нас является?

Автор: Mayk 6.6.2008, 12:14
Цитата(ama_kid @  6.6.2008,  16:12 Найти цитируемый пост)
Аналогом realloc, кстати, в этом случае что у нас является?

std::copy блин
Цитата(ama_kid @  6.6.2008,  16:12 Найти цитируемый пост)
Хорошо, а на что передавать указатели в случае new\delete?

Цитата(Mayk @  6.6.2008,  15:45 Найти цитируемый пост)
См аллокаторы в stl.  

Цитата(Mayk @  6.6.2008,  15:45 Найти цитируемый пост)
См аллокаторы в stl.  

Цитата(Mayk @  6.6.2008,  15:45 Найти цитируемый пост)
См аллокаторы в stl.  


На что передаются указатели  в аллокаторы?
На что передаются указатели  в аллокаторы?
На что передаются указатели  в аллокаторы?
На что передаются указатели  в аллокаторы?
На что передаются указатели  в аллокаторы?
На что передаются указатели  в аллокаторы?
На что передаются указатели  в аллокаторы?

Автор: ama_kid 6.6.2008, 12:14
Lazin, привел хороший пример, но к тебе тот же вопрос...

Автор: Lazin 6.6.2008, 12:15
Цитата(ama_kid @  6.6.2008,  12:12 Найти цитируемый пост)
Аналогом realloc, кстати, в этом случае что у нас является?

новый массив = new[] 
memcpy из старого в новый (если нужно)
delete[] старый массив

Автор: mes 6.6.2008, 12:16
Цитата(Alek86 @  6.6.2008,  11:55 Найти цитируемый пост)
если функция внутри dll находится

тут два варианта , или "чистый" виртуальный интерфейс, либо С -подход (не смотря на то что прога на С++)

Автор: Lazin 6.6.2008, 12:16
в си алокаторов и new/delete нету, так к слову...

Автор: nirburg 6.6.2008, 12:17
ama_kid, перед вызовом подобных функций использую предварительное выделение заведомо достаточного объема памяти в стеке, либо хипе (зависит от необходимых размеров). соответственно, память освобождается в той же функции, которая ее выделяла. если максимальный размер возвращаемых данных никак заранее нельзя определить (хотя бы примерно), то, дабы не обременять себя "ментальным геморроем", использую std::string. 

т.к., судя по всему, эти пути решения вас не устраивают, можно делать "пробный" вызов функции для определения необходимого объема памяти, выделять эту память, после чего вызывать функцию "по-настоящему". такой вариант, кажется, применяется в некоторых функциях ядра Windows, или, по крайней мере, где-то недалеко от ядра smile.

поясню:
Код

char *myfunc(char *buffer, int *len)
{
    if (!*len) {
        // определение необходимого размера
        *len = ...;
    } else {
        // работа с буфером
    }
}

void otherfunc()
{
    int buflen = 0;
    char *mybuf;
    
    myfunc(NULL, &buflen);      // buflen = необходимый размер буфера, первый параметр не нужен
    mybuf = new char [buflen];  // или malloc
    myfunc(mybuf, &buflen);
    delete [] mybuf;            // или free
}


как-то так, в общем.

Автор: ama_kid 6.6.2008, 12:17
Цитата(Mayk @  6.6.2008,  12:14 Найти цитируемый пост)
std::copy блин
хмм.. ну тада уж проще использовать с
std::string smile

Добавлено @ 12:20
Цитата(Lazin @  6.6.2008,  12:15 Найти цитируемый пост)
новый массив = new[] 
memcpy из старого в новый (если нужно)
delete[] старый массив 
Повторюсь - что в этом случае с областью видимости в случае использования внутри функции? Ничего, что мы выделяем память, которая будет невалидна потом?
Цитата(Lazin @  6.6.2008,  12:16 Найти цитируемый пост)
в си алокаторов и new/delete нету, так к слову... 
Ну, на самом деле иногда приходится работать со срещенным С\С++ - new\delete есть, а библиотеки шаблонов нет smile
Добавлено @ 12:21
Цитата(nirburg @  6.6.2008,  12:17 Найти цитируемый пост)
как-то так, в общем
В общем-то полностью согласен, и изначальный вопрос родился из мыслей: "а нельзя ли сделать как-нить эдак, чтобы не так?!"

Автор: Alek86 6.6.2008, 12:24
Цитата(mes @  6.6.2008,  12:16 Найти цитируемый пост)
или "чистый" виртуальный интерфейс

даже чистый вирт интерфейс не может работать с переменными типов std::auto_ptr и т.п.

Автор: mes 6.6.2008, 12:30
Цитата(Alek86 @  6.6.2008,  12:24 Найти цитируемый пост)
даже чистый вирт интерфейс не может работать с переменными типов std::auto_ptr и т.п. 

естественно ..  однако это не мешает использовать нам их в реализации ..


Автор: Lazin 6.6.2008, 12:31
Цитата(ama_kid @  6.6.2008,  12:17 Найти цитируемый пост)
Повторюсь - что в этом случае с областью видимости в случае использования внутри функции? Ничего, что мы выделяем память, которая будет невалидна потом?

все будет ОК, что-бы удалить блок памяти, не нужно знать его размер

Автор: nirburg 6.6.2008, 12:32
Цитата(ama_kid @  6.6.2008,  12:17 Найти цитируемый пост)
"а нельзя ли сделать как-нить эдак, чтобы не так?!"

как-нибудь эдак, конечно, можно. вопрос - нужно ли? smile 

(шепотом) там народ уже интерфейсы обсуждает )) скоро, чувствую, все сведется к необходимости создания дерева классов со сложной иерархией, наследованием, и прочей модной шелухой =)

Автор: mes 6.6.2008, 12:32
тем более вопрос был не об auto_ptr , а о том как избежать проблем с выделением памяти внутри функции

Добавлено через 1 минуту и 11 секунд
Цитата(nirburg @  6.6.2008,  12:32 Найти цитируемый пост)
(шепотом) там народ уже интерфейсы обсуждает )) скоро, чувствую, все сведется к необходимости создания дерева классов со сложной иерархией, наследованием, и прочей модной шелухой =) 

))

Автор: ama_kid 6.6.2008, 12:35
Цитата(Lazin @  6.6.2008,  12:31 Найти цитируемый пост)
все будет ОК, что-бы удалить блок памяти, не нужно знать его размер 
Удалить-то мы его удалим, а куда денется выделенная по-новой память при выходе из функции?

Автор: mes 6.6.2008, 12:39
Цитата(ama_kid @  6.6.2008,  12:35 Найти цитируемый пост)
Удалить-то мы его удалим, а куда денется выделенная по-новой память при выходе из функции?

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

Автор: ama_kid 6.6.2008, 12:51
Цитата(mes @  6.6.2008,  12:39 Найти цитируемый пост)
в остальных как и говорилось выше - нужно выделять памятж вне функции
Опять же - а если вне функции неизвестен размер требуемой памяти?

Хорошо, у меня вроде бы сформировался более-менее основной вопрос, попробую его сформулировать: как после выполнения оператора
Код

char * s = new char[2] = "";
... 
 s = GetString();
...
delete s;
получить корректный буфер s (т.е. что должно быть вместо "..." и внутри функции GetString? ), при условиях: 
1) размер строки становится известен только внутри GetString()... 
2) пользуясь new\delete? 
P.S. Для случаев с malloc\realloc\free я более-менее уже понимаю... 

Автор: vinter 6.6.2008, 13:01
присваивать так низя, юзай strcpy

Автор: ama_kid 6.6.2008, 13:06
Цитата(vinter @  6.6.2008,  13:01 Найти цитируемый пост)
присваивать так низя, юзай strcpy 
что передавать в strcpy? 
Ну это гипотетический пример, хоть strcpy, хоть что-то другое , без разницы, главное - на выходе получить валидный буфер

Автор: Fazil6 6.6.2008, 13:09
Цитата(ama_kid @  6.6.2008,  12:51 Найти цитируемый пост)
получить корректный буфер s (т.е. что должно быть вместо "..." и внутри функции GetString? ), при условиях: 
1) размер строки становится известен только внутри GetString()... 
2) пользуясь new\delete? 

Код

unsigned int GetString( char* buf , unsigned int buf_len)
{
     // пишем сколько влазит
     // возвращаем сколько нужно чтобы влезло
}

// если вернуло больше чем buf_len, то значит вернули не все


Добавлено через 1 минуту и 8 секунд
кстати 
Код

delete [] s;

Автор: ama_kid 6.6.2008, 13:19
Fazil6, ага, вариант понятен... В принципе, я так и делал всегда...

Кстати, щас только сообразил, что непроходимо туплю - сам же на свой вопрос вывесил в топике ответ - ссылка ответа от http://forum.vingrad.ru/index.php?showtopic=16429&view=findpost&p=109698. В связи с этим вопрос - насколько корректен приведённый там пример? Просто никто там после этого не отписался по поводу правильности\неправильности. Если он правилен - тогда вопрос снимается...

Автор: JackYF 6.6.2008, 13:25
Ого, сколько написали...

Подытожу по себе (то есть, как бы это делал я).
Варианта 2:

1) целевой язык - С

Код

char* func(int param)
{
  unsigned int size;
  //...
  char* p = new char[size];
  return p;
}

//...
char* u = func(n);
//...
delete [] u;


2) целевой язык - С++
Код

std::string func(int param)
{
  std::string buf;
  //...
  return buf;
}

//...

std::string u = func(n);
//...


В первом случае ты сам заботишься о том, чтобы освободить выделенную внутри функции память, во втором случае за тебя всё уже сделано.

Автор: ama_kid 6.6.2008, 13:28
Цитата(JackYF @  6.6.2008,  13:25 Найти цитируемый пост)
В первом случае ты сам заботишься о том, чтобы освободить выделенную внутри функции память
ага, а не будет ли в этом случае Access Violation в связи с доступом к невалидному участку памяти? Или участок памяти, выделенный внутри функции будет валидным?

Автор: nirburg 6.6.2008, 13:28
JackYF, в C нет new и delete  smile 

Автор: mes 6.6.2008, 13:32
Цитата(JackYF @  6.6.2008,  13:25 Найти цитируемый пост)
1) целевой язык - С

Цитата(JackYF @  6.6.2008,  13:25 Найти цитируемый пост)
char* p = new char[size];

 smile  smile 

Цитата(ama_kid @  6.6.2008,  13:19 Найти цитируемый пост)
сам же на свой вопрос вывесил в топике ответ - ссылка ответа от RAN

как я понял имеется ввиду этот ответ:
http://forum.vingrad.ru/index.php?showtopic=16429&view=findpost&p=109698

Код

void change_string(char* param) // передаем указатель на память 
{
   delete param; // удаляем .. не этично, хотя сработает если внимательно следить  
   const char str[] = "new string";
   param = NULL;
   param = new char[ sizeof(str) ]; // выделили память - но она станет не валидной после завершения функции 

// как минимум надо было  написать void change_string(char*&param) -
// но даже в ьтом случае пример выглядит не очень приятно ))




Автор: Fazil6 6.6.2008, 13:34
Цитата(ama_kid @  6.6.2008,  13:19 Найти цитируемый пост)
ссылка ответа от RAN. В связи с этим вопрос - насколько корректен приведённый там пример?


Зная этого чела лично, могу сказать с определенностью, что сейчас он так делать не стал бы.  smile 
Я тоже так делать не сталбы.
Код

void getTq(char * p)
{
    delete p;   //  ЧПОК!!!!!!!!
}


int main()
{
    char s[] = "!!!";
    getTq(s);
    return 0;
}

Автор: nirburg 6.6.2008, 13:41
Цитата(mes @  6.6.2008,  13:32 Найти цитируемый пост)
param = new char[ sizeof(str) ]; // выделили память - но она станет не валидной после завершения функции 

ну вот, приехали. new, оказывается, выделяет память в стеке? или C++ обзавелся хитрым сборщиком мусора, который подтирает всю выделенную в функции память?

Автор: mes 6.6.2008, 13:47
Цитата(nirburg @  6.6.2008,  13:41 Найти цитируемый пост)
Цитата(mes @  6.6.2008,  13:32 )
param = new char[ sizeof(str) ]; // выделили память - но она станет не валидной после завершения функции 
ну вот, приехали. new, оказывается, выделяет память в стеке? или C++ обзавелся хитрым сборщиком мусора, который подтирает всю выделенную в функции память? 

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

в коментариях в предыдушем посту это и указано  ))

Автор: Daevaorn 6.6.2008, 13:59
Цитата(mes @  6.6.2008,  14:47 Найти цитируемый пост)
просто значения указателя не возврашается из функции - т.е указатель переданный в функцию, после ее завершения, продолжает показывать  на ту же самую область что и раньше

Не, ну выделить память внутри функции и выдать её наружу вполне себе юзкейс. Так, допустим, malloc поступаетsmile

Автор: mes 6.6.2008, 14:04
Цитата(Daevaorn @  6.6.2008,  13:59 Найти цитируемый пост)
Не, ну выделить память внутри функции и выдать её наружу вполне себе юзкейс. Так, допустим, malloc поступает 

нет проблемы в том, чтоб выделить память внутри функции
проблема в том чтоб знать когда она выделилась и удалить ее когда нужно.
и самое главное, чтоб этот процесс не отвлекал от основной цели - программирование,
ибо если постояно помнить реализацию каждой функции, то и с ума сойти можно  smile 

Автор: nirburg 6.6.2008, 14:05
mes, прошу прощения, неправильно понял smile да, вы правы, param будет указывать в космос

Daevaorn, ну malloc'y на роду написано память внутри себя выделять. а если так будет поступать каждая гордая функция, то что с программой будет твориться, я не представляю

Автор: mes 6.6.2008, 14:07
malloc и new тем и хороши что всегда не задумываясь (о том как реализована функция) понятно, что делает функция и одназначно напрашивается вывод о применение ее антонима.
однако, что нужно делать после функции GetString (..) (удалять память или нет)  имхо отнюдь не очевидно  smile 
поэтому (на мой взгляд)  и принято "неписанное " соглашении о использовании внутри функции зараннее выделенного буффера..

Автор: Daevaorn 6.6.2008, 14:15
Цитата(mes @  6.6.2008,  15:07 Найти цитируемый пост)
поьтому и принято (на мой взгляд) "неписанное " соглашении о использовании внутри функции зараннее выделенного буффера

а если строка больше чем буфер? Тогда ещё одну функцию заводить, которая вернет размер, потом выделить память, и только потом передать в функцию - много буквsmile

Хотя, конечно, это оптимально с точки зрения прозрачности кода.

Автор: JackYF 6.6.2008, 14:33
Цитата(nirburg @  6.6.2008,  12:28 Найти цитируемый пост)
JackYF, в C нет new и delete 

 smile Верно smile
Код

char* func(int param)
{
  unsigned int size;
  //...
  char* p = (char*) malloc(size);
  return p;
}
//...
char* u = func(n);
//...
free(u);


Автор: mes 6.6.2008, 15:01
Цитата(Daevaorn @  6.6.2008,  14:15 Найти цитируемый пост)
а если строка больше чем буфер? Тогда ещё одну функцию заводить, которая вернет размер, потом выделить память, и только потом передать в функцию - много букв  

вот пример без добавления второй функции 
Код


int GetString (char *p, int size) // возврашает кол-во символов непоместившихся в буффер
{ ..
};

void Test()
{
   int size =   GetString( NULL, 0);

   char *p = new char(size); 
   GetString(p, size);
   ...
   delete...

};

Автор: Daevaorn 6.6.2008, 15:09
Цитата(mes @  6.6.2008,  16:01 Найти цитируемый пост)
вот пример без добавления второй функции 

ну по сути вы её добавили, сделав в GetString проверку `if( p == 0 )`.

Автор: ama_kid 6.6.2008, 15:26
В общем, как я понял, сделать средствами new\delete (без привлечения библиотек-контейнеров и выкрутасов с несколькими функциями\вызовами) аналогичный дельфёвому фокус не удастся. Что ж, будет еще один плюс в пользу malloc\realloc smile  Думаю, тему можно закрывать, потому что обсуждение идет по десятому кругу, всем спасибо за активное обсуждение...  smile
Хотя, если появятся новые идеи - буду рад увидеть, да и для потомков, думаю, будет полезно...  smile 

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)