Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
algorithms.doc
Скачиваний:
29
Добавлен:
06.12.2018
Размер:
9.73 Mб
Скачать
      1. Стандартная ссылочная реализация списков

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

struct SList2

{

int value;

struct SList2 *prev, *next;

};

здесь данные будут храниться в члене структуры value, а ссылки, соответственно, на предыдущий/следующий объекты представлены указателями prev и next. Признаком того, что данный элемент является первым в списке, может служить нулевое значение указателя prev, а признаком конца списка может служить нулевое значение указателя next.

Для сокращения имени типа в языке С можно использовать оператор typedef. Принцип работы данного оператора следующий. Если перед определением некоторой переменной с именем NAME написать оператор typedef, то NAME из имени переменной превратится в имя нового типа. Т.о. следующий оператор создает новый тип TList2, который можно использовать вместо типа struct SList2:

typedef struct SList2 TList2;

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

TList2 *head=NULL, *current=NULL;

Признаком пустоты списка служит нулевое значение указателя head. Установка текущего элемента на начало списка сводится к присвоению

current=head;

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

TList2 *InsertData(int value, TList2 **head, TList2 **current)

{

TList2 *New=( TList2 *)malloc(sizeof(TList2));

New->value = value;

if(*head == NULL) //Случай пустого списка

{*current =*head = New; (*head)->next= (*head)->prev=NULL; return New;}

if(*current==NULL) //Случай вставки в начало списка

{

(*current)=New; (*current)->next=*head; (*current)->prev=NULL;

(*head)->prev=New;

(*head)=New;

return New;

}

New->next = (*current)->next; New->prev=*current;

if((*current)->next != NULL) (*current)->next->prev = New;

(*current) ->next = New;

return New;

}

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

current = InsertData( value, &head, &current);

Здесь следует отметить, что в функции могут изменяться значения указателей на головной и текущий элементы списка. Особенностью языка С является то, что параметры передаются в функции исключительно по значению, т.е. в функцию передаются копии переменных. Альтернативой, в других языках программирования (например С++) служит передача параметров по ссылке. При передаче параметров по ссылке реально передается не значение переменной, а ее адрес. При этом внешний вид (синтаксис) как формальных, так и фактических параметров не отличается от случая передачи параметров по значению (фактическими параметрами называются параметры, передаваемые снаружи при вызове функции, а формальными – параметры, встречающиеся в определении функции). Для указания того, что переменная передается по ссылке, в языке С++ используется символ & перед именем переменной в описании параметров функции. Поэтому в языке С++ вышеуказанная функция может выглядеть чуть проще

TList2 *InsertData(int value, TList2 *&head, TList2 *&current)

{

TList2 *New=( TList2 *)malloc(sizeof(TList2));

New->value = value;

if(head == NULL) //Случай пустого списка

{current =head = New; head->next= head->prev=NULL; return New;}

if(current==NULL) //Случай вставки в начало списка

{

current=New; current->next=head; current->prev=NULL;

head->prev=New;

head=New;

return New;

}

New->next = current->next; New->prev=current;

if(current->next != NULL) current->next->prev = New;

current ->next = New;

return New;

}

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

InsertData( value, head, current);

Для полноты описания следует отметить, что в языке С++ структуры можно передавать в функции по значению.

Возможно, в силу невозможности передачи параметров по ссылке, в зыке С нельзя передавать структуры в функции в качестве параметров. Вместо этого, естественно, можно передавать указатели на структуры. Ситуация с массивами выглядит несколько двусмысленно. С одной сторону, массивы, в чистом виде, передаются по ссылке (т.е. при указании массива в качестве параметров функции передается не копия массива а адрес его нулевого элемента), но, с другой стороны, если вспомнить, что массивы, по всем своим свойствам являются постоянными указателями, то все сразу становится ясным: передавая массив в качестве параметра, мы передаем указатель на нулевой элемент массива и, при этом, сам указатель передается по значению.

Для правильного понимания понятия `структура’ следует иметь представление о выравнивании в структурах. Имеется в виду следующее. При определении структуры гарантируется, что первый член структуры будет располагаться по адресу самой структуры и все члены структуры располагаются в памяти в порядке их расположения в структуре. Однако, размер структуры может не совпадать с суммой размеров членов структуры. Это объясняется тем, что, обычно, адреса всех элементов в структуре должны иметь определенную четность – т.е. они должны быть кратны одному из значений 1, 2, 4, 8 и т.д. Это значение определяет выравнивание элементов в структуре (Structure Alignment). Например, если создать структуру

struct SS{char a; int b;};

то, с большой вероятностью, по умолчанию, ее размер будет равен 8 (т.е. sizeof(struct SS) = = 8). Поведение по умолчанию, как правило, можно изменить с помощью соответствующих ключей компилятора. Но это можно сделать не на всех машинах и не на всех компиляторах.

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]