Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > Visual C++/MFC/WTL > Проблемы с STL vector


Автор: mantissa 23.11.2007, 17:02
Привет Великий ALL!
Может кто сталкивался с подобным и поможет разобраться в следующем....
имеем:
  код проги Win32 на С++,
  Visual MS .net 2003
  STL ее же поставки
использую контейнер vector<string> (например v)
 вар.1. 
   после чистки v.clear()  вызываю функцию и передаю в ней ссылку на v
   на 2 вызове после 3 или 4 вставки v.push_back вызывает исключение и программа слетает
   если всего 2 вставки, то проблем не заметил...
вар.2.
  выношу чистку в ту функцию (ставлю перед циклом)
  проблема пропадает... все работает

НУ КАКАЯ ЕМУ (контейнеру) РАЗНИЦА????
с ув. BSD

Автор: Sartorius 23.11.2007, 17:04
 код в студию.

Автор: NiJazz 23.11.2007, 18:00
Следующее работало в VS 2k5 SP1:
Код

#include <string>
#include <vector>

void f( std::vector<std::string>& v )
{
    v.push_back( "1" );
    v.push_back( "2" );
    v.push_back( "3" );
    v.push_back( "4" );
}

int _tmain(int argc, _TCHAR* argv[])
{    
    std::vector<std::string> v;

    v.push_back( "1" );
    v.push_back( "2" );
    v.push_back( "3" );

    v.clear();

    f( v );

    return 0;
}


Добавлено через 1 минуту и 33 секунды
Попробуй выставить блок catch ( const std::exception& ex ) и посмотри, что вернёт ex.what(). 
Например:
Код

catch ( const std::exception& ex )
{
   std::cout << ex.what();
}


Автор: Sartorius 23.11.2007, 18:07
 У меня 2003-я все работает. May be переустановить студию?

Автор: JackYF 23.11.2007, 22:21
жестоко... ты уверен, что ты до работы с вектором нигде в памяти не накосячил?

Автор: Earnest 25.11.2007, 13:10
Цитата(JackYF @  23.11.2007,  23:21 Найти цитируемый пост)
жестоко... ты уверен, что ты до работы с вектором нигде в памяти не накосячил?

скорее всего.
Еще одна возможная причина: DLL + разные экземпляры CRT (например, как статик в каждой DLL) - т.е. кривой проект.

Автор: mantissa 26.11.2007, 18:34
Приветствую всех откликнувшихся!

JackYF > жестоко... ты уверен, что ты до работы с вектором нигде в памяти не накосячил?
допустим, но слабо вериться, что вызов метода v.clear() непосредственно перед вставкой способен исправить то, что я накосячил в памяти

Earnest> Еще одна возможная причина: DLL + разные экземпляры CRT (например, как статик в каждой DLL) - т.е. кривой проект.
вызываемая функция действительно компилируется в DLL...
а можно поподробнее... про  разные экземпляры CRT и т.д.

глюки с DLL уже видел (но в другом проекте), похоже отладчик шел по DLL, созданной DEBUG, а показывал RELEASE версию, но когда очередной STEP вместо функции MessageBox() выполнял CONTINUE на начало цикла, я чуть с кресла не упал....

а насчет кода... попробую выжимки сделать....

файл1
...
namespace {
  ...
  vector <string> vcsErrInfo;
 ...
}
fun1
{
...
  case WM_TIMER:
   ...
   reread_data(hdlg);
   ...
}
fun reread_data()
{
    ...    
    ::vcsErrInfo.clear();
    mcr = get_ErrInfo(::vcsErrInfo);
   ...
}
file2
...
EXPORT int get_ErrInfo     (vector<string>&);
...

file3
EXPORT int get_ErrInfo(vector<string>&vcS)
{
    FILE *fil;
  $ CHAR    buf[2048];
  //$ CHAR    buf1[256];
    CHAR    szDop[256] = "zero";
  fil = fopen("get_ErrInfo.log", "a+");
  fprintf(fil, "%s\n", "=============================\n get_ErrInfo: started ...");
  fflush(fil);
$WHENEVER sqlerror goto ERROR_SQL;
  lstrcpy(szDop, "declare curs1 cursor");
  $declare curs1 cursor with HOLD for 
    select dt||" "||fg||" "||nfile||" "||code||"     %1%"||NVL(s_num,"--")||" %2%"||NVL(unum,"--")||" %3%"||NVL(line,"--")||" %4%"||NVL(pos,"--")||" %5%"||NVL(beg_line,"--")||' %6%'||NVL(text,"ОПИСАНИЕ ОТСУТСТВУЕТ")||' %7%'||NVL(kodmes,'---')
--    select extend(dt,hour to second)||" "||fg||" "||nfile||" "||code||" %1%"||s_num||" %2%"||NVL(unum,"--")||" %3%"||NVL(line,"--")||" %4%"||NVL(pos,"--")||" %5%"||NVL(beg_line,"--")||' %6%'||NVL(text,"ОПИСАНИЕ ОТСУТСТВУЕТ")
   -- NVL(s_num,"---")||" "||NVL(unum,"---")||" "||NVL(line,"---")||" "||NVL(pos,"---")||" "||NVL(beg_line,"---")||" "||NVL(text,"ОПИСАНИЕ ОТСУТСТВУЕТ") 
    from tmp_err_info 
    --WHERE inuse = '-'
    order by dt DESC, fg, nfile
    --FOR UPDATE OF inuse
    ;
  //lstrcpy(szDop, "BEGIN WORK");
  //$BEGIN WORK;
  lstrcpy(szDop, "$open curs1 cursor");
  $open curs1;
  vcS.clear();    // теперича здесь стоит чистка, чтоб работало....
  for (INT j=0; ; ++j)
  {
    lstrcpy(szDop, "fetch curs1 cursor");
    $fetch curs1 into :buf;
    if (SQLCODE == 100) break;
    RTrim(buf);
    fprintf(fil, "%03d)\n", j+1);
    fprintf(fil, "buf=>%s<\n", buf);
    fflush(fil);
    string sTemp = buf;
    fprintf(fil, "&vcS=%p (%p)\n", &vcS, vcS);
    vcS.push_back(sTemp);
    fprintf(fil, "%s\n", "push_back(sTemp=buf) Ok!");
    fflush(fil);
    //$UPDATE tmp_err_info 
    //  SET inuse = '+'
    //  WHERE CURRENT OF curs1;
  }
  lstrcpy(szDop, "close curs1 cursor");
  $close curs1;
  lstrcpy(szDop, "free curs1 cursor");
  $free  curs1;
  //lstrcpy(szDop, "COMMIT WORK");
  //$COMMIT WORK;
  fclose(fil);
  return 0;
//-----
$WHENEVER SQLERROR continue;
ERROR_SQL: 
  int mcr = SqlMyInfo("get_ErrInfo", szDop);
  $close curs1;
  $free curs1;
  fclose(fil);
  return mcr;
}
 не успел почистить.... сорри

Автор: JackYF 26.11.2007, 18:41
mantissa, пользуйся кнопками "Код" и "Цитата".
Переформатируй сообщение, читать сложно.

Автор: NiJazz 26.11.2007, 22:24
Цитата
а можно поподробнее... про  разные экземпляры CRT и т.д.

Память выделенная в одной куче, не может быть освобождена в другой. Как вариант, ты инициализируешь вектор какими-то значениями (выделяешь память), к примеру, в exe, а затем передаёшь вектор по ссылке или указателю в функцию DLL и там вызываешь vector::clear, то есть освобождаешь память. У exe и dll разная куча. 
Но в то же время, обычно в DEBUG-версии срабатывает ASSERT при подобных ошибках, а не исключение. Для проверки можешь использовать _CrtIsValidHeapPointer( &my_vector[0] ).

Автор: mantissa 27.11.2007, 16:32
Цитата

Память выделенная в одной куче, не может быть освобождена в другой. Как вариант, ты инициализируешь вектор какими-то значениями (выделяешь память), к примеру, в exe, а затем передаёшь вектор по ссылке или указателю в функцию DLL и там вызываешь vector::clear, то есть освобождаешь память. У exe и dll разная куча. 
Но в то же время, обычно в DEBUG-версии срабатывает ASSERT при подобных ошибках, а не исключение. Для проверки можешь использовать _CrtIsValidHeapPointer( &my_vector[0] ).


ТОЧНО! отладчик застревал в кодах STL на попытке высвободить память.
а ведь я на похожие грабли уже наступал...
СПАСИБО Всем и персональное СПАСИБО NiJazz!

и... все равно появляется парочка вопросов....
1) а как же тогда ListBox (Control) справляется с вставкой и удалением своих строк из разных куч памяти (у него подобных проблем не замечал....)
2) чем чревато то, что объект создан в одной куче, а пользуется памятью чужой? (почему объект не извлекает ссылку на кучу в которой он создан, чтобы забирать память из той же кучи? может это настройки безопасности и их можно где-нибудь выставить в свойствах проекта? )

с ув. BSD

Автор: Fazil6 27.11.2007, 16:53
Цитата(mantissa @  27.11.2007,  16:32 Найти цитируемый пост)
 У exe и dll разная куча.

неверно. У exe и dll одна куча. Проблема кроется когда exe и dll скомпиллированы с разными версиями CRT и когда объект создается в одном модуле(одной версией CRT), а удаляется в другом модуле(другой версией CRT) получаем ошибку

Автор: mantissa 27.11.2007, 18:17
Цитата

неверно. У exe и dll одна куча. Проблема кроется когда exe и dll скомпиллированы с разными версиями CRT и когда объект создается в одном модуле(одной версией CRT), а удаляется в другом модуле(другой версией CRT) получаем ошибку


опаньки.... и как енто проверить или еще лучше где это в MS VC 2003.net посмотреть?

с ув. BSD

Автор: Fazil6 27.11.2007, 19:11
вот так

Автор: mantissa 29.11.2007, 16:37
Приветствую славную братию программистов!

Цитата

вот так

Присоединённый файл ( Кол-во скачиваний: 4 ) 


 в свойствах DLL стояло действительно вместо сингла мульти....
исправил на сингл...
результат тот же :

отладчик указывает на файл "vector"
Код

    iterator insert(iterator _Where, const _Ty& _Val)
        {    // insert _Val at _Where
        size_type _Off = size() == 0 ? 0 : _Where - begin();
        _Insert_n(_Where, (size_type)1, _Val);
        return (begin() + _Off);                                  // << сыпется здесь!
        }



может нужно какие-нибудь действия при присоединении DLL-ки делать?
или не использовать аллокатор по умолчанию в векторе, а назначать какой-нить свой??

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

с ув. BSD

Автор: Fazil6 29.11.2007, 17:19
Цитата(mantissa @  29.11.2007,  16:37 Найти цитируемый пост)
 в свойствах DLL стояло действительно вместо сингла мульти....исправил на сингл...результат тот же :

надо не сингл, НАДО ЧТОБЫ ЭТОТ ПАРАМЕТР БЫЛ ОДИНАКОВЫЙ У ВСЕХ, у ддл и у ехе

.
Цитата(mantissa @  29.11.2007,  16:37 Найти цитируемый пост)
в принципе проблема как бы замаскировалась, и про нее м. забыть, делая любые операции с вектором только "по ту сторону баррикад",но я потратил почти 2 дня, пока не замаскировал проблему эмпирическим путем, а поскольку чудес в программировании не бывает и сейчас срочной работой не грузят, как раз есть время разобраться в этом "чуде"...

чуда здесь нет. Вообще передача между модулями объектов(в том числе и STL) не слишком хорошая идея.  
Цитата(mantissa @  29.11.2007,  16:37 Найти цитируемый пост)
может нужно какие-нибудь действия при присоединении DLL-ки делать?или не использовать аллокатор по умолчанию в векторе, а назначать какой-нить свой??

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


Автор: mantissa 30.11.2007, 17:10
Привет, Fazil6 !

Цитата

надо не сингл, НАДО ЧТОБЫ ЭТОТ ПАРАМЕТР БЫЛ ОДИНАКОВЫЙ У ВСЕХ, у ддл и у ехе


были разные, теперь один... не помогает...
Цитата

использовать в ехе и dll один компиллятор, одной и той же версии с одинаковыми настройками.


дык один он... это вообще один Solution, в который включены 2 проекта (или как там они у него обзываются)....
да и на мой взгляд необходимости в DLL-ке вроде нет.... просто мой предшественник все, что относилось к ESQL/C решил засунуть в DLL

а почему передача параметров в DLL плохая идея и чем ссылка на объект хуже например указателя на строку?

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

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

стоп...
перейдем к конкретному примеру....
мне нужно отобразить содержимое файла, хранящееся в БД в BLOB-е и некоторые значения полей таблиц
рассмотрим варианты...
имеется универсальная функция уже написанная и откомпилированная...
 ты...
      1) просто ее вызовешь с необходимыми параметрами?
      2) скопируешь ее код в свой проект?
      3) напишешь нечто очень похожее, но свое?

как бы ты сам написал эту функцию?
  1) передал бы ей кучу хэндлов на окна и пущай в них сама все вставляет
  2) забрал бы от нее все данные в статические буфера (для BLOB например 1000К или больше) и сам бы повставлял данные
  3) то же но с динамическими буферами (string, vector и т.д.) (кстати вариант там распределять память, а здесь при закрытии окна освобождать - также не проходит по тем же причинам, приходиться выполнять лишние запросы для уточнения размеров)

поделись плиз логикой...

с ув. BSD
 

Автор: Fazil6 30.11.2007, 18:45
Цитата(mantissa @  30.11.2007,  17:10 Найти цитируемый пост)
а почему передача параметров в DLL плохая идея и чем ссылка на объект хуже например указателя на строку?

я говорил о объектах классов. Если у тебя в интерфейсе dll функция, которая в качестве праметра принимает к примеру std::map,  то как ты эту функцию будешь вызывать, скажем, в Delphi ? Причем, не факт даже, что программа компиллируемая С++ компиллятором всегда сможет работать с такой dll, просто потому что использует какую-нибудь другую версию STL, потому что объект std::list создаваемый ею отличается в силу особеностей компиллятора от объекта создаваемого компиллятором dll. Объект класса - это ведь не int какой-нибудь.
Цитата(mantissa @  30.11.2007,  17:10 Найти цитируемый пост)
а функции стараюсь писать универсальные, чтобы действительно эту DLL  м. было другими EXE-шниками использовать

для этой цели нужно использовать переносимые типы в интерфейсе dll. Использование классов резко сужает область применимости такой библиотеки.
Цитата(mantissa @  30.11.2007,  17:10 Найти цитируемый пост)
и на мой взгляд проще передать объект, чтобы некто там напихал в него данные, используя его же методы, вместо того, чтобы пихать в объект методы, которые используют вызовы множества ESQL/C функций

см. выше. 
Для того чтобы напихать в твой объект вызывающий должен знать что это такое и правильно его использовать

насчет задачи твоей... Постановка слишком неконкретная. Впринципе возможны любые из приведенных тобой вариантов. Причем здесь dll? Если уж рассматривать некие варианты, то если есть некая либа, которая инкапсулирует, допустим, работу с БД, то функция чтения значения из BLOB записывала бы результат в некий буфер unsigned char* но это далеко не единственное возможное и приемлемое решение. В каждом конкретном случае я буду решать как удобне. У меня тоже есть проекты, которые содержат модули с интерфейсом использующим STL объекты. 

Автор: NiJazz 30.11.2007, 19:05
Цитата
неверно. У exe и dll одна куча

В данном случае, разная.
Не нужно пытаться исправлять ошибку просто обойдя проблему - выставить одинаковый рантайм. Просто не надо выпускать наружу контейнеры STL и всё тут. Используйте копирование, тогда и проблем не будет.

Добавлено через 3 минуты и 1 секунду
Об отладочной куче можно почитать http://msdn2.microsoft.com/en-us/library/974tc9t1(VS.80).aspx

Автор: mantissa 3.12.2007, 17:49
Привет Все!

Согласен с NiJazz по поводу того, что в 32 битной ОС кучи будут разные, но все равно не понимаю, в чем проблема освободить занимаемую память любой кучи, похоже что для безопасности процессов запрещают насильственными методами (ОС д.б. абсолютно все равно)

и второе... что плохого, что вектор находиться в одной куче и содержит объекты, хранящиеся в другой куче?


Цитата(Fazil6 @  30.11.2007,  18:45 Найти цитируемый пост)
... допустим, работу с БД, то функция чтения значения из BLOB записывала бы результат в некий буфер unsigned char* ...  


кто будет распределять память под буфер???
 с целью оптимизации было бы выгоднее, чтобы модуль сам распределял память (он то знает какого размера BLOB) (заодно бы и сообщения ползунку слала бы о ходе загрузки),  а программа бы в конце высвобождала по этому unsigned char* память. Но так не работает!

кстати отказаться от DLL начинает все больше привлекать меня smile (видно не дорос еще я до них)

с ув. BSD

Автор: mantissa 4.12.2007, 16:11
привет Всем!

более внимательное тестирование показало, что я был не прав, утверждая, что std::string работает без проблем по ссылке в DLL модуле.
НИЧЕГО не работает. Ни один объект динамически перераспределяющий память не будет корректно работать с другим потоком

учту ошибки в будущем. Всем СПАСИБО!

с ув. BSD

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