Тот факт, что вы приняли ответ Серга, указывает на то, что с кодом создания вашего узла что-то не так. Ваш комментарий к этому ответу подтверждает это.
Я добавляю это как новый ответ, потому что изменения в вопросе существенно меняют его.
Код связанного списка должен выглядеть следующим образом:
var
Head: PNode=nil;
//this may be a global variable, or better, a field in a class,
//in which case it would be initialized to nil on creation
function AddNode(var Head: PNode): PNode;
begin
New(Result);
Result.Next := Head;
Head := Result;
end;
Обратите внимание, что мы добавляем узел в начало списка. Нам не нужно инициализировать Next
до nil
где-либо, потому что мы всегда назначаем указатель другого узла на Next
. Это правило важно.
Я написал это как функцию, которая возвращает новый узел. Поскольку новый узел всегда добавляется в заголовке, это несколько избыточно. Поскольку вы можете игнорировать возвращаемые значения функции, это не причинит никакого вреда.
Иногда вам может потребоваться инициализировать содержимое узла при добавлении новых узлов. Например:
function AddNode(var Head: PNode; const Caption: string): PNode;
begin
New(Result);
Result.Caption := Caption;
Result.Next := Head;
Head := Result;
end;
Я очень предпочитаю такой подход. Всегда убедитесь, что ваши поля инициализированы. Если для вас подходит нулевая инициализация, вы можете использовать AllocMem
для создания вашего узла.
Вот более конкретный пример использования такого метода:
type
PNode = ^TNode;
TNode = record
Caption: string;
Next: PNode;
end;
procedure PopulateList(Items: TStrings);
var
Item: string;
begin
for Item in Items do
AddNode(Head, Item);
end;
Чтобы уничтожить список, код работает так:
procedure DestroyList(var Head: PNode);
var
Next: PNode;
begin
while Assigned(Head) do begin
Next := Head.Next;
Dispose(Head);
Head := Next;
end;
end;
Вы можете ясно видеть, что этот метод может возвращаться только тогда, когда Head
равен nil
.
Если вы инкапсулируете свой связанный список в классе, тогда вы можете сделать указатель заголовка членом класса и избежать необходимости его передачи.
Главное, что я хотел бы отметить, - это то, что код ручного распределения памяти деликатный. Легко сделать небольшие ошибки в деталях. В подобных ситуациях выгодно помещать тонкий код во вспомогательные функции или методы, поэтому вам нужно написать его только один раз. Связанные списки являются отличным примером проблемы, которую нравится решать с помощью дженериков. Вы можете написать код управления памятью один раз и повторно использовать его для всех типов узлов разных типов.