2012-02-09 9 views
11

Sto imparando come utilizzare l'API della lista collegata del kernel da list.h.Perché è necessario list_for_each_safe() per eliminare i nodi nell'elenco dei nodi del kernel?

Ho appreso che ho bisogno di utilizzare list_for_each_safe() quando si eliminano i nodi con invece di utilizzare list_for_each().

Codice per 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) 

Codice per list_for_each():

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

ho notato che entrambi sono molto simili, tranne che la versione _safe prende un argomento in più per essere utilizzato come 'deposito temporaneo' (indicato qui, list.h).

Ho capito quando applicare correttamente la funzione, versione _safe per l'eliminazione, versione normale per l'accesso, ma sono curioso di sapere come l'argomento extra lo ha reso "sicuro"?

Si consideri il seguente, dove sto cancellando ogni nodo in una lista collegata con 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); 
    } 

Come si fa a dare q aiuto nella eliminazione?

Grazie per qualsiasi aiuto!

+0

ho presa, mai pensato che questo dritto in avanti, Grazie ! –

+0

q dovrebbe essere stato nominato in un modo migliore .. qualcosa come pos_next. –

risposta

20

Questo è necessario perché list_del modifica internamente il valore di pos campi. Nel tuo esempio il corpo del loop libera anche la memoria occupata da pos. Supponiamo che si usa la versione non sicura del ciclo:

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

Dopo l'esecuzione del corpo del ciclo pos puntatore non è più valido rompere l'espressione minimo: pos = pos->next.

Come opposto, il foreach di sicurezza pre-salva il valore di pos->next in una variabile temporanea e quindi fa riferimento a questi ultimi, invece di dereferenziazione pos:

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

anziché

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

se del() è libero() e memset(), pos-> è prossimo indefinito