Зачем нам нужно list_for_each_safe() для удаления узлов в связанном с ядром списке?

Я изучаю, как использовать API-интерфейс связанного списка ядра из list.h.

Я узнал, что мне нужно использовать list_for_each_safe() при удалении узлов с помощью list_del() вместо list_for_each().

Код для list_for_each_safe():

#define list_for_each_safe(pos, n, head) \
    for (pos = (head)->next, n = pos->next; pos != (head); \
        pos = n, n = pos->next)

Код для list_for_each():

    for (pos = (head)->next; pos != (head); pos = pos->next)

Я заметил, что оба они очень похожи, за исключением того, что версия _safe принимает дополнительный аргумент, который будет использоваться как "временное хранилище" (здесь указано list.h).

Я понимаю, когда нужно корректно применять эту функцию, _safe версия для удаления, нормальная версия для доступа, но мне любопытно, как дополнительный аргумент сделал ее "безопасной"?

Рассмотрим следующее, где я удаляю каждый node в связанном списке с помощью list_for_each_safe():

struct kool_list{
    int to;
    struct list_head list;
    int from;
    };

struct kool_list *tmp;
struct list_head *pos, *q;
struct kool_list mylist;

list_for_each_safe(pos, q, &mylist.list){
         tmp= list_entry(pos, struct kool_list, list);
         printf("freeing item to= %d from= %d\n", tmp->to, tmp->from);
         list_del(pos);
         free(tmp);
    }

Как при помощи q можно удалить?

Спасибо за любую помощь!

Ответ 1

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

for (pos = (head)->next; pos != (head); pos = pos->next)

После выполнения тега цикла pos указатель становится недействительным, вызывая выражение приращения: pos = pos->next.

Как и наоборот, безопасный foreach предварительно сохраняет значение pos->next во временной переменной и затем ссылается на последнее вместо разыменования pos:

for (pos = (head)->next, n = pos->next; pos != (head); \
    pos = n, n = pos->next)

Ответ 2

pos = start;
del(pos);
pos = pos->next;

в отличие от

pos = start;
n = pos->next;
del(pos);
pos = n;

если del() свободен() и memset(), pos- > next is undefined