Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Лабораторная работа №4_No_Classes2014

.pdf
Скачиваний:
29
Добавлен:
11.02.2015
Размер:
1.05 Mб
Скачать

Основы создания программ в Си

Page 31 of 38

 

 

 

float GetFiguresP(void** ppFigures, int count)

{// периметр float P = 0;

for(int i = 0; i < count; i++)

{

void* p = ppFigures[i]; // указатель на фигуру

// получаем тип фигуры

Figures kind = *((Figures*)p); switch(kind)

{

case FCyrcle: // круг

P += 2*((Cyrcle*)p)->Rad*3.14; break;

case FRectangle: // прямоугольник

P += 2*(((Rect*)p)->width+((Rect*)p)->len); break;

case FTriangle:

{// автоматическая переменная указателя на треугольник

Triangle* t = (Triangle*)p; //периметр треугольника

P+= t->side_a + t->side_b + t->side_c; break;

}

default:

printf("Error in Сalculation of Perimeter!!! \n"); return 0;

}

}

return P;

}

Использование функций расчета в функции main будет производиться при обработке соответствующих команд «'s' - Calculate Square for figures.» и «‘p' - Calculate Perimeter for figures».

...

switch (key)

{

case 'a': case 'A':

...

break;

Основы создания программ в Си

Page 32 of 38

 

 

 

case 's': case 'S':

if(!realCount) // проверка наличия фигур для расчета

{

printf("No figures were putted for calculation!!!\n"); break;

}

float s = GetFuguresS(ppFigArray,realCount); printf("Squere of figures is: %f", s); break;

case 'p': case 'P':

if(!realCount) // проверка наличия фигур для расчета

{

printf("No figures were putted for calculation!!!\n"); break;

}

float p = GetFiguresP(ppFigArray,realCount); printf("Perimeter of figures is: %d", p); break;

...

Для выбранного индивидуального задания реализуйте методы обработки данных для расчета нужных значений в приложении. Результаты вычислений должны выводиться в функции wmain.

Использование указателя на функцию

Обобщая реализованные части приложения можно заключить следующее:

1.Приложение реализует меню ввода геометрических объектов разного типа (круг, прямоугольник, треугольник) на основе ввода символов с клавиатуры и оператора выбора switch;

2.Ссылки (адреса) динамически созданных объектов фигур хранятся в одном массиве;

3.Реализованы функции обработки данных имеющихся геометрических фигур, например, расчет общей площади фигур и расчет общего периметра фигур.

Операции обработки (функции) геометрических фигур явно имеют одинаковые сигнатуры – на вход они принимают адрес первого элемента массива типа void*, т.е. void**, и фактичееское количество элементов в этом массиве. Типом возвращаемого значения этих функций является float. Прототип этих функций имеет обобщенно следующий вид:

float someOperation (void**, int);

Основы создания программ в Си

Page 33 of 38

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

Для удобства объявления указателя на функцию, целесообразно использовать ключевое слово typedef, которое позволит создать тип данных указателя на функцию:

// объявление метода обратного вызова - указателя на метод typedef float (*callbackMethod)(void**, int );

Представленное объявление определяет тип указателя на функцию “callbackMethod”. Функция на которую будет ссылаться данный указатель должна возвращать значение типа float и принимать 2 аргумента соответственно типов void** и int.

Таким образом, можно модифицировать содержание функции main:

bool executeCalc = false; // флаг необходимости выполнения функции // объявление указателя на функцию

callbackMethod method = NULL; switch (key)

{

case 'a':

...

case 's': case 'S':

if(!realCount)

{

printf("No figures were putted for calculation!!!\n"); break;

}

method = GetFuguresS; printf("Squere of figures is: "); break;

case 'p': case 'P':

if(!realCount)

{

printf("No figures were putted for calculation!!!\n"); break;

}

method = GetFiguresP; printf("Perimeter of figures is: "); break;

Основы создания программ в Си

Page 34 of 38

 

 

 

default:

printf("Bad command. Try again or Quit.\n");

}

// проверка инициализации указателя на функцию if(method != NULL)

{// выполнение выбранного метода для фигур float value = method(ppFigArray,realCount);

printf("%10.2f\n", value);

}

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

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

Наблюдение за утечками памяти в инструментарии VisualStudio 2005

Как возможно предположить разработанное приложение будет иметь утечки памяти, в случае если указатели в массиве ppFigArray будут перезаписываться, без удаления соответствующих объектов. Таким образом, объекты в памяти приложения будут оставаться, занимать нужное пространство даже если приложение уже не имеет ни одного указателя на эти объекты и никогда его уже не будет использовать.

Инструментарий среды разработки VisualStudio 2005 предоставляет возможность показать такие объекты в памяти приложения, которые остаются не уничтоженными после завершения работы приложения. Т.е. фактические утечки памяти. Кроме этого, с использованием специальных отладочных библиотек имеется возможность определить момент создания такого объекта , который в последствии будет «потерян» (своевременно не уничтожен) приложением.

Для активации режима отслеживания утечек памяти необходимо выполнить следующие действия:

1.Подключить нужные заголовочные файлы библиотек окружения разработки (stdlib.h и crtdbg.h).

2.Вызвать функцию сбора данных об утечках памяти _CrtDumpMemoryLeaks в конце выполнения функции wmain.

Реализация этих изменений в коде будет выглядеть следующим образом:

В файле подключения заголовочных файлов stdafx.h:

Основы создания программ в Си

Page 35 of 38

 

 

 

#include <stdio.h> // /* printf, scanf, NULL */ #include <conio.h>

#include <tchar.h>

#include <stdlib.h> // /* malloc, free, rand */ #include <math.h>

#include <crtdbg.h> // определение утечки памяти

В завершении метода wmain вызываем производим вызов функции _CrtDumpMemoryLeaks:

_CrtDumpMemoryLeaks(); // собираем утечки памяти return 0;

}

Теперь если в приложении произвести ввод фигур повторно, т.е. при наличии фигур в массиве вызвать команду «'a' - Fill figures array» и ввести фигуры заново, то ссылки на первоначально расположенные в массиве фигуры будут «потеряны» приложением. Объекты будут занимать память процесса но будут не доступны. При завершении работы приложения будет вызвана функция _CrtDumpMemoryLeaks, которая выведет информацию о наличии таких потерянных объектов в окно Вывод (Output). Точнее сказать будет выведена информация о наличии блоков памяти определенного размера, которые были выделены приложением но не были очищены.

Рис. 4-29 Вывод информации об утечках памяти в инструментальном окружении

VisualStudio 2005

При детальном рассмотрении информации об утечках памяти можно выявить информацию необходимую для определения времени и места создания объекта, который в результате не был уничтожен корректно. Например, следующая информация об утечках показывает:

 

Основы создания программ в Си

Page 36 of 38

 

 

 

 

 

 

Dumping objects ->

 

 

 

 

m:\tmp\lab4_test\lab4_test\lab4_test.cpp(135) : {104} normal block at

 

 

0x00369DE8, 16 bytes long.

 

 

Data: <

> 01 00 00 00 14 00 00 00 CD CD CD CD CD CD CD CD

 

 

m:\tmp\lab4_test\lab4_test\lab4_test.cpp(167) : {103} normal block at

 

 

0x00369D98, 16 bytes long.

 

 

Data: <

> 03 00 00 00 05 00 00 00 08 00 00 00 06 00 00 00

 

 

 

...

 

 

 

 

 

 

 

 

1, Имя файла в котором произошла ошибка работы с динамически распределяемой памятью

(m:\tmp\lab4_test\lab4_test\lab4_test.cpp);

2.Номер строчки в которой произошло распределение памяти (135) – эта информация доступна не во всех случаях.

3.Номер операции по распределению памяти (индекс – {104}), который возможно использовать впоследствии для установки специальной точки останова и выявления момента создания объекта (выделения памяти под объект). Индексы указываются в обратном хронологическом порядке.

4.Адрес по которому располагается объект – фактически это значение указателя на объект который был потерян.

5.Размер распределенной области памяти (16 bytes long)

6.Символьное представление и значения первых байтов в обозначенной области памяти (Data: <

>03 00 00 00 05 00 00 00 08 00 00 00 06 00 00 00).

Таким образом, можно определять утечки памяти в приложении.

Для выбранного индивидуального задания реализуйте контроль за утечками памяти.

Уничтожение динамически созданных объектов

Для корректной очистки памяти достаточно создать метод - RemoveFigures, который будет освобождать память, выделенную для объектов геометрических фигур. В реализации метода также будет необходимо определять тип объекта, на который ссылается указатель, так же как это сделано в функциях расчета. Метод очистки будет принимать в качестве параметров указатель на массив указателей объектов void** и фактическое количество объектов, которые нужно удалить из памяти.

// метод для очистки памяти фигур

void RemoveFigures(void** ppFigs, int count)

{

int counter = 0; // индекс текущего объекта

void* p = ppFigs[counter]; // указатель на текущий объект

Основы создания программ в Си

Page 37 of 38

 

 

 

while(p != NULL)

{

// получаем тип фигуры

Figures kind = *((Figures*)p); switch(kind)

{

case FCyrcle:

free((Cyrcle*)p); // очистка выделенной памяти printf("Cyrcle was removed. \n");

break;

case FRectangle: free( (Rect*)p);

printf("Rect was removed. \n"); break;

case FTriangle:

free( (Triangle*)p); printf("Triangle was removed. \n"); break;

default:

printf("Error in RemoveFigures!!! \n"); return;

}

ppFigs[counter] = NULL; // обнуление указателя в массиве counter++;

if(counter == count) return;

p = ppFigs[counter];

}

}

Вызов метода необходимо разместить в конце функции main и в начале функции AddFigures.

В функции main вызов будет выглядеть следующим образом:

// очистка памяти

RemoveFigures(ppFigArray, realCount);

_CrtDumpMemoryLeaks(); // собираем утечки памяти return 0;

}

Основы создания программ в Си

Page 38 of 38

В методе AddFigures очистка массива фигур будет производиться при условии, что первый указатель в массив0е не равен NULL:

int AddFigures(void** ppFigs)

{

if(ppFigs[0] != NULL) // проверка что первый элемент массива не нулевой

RemoveFigures(ppFigs, 4);

int counter = 0; bool quit = false;

...

Для выбранного индивидуального задания реализуйте функцию для корректного удаления динамически созданных объектов. Используйте функцию в методе заполнения массива динамических объектов.