Malaja
|
|
« : 13-10-2005 14:01 » |
|
Господа, с помощью каких функций в данном конкретном случае проверить неверное распределение памяти? void CCheckMemoryLeaks::OnStartMyexample() { char** pStr = NULL; int* pArr = NULL;
pStr = (char**) malloc (2 * sizeof(char*)); if (pStr) { for (int i = 0; i < 2; i++) { pStr[i] = (char*) malloc (15 * sizeof(char)); if (pStr[i]) { strcpy(pStr[i], "test"); } } }
pArr = (int*) malloc(2 * sizeof(int)); if (pArr) { pArr[0] = 1; pArr[1] = 2; pArr[2] = 3; // ERROR !!! }
if (pStr) { for (int i = 0; i < 2; i++) { free (pStr[i]); pStr[i] = NULL; } free (pStr); pStr = NULL; }
if (pArr) { free (pArr); pArr = NULL; } CloseFile(); }
(Эта программка - грубое воспроизведение допущенной мной ошибки, я ее 2 часа искала... Захотелось разобраться, как на уровне debuga такие вещи контролировать, чтобы не бегать за черной кошкой в черной комнате, т.е. не дебагить написанное, а по сообщениям поймать ошибку.) Я посмотрела список функций, но что-то ничего толкового не увидела Видимо, не так смотрела
|
|
|
Записан
|
холоднокровней, Маня, Ви не на работе --------------------------------------- четкое определение сущности бытия: - А мы в прошлом или в будущем?- спросила Алиса. - Мы в жопе, - ответил кролик. - А "жопа" - это настоящее? - спросила Алиса. - А "жопа" - это у нас символ вечности.
|
|
|
npak
|
|
« Ответ #1 : 14-10-2005 15:20 » |
|
Malaja,
есть различные коммерческие тулы, которые могут следить за захватом/освобождением памяти, но здесь проблема не с распределением памяти, а с выходом за границы разрешённой области. Возможно, какие-то инструменты статического анализа кода способны распознать проблему, но я сильно сомневаюсь, что они смогут помочь в общем случае, когда число элементов задаётся переменной.
Классики и практики рекомендуют пользоваться контейнерами со встроенной проверкой границ массива и автоматическим увеличением/уменьшением занимаемой памяти. Например, взять тип list<int> Это дополнительные накладные расходы во время выполнения, зато значительная экономия времени разработчика -- гораздо более ценного ресурса, чем время пользователя.
|
|
|
Записан
|
|
|
|
Malaja
|
|
« Ответ #2 : 24-10-2005 10:29 » |
|
во-первых, извиняюсь за молчание - я подружилась с гриппом, пришлось полежать ;-( во-вторых - спасибо за ответ. Все ясно, никаких перспектив Коммерческие тулы меня не спасут, как и отца русской демократии, т.к. мне на них никто финансы не выделит, а обычными средствами самозащиты я не обойдусь. Контейнеры в данном случае применять не могу, т.к. по условию задачи вся программа должна быть написана на чистом C ;-( Просто я при внесении последних изменений потратила кучу времени (часа 2 как минимум) и нервов (вагон), пока нашла причину сбоя. Вот и задалась вопросом, как бы это проверить на ошибку. А получается, что никак. Только повышенное внимание, помноженное на пару литров крепкого кофе Еще раз всем спасибо!
|
|
|
Записан
|
холоднокровней, Маня, Ви не на работе --------------------------------------- четкое определение сущности бытия: - А мы в прошлом или в будущем?- спросила Алиса. - Мы в жопе, - ответил кролик. - А "жопа" - это настоящее? - спросила Алиса. - А "жопа" - это у нас символ вечности.
|
|
|
Vadim Mc
|
|
« Ответ #3 : 24-10-2005 12:28 » |
|
А надо именно на чистом С а не С++? Сейчас в рамках проекта Open Solaris распостраняется бесплатно IDE Sun Studio 10 (С, С++, Fortran 77, gcc- под Silaris 10 b Linux, под солярисом проверял, снимется 60-ти дневный триал - шлют ключ, есть и в Доунлоаде и под Виндовс ) - там включены анализаторы производительности в состав IDE наряду с дебагерами
|
|
|
Записан
|
"Теория теорией, но сточки зрения инженера-практика никакая, даже самая элегантная методология, предлагаемая учеными, не стоит и ломаного гроша, если она не помогает в построении реальных, работающих систем." @ Гради Буч
|
|
|
npak
|
|
« Ответ #4 : 24-10-2005 12:33 » |
|
во-первых, извиняюсь за молчание - я подружилась с гриппом, пришлось полежать ;-(
Надеемся, что грипп не птичий, тьфу-тьфу Контейнеры в данном случае применять не могу, т.к. по условию задачи вся программа должна быть написана на чистом C ;-(
На Си это сделать не сложно, проблема будет только в том, что для каждого типа надо будет писать свой контейнер, нет в Си шаблонов Заголовочный файл struct s_int_vector; // Вектор целых чисел typedef struct s_int_vector IntVector;
// Создать вектор заданного размера, элемены инициализируются нулями IntVector * new_IntVector(size_t size); // Удалить вектор и все элементы void release_IntVector(IntVector * v);
// сохранить заданное значение в заданное место в векторе // v[position] = value; void put_IntVector(IntVector * v, size_t position, int value); // Получить значение из заданной позиции // x = v[position]; int get_Vector(Vector * v, size_t position);
В реализации в том случае когда position больше чем size, делать abort, чтобы узнать, что пошло не так.
|
|
|
Записан
|
|
|
|
Malaja
|
|
« Ответ #5 : 25-10-2005 11:09 » |
|
npak, поняла, опять все своими шаловливыми ручками Так, это я "почти" сделала - у меня возникла нехорошая проблема - работает все, кроме очищения контейнера ,-( А что не так - никак не пойму : struct s_int_vector { int m_nValInt; size_t m_nSize; s_int_vector* m_pPos; };
void release_IntVector(IntVector * v) { size_t nCount = 0; int i = 0; IntVector* vCur = NULL; // находим количество элементов в векторе //seekLastElement(v, &nCount);
for (i = nCount - 1; i >= 0; i--) { if (v[i].m_pPos) { free (v[i].m_pPos); } }
free (v); }
при вызове free (v .m_pPos) возникает проблема, хотя элемент существует...
|
|
|
Записан
|
холоднокровней, Маня, Ви не на работе --------------------------------------- четкое определение сущности бытия: - А мы в прошлом или в будущем?- спросила Алиса. - Мы в жопе, - ответил кролик. - А "жопа" - это настоящее? - спросила Алиса. - А "жопа" - это у нас символ вечности.
|
|
|
npak
|
|
« Ответ #6 : 25-10-2005 16:50 » |
|
Хм... Это не совсем та реализация, которую бы сделал я Вот, посмотри struct s_int_vector { size_t m_size; int * m_array; };
IntVector * new_IntVector(size_t size) { if (size == 0) { return NULL; } else { IntVector * result; int * array = calloc(size, sizeof(int)); if (array == NULL) return NULL; result = malloc(sizeof(*result)); if (result == NULL) { free(array); return NULL; } result->m_size = size; result->m_array = array; return result; } }
void release_IntVector(IntVector * v) { free(v->m_array); free(v); }
size_t size_IntVector(IntVector * v) { return v->m_size; }
void put_IntVector(IntVector * v, size_t position, int value) { if (position < v->m_size) { v->m_array[position] = value; } else { #ifdef DEBUG abort(); #endif } }
int get_IntVector(IntVector * v, size_t position) { if (position < v->m_size) { return v->m_array[position]; } else { #ifdef DEBUG abort(); #endif return 0; } }
Вот как им пользоваться: void print_IntVector(IntVector * v, const char * vectorName) { size_t i; for (i = 0; i < size_IntVector(v); ++ i) { printf("%s[%d] == %d\n", vectorName, i, get_IntVector(v, i)); } }
int main(int argc, char ** argv) { size_t pos; IntVector * v = new_IntVector(10); print_IntVector(v, "v"); for (pos = 0; pos < size_IntVector(v); ++ pos) { put_IntVector(v, pos, 5 - (int)pos); } print_IntVector(v, "v");
put_IntVector(v, 10, 1); return 0; }
Обрати внимание на put_IntVector(v, 10, 1); -- выход за границу массива. В отладочной сборке с определённым символом DEBUG сработает abort. Программа навернётся, запустится отладчик, можно будет узнать, что и где сломалось. Предложенное решение хорошо работает для целочисленных типов и структур без указателей. Для чистого Си можно на основе предложенных функций сделать макросы, которые будут автоматически генерировать реализации конструктора и деструктора (new_ и release_), get_, put_ и size_. Если надо в контейнере хранить указатели, то жизнь значительно усложнится, так как надо будет каким-то образом гарантировать, что память, на которую указывает хранимый указатель, не будет освобождена или затёрта всё то время, которое хранится указатель.
|
|
|
Записан
|
|
|
|
Malaja
|
|
« Ответ #7 : 26-10-2005 08:43 » |
|
ага, понятно - женская ж логика а она всегда ведет в другом направлении это нормальные герои всегда идут в обход - а я подумала, что ты имеешь в виду построение аналога вектора из STL, ну и пошла вспоминать детство, как эта фиговинка устроена и работает В результате вспомнила не до конца То, что ты предложил, значительно проще и легче. Единственное, о чем я думаю - в принципе, эта же логика подошла бы и для работы со строками (char), но там возникают описанные тобой проблемы. Хотя это все равно лучше, чем ничего! Единственный вопрос - что имеется в виду под "В отладочной сборке с определённым символом DEBUG " ? Ты имеешь в виду, что эта константа должна быть задана при компиляции? Или что?
|
|
|
Записан
|
холоднокровней, Маня, Ви не на работе --------------------------------------- четкое определение сущности бытия: - А мы в прошлом или в будущем?- спросила Алиса. - Мы в жопе, - ответил кролик. - А "жопа" - это настоящее? - спросила Алиса. - А "жопа" - это у нас символ вечности.
|
|
|
npak
|
|
« Ответ #8 : 27-10-2005 12:39 » |
|
Единственный вопрос - что имеется в виду под "В отладочной сборке с определённым символом DEBUG " ? Ты имеешь в виду, что эта константа должна быть задана при компиляции? Или что?
Да, я предлагаю делать abort при отладке и молча игнорировать выход за границу массива в релизе. Пусть глючит, но не валится -- пользователь, ИМХО, на крах программы реагирует болезненней, чем на ошибки в поведении. Благодаря тому, что при добавлении / извлечении элементов происходит проверка, в релизе не будет краха из-за ошибок с управлением памятью. Да, будет неверное поведение, так как get_IntVector возвратит 0, но это не столь фатально, как "Программа выполнила недопустимую операцию и будет закрыта" . с другой стороны, на этапе тестирования и отладки необходимо, чтобы ошибка проявлялась как можно раньше. Именно для этого я предлагаю вставить abort -- аварийное завершение с запуском отладчика. Можно будет посмотреть стек и попытаться определить, что привело к краху. Наиболее верный способ различать отладочную сборку и релиз -- использовать специальный символ препроцессора. Например, Visual Studio при сборке Debug конфигурации определяет символ _DEBUG. Если используется другая среда для сборки проекта, то может использоваться другой символ. Или можно определить свой собственный.
|
|
|
Записан
|
|
|
|
Malaja
|
|
« Ответ #9 : 27-10-2005 14:18 » |
|
npak,
все ясно, поняла. Еще раз большое спасибо за помощь!
|
|
|
Записан
|
холоднокровней, Маня, Ви не на работе --------------------------------------- четкое определение сущности бытия: - А мы в прошлом или в будущем?- спросила Алиса. - Мы в жопе, - ответил кролик. - А "жопа" - это настоящее? - спросила Алиса. - А "жопа" - это у нас символ вечности.
|
|
|
npak
|
|
« Ответ #10 : 27-10-2005 16:52 » |
|
The pleasure was all mine Приятно помочь хорошему человеку
|
|
|
Записан
|
|
|
|
|