Форум программистов «Весельчак У»
  *
Добро пожаловать, Гость. Пожалуйста, войдите или зарегистрируйтесь.
Вам не пришло письмо с кодом активации?

  • Рекомендуем проверить настройки временной зоны в вашем профиле (страница "Внешний вид форума", пункт "Часовой пояс:").
  • У нас больше нет рассылок. Если вам приходят письма от наших бывших рассылок mail.ru и subscribe.ru, то знайте, что это не мы рассылаем.
   Начало  
Наши сайты
Помощь Поиск Календарь Почта Войти Регистрация  
 
Страниц: [1]   Вниз
  Печать  
Автор Тема: тест ошибок с распределением памяти  (Прочитано 12543 раз)
0 Пользователей и 5 Гостей смотрят эту тему.
Malaja
Команда клуба

de
Offline Offline
Пол: Женский

« : 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
Команда клуба

ru
Offline Offline
Пол: Мужской

« Ответ #1 : 14-10-2005 15:20 » 

Malaja,

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

Классики и практики рекомендуют пользоваться контейнерами со встроенной проверкой границ массива и автоматическим увеличением/уменьшением занимаемой памяти.  Например, взять тип list<int>
Это дополнительные накладные расходы во время выполнения, зато значительная экономия времени разработчика -- гораздо более ценного ресурса, чем время пользователя.
Записан

UniTesK -- индустриальная технология надежного тестирования.

http://www.unitesk.com/ru/
Malaja
Команда клуба

de
Offline Offline
Пол: Женский

« Ответ #2 : 24-10-2005 10:29 » 

во-первых, извиняюсь за молчание - я подружилась с гриппом, пришлось полежать ;-(
во-вторых - спасибо за ответ. Все ясно, никаких перспектив Ага Коммерческие тулы меня не спасут, как и отца русской демократии, т.к. мне на них никто финансы не выделит, а обычными средствами самозащиты я не обойдусь.
Контейнеры в данном случае применять не могу, т.к. по условию задачи вся программа должна быть написана на чистом C ;-(
Просто я при внесении последних изменений потратила кучу времени (часа 2 как минимум) и нервов (вагон), пока нашла причину сбоя. Вот и задалась вопросом, как бы это проверить на ошибку. А получается, что никак. Только повышенное внимание, помноженное на пару литров крепкого кофе Ага

Еще раз всем спасибо!
Записан

холоднокровней, Маня, Ви не на работе
---------------------------------------
четкое определение сущности бытия:
- А мы в прошлом или в будущем?- спросила Алиса.
- Мы в жопе, - ответил кролик.
- А "жопа" - это настоящее? - спросила Алиса.
- А "жопа" - это у нас символ вечности.
Vadim Mc
Помогающий

ru
Offline Offline
Пол: Мужской

« Ответ #3 : 24-10-2005 12:28 » 

А надо именно на чистом С а не С++? Сейчас в рамках проекта Open Solaris распостраняется бесплатно IDE Sun Studio 10 (С, С++, Fortran 77, gcc- под Silaris 10 b Linux, под солярисом проверял, снимется 60-ти дневный триал - шлют ключ, есть и в Доунлоаде и под Виндовс ) - там включены анализаторы производительности в состав IDE наряду с дебагерами
Записан

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

ru
Offline Offline
Пол: Мужской

« Ответ #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, чтобы узнать, что пошло не так.
Записан

UniTesK -- индустриальная технология надежного тестирования.

http://www.unitesk.com/ru/
Malaja
Команда клуба

de
Offline Offline
Пол: Женский

« Ответ #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
Команда клуба

ru
Offline Offline
Пол: Мужской

« Ответ #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_.

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

UniTesK -- индустриальная технология надежного тестирования.

http://www.unitesk.com/ru/
Malaja
Команда клуба

de
Offline Offline
Пол: Женский

« Ответ #7 : 26-10-2005 08:43 » 

ага, понятно - женская ж логика Ага а она всегда ведет в другом направлении Ага
это нормальные герои всегда идут в обход - а я подумала, что ты имеешь в виду построение аналога вектора из STL, ну и пошла вспоминать детство, как эта фиговинка устроена и работает Ага В результате вспомнила не до конца Ага
То, что ты предложил, значительно проще и легче. Единственное, о чем я думаю - в принципе, эта же логика подошла бы и для работы со строками (char), но там возникают описанные тобой проблемы. Хотя это все равно лучше, чем ничего!

Единственный вопрос - что имеется в виду под "В отладочной сборке с определённым символом DEBUG " ? Ты имеешь в виду, что эта константа должна быть задана при компиляции? Или что?
Записан

холоднокровней, Маня, Ви не на работе
---------------------------------------
четкое определение сущности бытия:
- А мы в прошлом или в будущем?- спросила Алиса.
- Мы в жопе, - ответил кролик.
- А "жопа" - это настоящее? - спросила Алиса.
- А "жопа" - это у нас символ вечности.
npak
Команда клуба

ru
Offline Offline
Пол: Мужской

« Ответ #8 : 27-10-2005 12:39 » 

Единственный вопрос - что имеется в виду под "В отладочной сборке с определённым символом DEBUG " ? Ты имеешь в виду, что эта константа должна быть задана при компиляции? Или что?

Да, я предлагаю делать abort при отладке и молча игнорировать выход за границу массива в релизе. 

Пусть глючит, но не валится -- пользователь, ИМХО, на крах программы реагирует болезненней, чем на ошибки в поведении.  Благодаря тому, что при добавлении / извлечении элементов происходит проверка, в релизе не будет краха из-за ошибок с управлением памятью.  Да, будет неверное поведение, так как get_IntVector возвратит 0, но это не столь фатально, как "Программа выполнила недопустимую операцию и будет закрыта" Улыбаюсь.

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

Наиболее верный способ различать отладочную сборку и релиз -- использовать специальный символ препроцессора.  Например, Visual Studio при сборке Debug конфигурации определяет символ _DEBUG.  Если используется другая среда для сборки проекта, то может использоваться другой символ.  Или можно определить свой собственный.
Записан

UniTesK -- индустриальная технология надежного тестирования.

http://www.unitesk.com/ru/
Malaja
Команда клуба

de
Offline Offline
Пол: Женский

« Ответ #9 : 27-10-2005 14:18 » 

npak,

все ясно, поняла.
Еще раз большое спасибо за помощь!
Записан

холоднокровней, Маня, Ви не на работе
---------------------------------------
четкое определение сущности бытия:
- А мы в прошлом или в будущем?- спросила Алиса.
- Мы в жопе, - ответил кролик.
- А "жопа" - это настоящее? - спросила Алиса.
- А "жопа" - это у нас символ вечности.
npak
Команда клуба

ru
Offline Offline
Пол: Мужской

« Ответ #10 : 27-10-2005 16:52 » new

The pleasure was all mine Улыбаюсь

Приятно помочь хорошему человеку Улыбаюсь
Записан

UniTesK -- индустриальная технология надежного тестирования.

http://www.unitesk.com/ru/
Страниц: [1]   Вверх
  Печать  
 

Powered by SMF 1.1.21 | SMF © 2015, Simple Machines