Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
ЯП - ПОИТ (Бахтизин) часть 1 редакт.doc
Скачиваний:
0
Добавлен:
07.01.2020
Размер:
1.76 Mб
Скачать

8.10. Перечисления

Перечисляемый тип представляет собой упорядоченное множество элементов, доступ к которым осуществляется по имени. Этот тип позволяет улучшить читаемость текста программы.

Синтаксис для задания данных переменных подобен синтаксису объявления структурной переменной и в общем виде выглядит так:

enum имя_типа_перечисл {список_перечисл} список_переменных;

enum month{Jan,Feb,Mar,...}; // объявляется перечисление

// с именем month, в фигурных

// скобках – возможные

// значения данной переменной

enum month m1; // m1 объявляется в качестве

// переменной типа month

m1 = Jan; // m1 присваивается Jan

Элементы списка, указанные в шаблоне, представляют собой константы, которые обозначаются идентификатором. Им соответствуют неявно значения, начиная с 0, с последующим увеличением на 1. Выражение в шаблоне можно проинициализировать, т.е. назначить отдельному элементу перечисления явно какое-либо значение.

Число байт, отводимое под переменную перечисления, зависит от количества и значений констант в шаблоне.

Если все значения констант могут быть представлены значениями типа unsigned char, то под переменную m1 отведется 1 байт, если не хватает – 2 байта.

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

if ((m1 < Mar) || (m1 > Nov))

printf(“Winter”);

else

{

if (m1 < Jun)

printf(“Spring”);

else

{

if (m1 < Sep)

printf(“Summer”);

else

printf(“Autumn”);

}

}

9. Динамические структуры данных

9.1. Общие сведения

В этой главе вводится понятие динамических структур данных. Это такие структуры данных, которые не являются строго ограниченными по размеру, как, например, массивы или структуры, а увеличиваются или уменьшаются в процессе выполнения программы. Примером таких структур являются связные списки, стеки, очереди, графы, деревья. Все они будут рассмотрены подробнее далее.

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

В Си для этого существуют функции calloc, malloc и free.

Функции malloc и calloc служат для выделения памяти в динамической куче, а free – для ее освобождения.

9.2. Связные списки

9.2.1. Односвязные списки

Список – это линейная динамическая структура данных, число элементов которой может изменяться по мере их добавления и/или удаления.

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

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

В зависимости от организации связи между узлами списка различают его различные виды.

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

На рис. 9.1 приведен пример односвязного направленного списка. Узел данного списка состоит из поля данных element типа int и указателя на следующий элемент NextPtr, указатель на первый элемент – HeadPtr.

Структура, описывающая создаваемый список:

struct spisok

{

int element;

struct spisok *nextPtr;

};

Рис. 9.1. Односвязный список

Если список пуст, то указатель на список (headPtr) будет содержать NULL, и в этом случае список попросту будет заполняться в том порядке, в котором будут вводиться данные. Ниже приведена программа по созданию данного списка.

// Программа, демонстрирующая создание односвязного списка

#include <stdio.h>

#include <conio.h>

#include <stdlib.h>

// структура, описывающая создаваемый список

struct spisok

{

int element;

struct spisok *nextPtr;

};

typedef spisok Spisok;

typedef Spisok *SpisokPtr;

void createSpisok(SpisokPtr *firstPtr); // описание прототипа

// функции создания

void main()

{

clrscr();

SpisokPtr headPtr = NULL;

int a;

printf(“Vvedite elementi:\n”);

createSpisok(&headPtr);

}

// функция создания односвязного списка

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

void createSpisok(SpisokPtr *firstPtr)

{

SpisokPtr newPtr;

int value;

scanf(“%d”, &value);

// выделение памяти для нового элемента списка

newPtr = (Spisok *) malloc(sizeof(Spisok));

if ((newPtr != NULL) && (value != 1))

{

newPtr->element = value;

newPtr->nextPtr = *firstPtr; // список сдвигается на один

// элемент, путем

*firstPtr = newPtr; // вставки на первое

// место нового элемента

// рекурсивный вызов функции создания

createSpisok(&((*firstPtr)->nextPtr));

}

else

printf(“Error!”);

}

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

Рис. 9.2. Вставка элемента в список

// Функция вставки элемента в список

void insertElement(SpisokPtr *firstPtr, int value)

{

SpisokPtr newPtr, previousPtr, workPtr;

newPtr = (Spisok *) malloc(sizeof(Spisok));

if (newPtr != NULL)

{

newPtr->element = value;

newPtr->nextPtr = NULL;

workPtr = *firstPtr; // некоторому рабочему указателю

// присваивается значение указателя

// на первый элемент списка

previousPtr = NULL; // значение предыдущего указателя

// равно NULL

while (workPtr != NULL && value < workPtr->element)

{

previousPtr = workPtr; // происходит продвижение

// по списку

workPtr = workPtr->nextPtr;

}

previousPtr->nextPtr = newPtr; // вставка нового элемента

// в список

newPtr->nextPtr = workPtr;

}

else

printf("Error!");

}

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

// Функция удаления элемента из списка

void deleteElement(SpisokPtr *firstPtr, int value)

{

SpisokPtr newPtr, predPtr, workPtr;

if (*firstPtr != NULL)

{

workPtr = *firstPtr;

predPtr = NULL;

//пока не конец списка или не найден искомый элемент

while (workPtr != NULL && value < workPtr->element)

{

predPtr = workPtr; //перемещение по списку

workPtr = workPtr->nextPtr;

}

if (predPtr != NULL)

{

// если удаляемый элемент находится не в начале списка

predPtr->nextPtr = workPtr->nextPtr;

free(workPtr);

}

else

{

predPtr = *firstPtr; // если в начале

*firstPtr = (*firstPtr)->nextPtr;

free(predPtr);

}

}

else

printf("Error!");

}