Реализация списка generi c хранит значения элементов фиксированного размера, заданные для list_new()
, вместе с указателем на дополнительную функцию обратного вызова для освобождения значения элемента при разрушении узла списка. Размер элемента сохраняется в элементе elementSize
списка, а указатель функции обратного вызова (который равен NULL
, если не требуется вызывать функцию обратного вызова) сохраняется в элементе freeFn
.
list_append()
и list_prepend()
добавляет новый узел в список, и ему предоставляется указатель на значение элемента, которое будет добавлено в список. Они выделяют новый узел списка и выделяют блок памяти, на который указывает node->data
, для хранения значения элемента, которое копируется в выделенный блок памяти с помощью memcpy()
.
list_destroy()
освобождает все узлы на список, вызывающий функцию обратного вызова freeFn
(если имеется) с указателем data
для каждого значения элемента. Это зависит от функции обратного вызова, чтобы определить, что нужно сделать со значением элемента, чтобы освободить его. Функция обратного вызова не освобождает память, указанную указателем data
. Он использует значение, на которое указывает указатель data
. list_destroy()
освобождает блок памяти node->data
, который был выделен list_append()
или list_prepend()
.
В целочисленном примере значение, которое будет сохранено, является просто значением типа int
. elementSize
участник списка sizeof(int)
. Указатель на функцию обратного вызова freeFn
равен NULL
, потому что ничего не нужно делать для освобождения int
- это просто число. В примере вызывается list_append()
с указателем на значение int
для сохранения. list_append()
выделяет блок размером sizeof(int)
, на который указывает node->data
, и копирует в него значение int
.
В примере строки значение, которое будет сохранено, представляет собой char *
, который указывает на динамически размещенный строковый буфер, выделенный strdup()
. Этот буфер должен быть освобожден при уничтожении узла списка. Следовательно, строковый пример предоставляет указатель на функцию free_string
, чтобы освободить строковый буфер, выделенный strdup()
. В примере вызывается list_append()
с указателем на значение char *
для сохранения. list_append()
выделяет блок размером sizeof(char *)
, на который указывает node->data
, и копирует в него значение char *
. (Примечание: оно копирует само значение указателя char *
, а не содержимое строки. Содержимое строки было скопировано strdup()
.)
Функция обратного вызова free_string()
получает шаблон c void *data
указатель на копию значения, на которое было указано при вызове list_append()
. Он указывает на значение char *
, поэтому первое, что ему нужно сделать, - это преобразовать указатель generi c void *
в указатель char **
. Это делается с помощью оператора приведения типа: (char **)data
. Затем он разыменовывает char **
, чтобы получить char *
значение: *(char **)data
. Это значение char *
указывает на память, которая была выделена strdup()
и которую необходимо освободить: free(*(char **)data);
.
Если это упрощает просмотр того, что делает free_string()
, оно может иметь определены некоторые локальные переменные для хранения значений промежуточного указателя, например:
void free_string(void *data)
{
char **p = data; /* convert pointer type (same as: char **p = (char **)data;) */
char *strbuf = *p; /* dereference the pointer (same as char *strbuf = p[0];) */
free(strbuf); /* free the buffer pointed to by the dereferenced pointer */
}
Как видите, free_string
не освобождает буфер, на который указывает указатель data
. Он просто освобождает буфер, на который указывает значение char *
, на которое указывает указатель data
.