ProgrammazioneSviluppatore Embedded

Parlami delle sfumature di dichiarazione e utilizzo delle strutture dati dinamiche (ad esempio, liste collegate) nel linguaggio C. A cosa prestare particolare attenzione durante la loro implementazione?

Supera i colloqui con l'assistente IA Hintsage

Risposta.

Le strutture dati dinamiche in C (ad esempio, liste collegate, alberi) vengono solitamente implementate manualmente utilizzando puntatori e allocazione dinamica della memoria (malloc, calloc, free).

Chiavi di attenzione durante l'implementazione:

  • Inizializza sempre i puntatori: i rifiuti nei puntatori non inizializzati portano a perdite di memoria o segfault.
  • Gestisci gli errori di allocazione della memoria: controlla il risultato di malloc/calloc, altrimenti il programma potrebbe lavorare con un puntatore non valido.
  • Libera correttamente la memoria: non dimenticare di chiamare free per ogni struttura allocata, in modo da evitare perdite di memoria.
  • Evita i puntatori pendenti (dopo free azzera il puntatore).

Esempio: creazione e cancellazione di una semplice lista collegata

typedef struct Node { int value; struct Node* next; } Node; Node* create_node(int value) { Node* n = malloc(sizeof(Node)); if (!n) return NULL; n->value = value; n->next = NULL; return n; } void free_list(Node* head) { while (head) { Node* tmp = head; head = head->next; free(tmp); } }

Domanda trabocchetto.

È corretto liberare la memoria dei nodi della lista all'interno di un ciclo utilizzando solo il puntatore corrente?

Non è corretto liberare il nodo corrente senza prima salvare il successivo! Dopo la chiamata a free, la memoria all'indirizzo può essere sovrascritta o restituita al sistema operativo.

L'approccio corretto:

Node* curr = head; while (curr) { Node* next = curr->next; free(curr); curr = next; }

Se non si salva next, si verifica un accesso alla memoria già liberata (e potenzialmente non tua!).


Storia


A causa dell'oblio di liberare l'intera lista (free) in caso di terminazione errata di una delle operazioni, è stata eseguita una prova automatica su 10000 operazioni di aggiunta/rimozione, a causa della quale il consumo di memoria è gradualmente aumentato: il profiler ha mostrato una grande perdita di memoria.


Lo sviluppatore ha mantenuto un puntatore all'ultimo nodo della lista, ma non l'ha azzerato dopo aver rimosso tutti gli elementi, il che ha causato un segfault difficile da identificare in un'altra funzione, facendo riferimento a memoria già liberata.


Nella gestione degli alberi, hanno dimenticato di eliminare ricorsivamente tutti gli "sottobranche", liberando solo il nodo radice. Risultato: a causa della pulizia incompleta, la struttura di memoria è rimasta sporca, portando a crash periodici.