URL: https://www.opennet.ru/cgi-bin/openforum/vsluhboard.cgi
Форум: vsluhforumID9
Нить номер: 2647
[ Назад ]

Исходное сообщение
"Класс OSTRSTREAM странное поведение"

Отправлено FrOdO , 10-Мрт-04 13:31 
Hi ALL.

Столкнулся с классом ostrstream, позволяющим писать в буфер.
В цикле у меня вызывется функция, в которой формируется буфер, с помощью данного класса. Причем буфер бывает разной длины, в зависимости от данных. У меня появляются концы старых, более длинных, строк. Хотя при выходе из функции объект класса должен уничтожаться, зн. и внутренний буфер со старыми данными, тоже должен исчезнуть.
Я вывожу сформированные данные на stdout, получив до этого замороженный буфер посредством метода str() (потом я удаляю этот буфер оператором delete[]). Свои изыскания я основывал на книге Тереса Чана "Системное программирование на С++ для Unix".
Если предположить, что внутри класса используется статический буфер, не уничтожающийся при выходе из функции, то все равно он должен выставлять корректный размер данных при входе в эту функцию снова.

Может кто сталкивался с подобным явлением. У меня стоит Fedore Core 1, gcc 3.3.3.

Благодарю за внимание.


Содержание

Сообщения в этом обсуждении
"Класс OSTRSTREAM странное поведение"
Отправлено solov , 10-Мрт-04 17:16 
Ты имееш ввиду класс ostringstream? Покажи код...

"Класс OSTRSTREAM странное поведение"
Отправлено FrOdO , 10-Мрт-04 17:49 
>Ты имееш ввиду класс ostringstream? Покажи код...

Пример:

// cut here ------------

#include <strstream>
#include <iostream>
#include <string>
using namespace std;

string str[4] = {"str1", "str1str2", "str1str2str3", "sr"};
int i = 0;

char *Func()
{
  ostrstream s_out;
  s_out << "String " << str[i] << endl;
  return s_out.str();
}

int main()
{
  for(; i < 4; i++)
  {
    char *s = Func();
    cerr << s;
    delete[] s;
  }

  cerr << endl;
  return 0;
}

// cut here ------------

при работе выдает следующий результат:

String str1
String str1str2
String str1str2str3
String sr
1str2s

последней строки быть не должно !!!


"Класс OSTRSTREAM странное поведение"
Отправлено solov , 10-Мрт-04 19:33 
#include <sstream>
#include <iostream>
#include <string>


std::string str[4] = {"str1", "str1str2", "str1str2str3", "sr"};
int i = 0;


char* Func()
{
   std::ostringstream s_out;
   s_out << "String " << str[i] << std::endl;
   return (s_out.str()).c_str();   // !!!!!
}

int main()
{
   for(; i < 4; i++) {

   char *s = Func();
   std::cout << s;
   //delete [] s;
   }

   std::cout << std::endl;

   return 0;
}


"Класс OSTRSTREAM странное поведение"
Отправлено asso , 11-Мрт-04 04:42 
>char* Func()
>{
>   std::ostringstream s_out;
>   s_out << "String " << str[i] << std::endl;
>   return (s_out.str()).c_str();   // !!!!!
>}

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

Надо либо вообще все переделать либо сделать что-то вроде

#include <sstream>
#include <iostream>
#include <string>


std::string str[4] = {"str1", "str1str2", "str1str2str3", "sr"};
int i = 0;


std::string Func()
{
   std::ostringstream s_out;
   s_out << "String " << str[i] << std::endl;
   return s_out.str();
}

int main()
{
   for(; i < 4; i++) {

   std::string s(Func());
   std::cout << s;
   //delete [] s;
   }

   std::cout << std::endl;

   return 0;
}


"Класс OSTRSTREAM странное поведение"
Отправлено solov , 11-Мрт-04 13:51 
>Вот это то же не должно работать.  s_out размещается в стеке
>и уничтожается при выходе из функции, а вместе с ней и
>строка которая была записана.


Память под char* выделяется статически !!!, поэтому после вызова функции не освобождается


"Класс OSTRSTREAM странное поведение"
Отправлено asso , 12-Мрт-04 05:59 
>Память под char* выделяется статически !!!, поэтому после вызова функции не освобождается

Не понял что ты этим хотел сказать.

Посмотри исходный текст std::strsream:

typedef basic_string<char_type, _Traits, _Alloc> __string_type;

// Get and set:
/**
*  @brief  Copying out the string buffer.
*  @return  A copy of one of the underlying sequences.
*
*  "If the buffer is only created in input mode, the underlying
*  character sequence is equal to the input sequence; otherwise, it
*  is equal to the output sequence." [27.7.1.2]/1
*/
__string_type
str() const
{
    if (_M_mode & ios_base::out)
      {
        // This is the deal: _M_string.size() is a value that
        // represents the size of the initial string that makes
        // _M_string, and may not be the correct size of the
        // current stringbuf internal buffer.
        __size_type __len = _M_string.size();
        if (_M_out_end > _M_out_beg)
          __len = max(__size_type(_M_out_end - _M_out_beg), __len);
        return __string_type(_M_out_beg, _M_out_beg + __len);
      }
    else
      return _M_string;
}

Другими словами создается новый экземпляр __string_type.  В случае std::ostrstream тип __string_type эквивалентен std::string.  Метод c_str() у std::string не выделяет память а возвращает указатель на свой внутренний буфер (поэтому его не надо освобождать).

Другими словами, код

const char *foo() {
  std::string s("la-la-la");
  return s.c_str();
}

будет возвращать неправильный указатель потому что строка s будет уничтожена при возврате из функции.  Код

   return (s_out.str()).c_str();

не будет работать правильно по той же причине.  Более того, специально для таких случаев метод c_str() возвращает не просто char*, а const char*, поэтому этот код даже не откомпилируется.

На практике можно явно преобразовать const char* в char* и это, возможно, даже будет иногда работать в зависимости от состояния кучи и стека.  Но состояние кучи и стека не всегда будет оставаться благоприятным поэтому ошибка рано или поздно даст о себе знать.


"Класс OSTRSTREAM странное поведение"
Отправлено solov , 12-Мрт-04 13:33 
>Вот это то же не должно работать.  s_out размещается в стеке и >уничтожается при выходе из функции, а вместе с ней и строка которая была >записана.

Почти все правильно кроме одного что мы получаем при помощи c_str() следующий тип char* и этот тип всегда будет static поэтому и остается в стеке. Но ты прав с константой. Да c_str() возращает тип const char*  поэтому пишим
const_cast<char*>((s_out.str()).c_str())
Но это уже извращение да и функция char* Func() не нужна.


"Класс OSTRSTREAM странное поведение"
Отправлено asso , 13-Мрт-04 08:35 
>Почти все правильно кроме одного что мы получаем при помощи c_str() следующий
>тип char* и этот тип всегда будет static поэтому и остается
>в стеке.

Да, на счет этого я был не прав.  Действительно получается static, поэтому никаких проблем с памятью не будет.


"Класс OSTRSTREAM странное поведение"
Отправлено sas , 13-Мрт-04 12:58 
>>Вот это то же не должно работать.  s_out размещается в стеке и >уничтожается при выходе из функции, а вместе с ней и строка которая была >записана.
>
>Почти все правильно кроме одного что мы получаем при помощи c_str() следующий
>тип char* и этот тип всегда будет static поэтому и остается
>в стеке. Но ты прав с константой. Да c_str() возращает тип
>const char*  поэтому пишим
>const_cast<char*>((s_out.str()).c_str())
>Но это уже извращение да и функция char* Func() не нужна.

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

Простите, но откуда здесь static?

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

Стандарт  четко говорит, что возвращаемый c_str const указатель верен ТОЛЬКО пока строка (объект класса) существует и пока не была вызвана хотя бы одна не константная функция.

Удачи
--- sas


"Класс OSTRSTREAM странное поведение"
Отправлено solov , 13-Мрт-04 13:23 
>Доброго времени суток,
>
>Простите, но откуда здесь static?

Стандарт четко говорит что char* всегда будет static char*

>
>Ваше мнение к сожалению ложно. Как сказал предидущий оратор - это работать
>если и будет, то только случайно при определенных условиях.
>
>Стандарт  четко говорит, что возвращаемый c_str const указатель верен ТОЛЬКО пока
>строка (объект класса) существует и пока не была вызвана хотя бы
>одна не константная функция.
>
>Удачи
>--- sas



"Класс OSTRSTREAM странное поведение"
Отправлено sas , 13-Мрт-04 13:35 
>>Доброго времени суток,
>>
>>Простите, но откуда здесь static?
>
>Стандарт четко говорит что char* всегда будет static char*
>
>>
>>Ваше мнение к сожалению ложно. Как сказал предидущий оратор - это работать
>>если и будет, то только случайно при определенных условиях.
>>
>>Стандарт  четко говорит, что возвращаемый c_str const указатель верен ТОЛЬКО пока
>>строка (объект класса) существует и пока не была вызвана хотя бы
>>одна не константная функция.
>>
>>Удачи
>>--- sas

Я прошу прощения и где он это говорит? Ссылочка или хотя-бы пункт помогли бы отцу русской демократии :)

Удачи
--- sas


"Класс OSTRSTREAM странное поведение"
Отправлено solov , 13-Мрт-04 16:24 
Строковым литералом называется последовательность символов заключенная в двойные кавычки:
“Это строка”
.....

Память под строкавые литералы выделяется статически поэтому их свободно можно возращать в качестве значения функции. Например:
    const char* error_message(int i)
    {
        // ...
        retrun “Выход за пределы диапазона”;
    }
Память содержащая строку “Выход за пределы диапазона” не будет освобождена после вызова error_message().

Стр. 130 Глава 5.2.2.  “Язык программирования С++ специальное издание” Bjarne Stroustrup



"Класс OSTRSTREAM странное поведение"
Отправлено sas , 13-Мрт-04 17:23 
>Строковым литералом называется последовательность символов заключенная в двойные кавычки:
>?Это строка?
>.....
>
>Память под строкавые литералы выделяется статически поэтому их свободно можно возращать в
>качестве значения функции. Например:
> const char* error_message(int i)
> {
>  // ...
>  retrun ?Выход за пределы диапазона?;
> }
>Память содержащая строку ?Выход за пределы диапазона? не будет освобождена после вызова
>error_message().
>
>Стр. 130 Глава 5.2.2.  ?Язык программирования С++ специальное издание? Bjarne Stroustrup
>

Прекрасно!!! :)) Возразить НЕЧЕГО... И где же здесь аналогия с нашим случаем?

Удачи
--- sas


"Класс OSTRSTREAM странное поведение"
Отправлено ed , 13-Мрт-04 17:24 
>Строковым литералом называется последовательность символов заключенная в двойные кавычки:
>?Это строка?
>.....
>

Ну и что ? Да "String " и "str1str2str3" - static const char*, но неужели ты думаеш что (std::string("String ") + "str1str2str3") все еще static !?


"Класс OSTRSTREAM странное поведение"
Отправлено sas , 13-Мрт-04 17:27 
>Строковым литералом называется последовательность символов заключенная в двойные кавычки:
>?Это строка?
>.....
>
>Память под строкавые литералы выделяется статически поэтому их свободно можно возращать в
>качестве значения функции. Например:
> const char* error_message(int i)
> {
>  // ...
>  retrun ?Выход за пределы диапазона?;
> }
>Память содержащая строку ?Выход за пределы диапазона? не будет освобождена после вызова
>error_message().
>
>Стр. 130 Глава 5.2.2.  ?Язык программирования С++ специальное издание? Bjarne Stroustrup
>

Уважаемый  solov!

А вдогонку вопрос: А можно менять постоянные статические строки?

Удачи
--- sas


"Класс OSTRSTREAM странное поведение"
Отправлено sas , 13-Мрт-04 17:51 
>Строковым литералом называется последовательность символов заключенная в двойные кавычки:
>?Это строка?
>.....
>
>Память под строкавые литералы выделяется статически поэтому их свободно можно возращать в
>качестве значения функции. Например:
> const char* error_message(int i)
> {
>  // ...
>  retrun ?Выход за пределы диапазона?;
> }
>Память содержащая строку ?Выход за пределы диапазона? не будет освобождена после вызова
>error_message().
>
>Стр. 130 Глава 5.2.2.  ?Язык программирования С++ специальное издание? Bjarne Stroustrup
>

Уважаемый Mr Solov,

И еще один вопрос: Меняется ли содержимое объекта (i|o)stringstream  в процессе его жизнидеятельности?

Удачи
--- sas


"Класс OSTRSTREAM странное поведение"
Отправлено FrOdO , 11-Мрт-04 14:46 
Всем спасибо, разобрался.
Плохо, когда нет хорошего хелпа с возможностью поиска.

"Класс OSTRSTREAM странное поведение"
Отправлено sas , 13-Мрт-04 18:22 
>Hi ALL.
>
>Столкнулся с классом ostrstream, позволяющим писать в буфер.
>В цикле у меня вызывется функция, в которой формируется буфер, с помощью
>данного класса. Причем буфер бывает разной длины, в зависимости от данных.
>У меня появляются концы старых, более длинных, строк. Хотя при выходе
>из функции объект класса должен уничтожаться, зн. и внутренний буфер со
>старыми данными, тоже должен исчезнуть.
>Я вывожу сформированные данные на stdout, получив до этого замороженный буфер посредством
>метода str() (потом я удаляю этот буфер оператором delete[]). Свои изыскания
>я основывал на книге Тереса Чана "Системное программирование на С++ для
>Unix".
>Если предположить, что внутри класса используется статический буфер, не уничтожающийся при выходе
>из функции, то все равно он должен выставлять корректный размер данных
>при входе в эту функцию снова.
>
>Может кто сталкивался с подобным явлением. У меня стоит Fedore Core 1,
>gcc 3.3.3.
>
>Благодарю за внимание.

Для полноты картины, хотя и несколько поздновато:

Объекты классов (i|o|)strstream являются устаревшими. По стандарту пользователь обязан сам "закрывать" строку  добавляя ends, если ему это нужно.
...
 ostrstream s_out;
 s_out << "String " << str[i] << "\n" << ends;
 return s_out.str();
...

Массив который Вы возвращаете выделен из динамической памяти

Как уже было указано  лучше использовать stringstream.

Удачи
--- sas


"Класс OSTRSTREAM странное поведение"
Отправлено sas , 13-Мрт-04 18:36 
Гораздо более полное и понятное объяснение чем мое :)

http://docs.freebsd.org/info/iostream/iostream.info.Strings....

Удачи
--- sas

>>Hi ALL.
>>
>>Столкнулся с классом ostrstream, позволяющим писать в буфер.
>>В цикле у меня вызывется функция, в которой формируется буфер, с помощью
>>данного класса. Причем буфер бывает разной длины, в зависимости от данных.
>>У меня появляются концы старых, более длинных, строк. Хотя при выходе
>>из функции объект класса должен уничтожаться, зн. и внутренний буфер со
>>старыми данными, тоже должен исчезнуть.
>>Я вывожу сформированные данные на stdout, получив до этого замороженный буфер посредством
>>метода str() (потом я удаляю этот буфер оператором delete[]). Свои изыскания
>>я основывал на книге Тереса Чана "Системное программирование на С++ для
>>Unix".
>>Если предположить, что внутри класса используется статический буфер, не уничтожающийся при выходе
>>из функции, то все равно он должен выставлять корректный размер данных
>>при входе в эту функцию снова.
>>
>>Может кто сталкивался с подобным явлением. У меня стоит Fedore Core 1,
>>gcc 3.3.3.
>>
>>Благодарю за внимание.
>
>Для полноты картины, хотя и несколько поздновато:
>
>Объекты классов (i|o|)strstream являются устаревшими. По стандарту пользователь обязан сам "закрывать" строку
> добавляя ends, если ему это нужно.
>...
> ostrstream s_out;
> s_out << "String " << str[i] << "\n" << ends;
> return s_out.str();
>...
>
>Массив который Вы возвращаете выделен из динамической памяти
>
>Как уже было указано  лучше использовать stringstream.
>
>Удачи
>--- sas