Реализация списка фактически создает кольцо.Заголовок списка - это фиктивный элемент, который указывает next
на первый элемент и prev
на последний элемент.(Изначально оба указывают на сам заголовок списка.) Добавление элемента в хвосте фактически реализовано как добавление его «перед заголовком списка».При зацикливании этого кольца голова помечается отдельным указателем, указывающим на него.Нет другого способа отличить его от других элементов списка.
Цикл for
в list_for_each_entry
имеет сравнение с указателем head
в качестве условия цикла, поэтому он остановится, когдаон снова достигнет объекта, предоставленного в качестве заголовка списка.
/**
* list_for_each_entry - iterate over list of given type
* @pos: the type * to use as a loop cursor.
* @head: the head for your list.
* @member: the name of the list_head within the struct.
*/
#define list_for_each_entry(pos, head, member) \
for (pos = list_first_entry(head, typeof(*pos), member); \
&pos->member != (head); \
pos = list_next_entry(pos, member))
Оба макроса list_first_entry
и list_next_entry
возвращают указатель на пользовательскую структуру, которая должна содержать struct list_head
, используяmacro container_of
.
Если вы передадите reports.next
вместо &reports
на list_for_each_entry()
, он примет это как элемент заголовка фиктивного списка и рассмотрит все остальные элементы в кольце как реальные записи списка.
Ваш код печатает мусор для элемента позади элемента tail, потому что это чистый struct list_head
, который не встроен в struct struct_report
, поэтому макрос list_next_entry
возвращает указатель на память перед вашим struct list_head reports
in main()
, что является неопределенным поведением.
Если ваша программа не падает, вы получите такой же мусор после elt4
, если передадите, например, reports.next->next
.В этом случае я бы ожидал вывод, как это:
============> Next retreport: elt3
============> Next retreport: elt4
============> Next retreport: <garbage>
============> Next retreport: elt1