En esta entrevista de Slashdot, se cita a Linus Torvalds diciendo:
He visto a demasiadas personas que eliminan una entrada de la lista enlazada individualmente haciendo un seguimiento de la entrada "anterior", y luego eliminan la entrada, haciendo algo como
if (anterior) anterior-
> siguiente = entrada-> siguiente;
else
list_head = entry-> next;y cada vez que veo un código así, solo digo "Esta persona no entiende los punteros". Y lamentablemente es bastante común.
Las personas que entienden los punteros simplemente usan un "puntero al puntero de entrada" e inicializan eso con la dirección de list_head. Y luego, a medida que atraviesan la lista, pueden eliminar la entrada sin usar ninguna condición, simplemente haciendo un "* pp = entrada-> siguiente".
Como desarrollador de PHP, no he tocado punteros desde Introducción a C en la universidad hace una década. Sin embargo, siento que este es un tipo de situación con la que al menos debería estar familiarizado. ¿De qué está hablando Linus? Para ser honesto, si me pidieran implementar una lista vinculada y eliminar un elemento, la forma "incorrecta" anterior es la forma en que lo haría. ¿Qué necesito saber para codificar como Linus dice mejor?
Estoy preguntando aquí en lugar de sobre Stack Overflow, ya que en realidad no tengo problemas con esto en el código de producción.
prev
, en lugar de almacenar el nodo completo, puede almacenar la ubicación deprev.next
, ya que eso es lo único que le interesa. Un puntero a un puntero. Y si haces eso, evitas lo tontoif
, ya que ahora no tienes el extraño caso delist_head
ser un puntero desde fuera de un nodo. El puntero al encabezado de la lista es semánticamente igual que el puntero al siguiente nodo.Respuestas:
Usando mis habilidades de pintura L331 MS:
La solución original es apuntar a los nodos vía
curr
. En ese caso, verifica si el siguiente nodocurr
tiene el valor de eliminación y, de ser así, restablece el puntero delcurr
nodonext
. El problema es que no hay ningún nodo que apunte al encabezado de la lista. Eso significa que tiene que haber un caso especial para verificarlo.Lo que propone Linus (probablemente) en su lugar no es guardar el puntero en el nodo examinado actual, sino más bien el puntero al puntero en el nodo actual (etiquetado
pp
). La operación es la misma: si elpp
puntero apunta a un nodo con el valor correcto, reinicia elpp
puntero.La diferencia viene al comienzo de la lista. Si bien no hay ningún nodo que apunte al encabezado de la lista, de hecho, hay un puntero al encabezado de la lista. Y es lo mismo un puntero a un nodo, tal como otro
next
puntero de nodos . Por lo tanto, no hay necesidad de una cláusula especial para el comienzo de la lista.fuente
list_head
apuntar a algo con unnext
nodo que apunte al primer elemento de datos real (y se hayaprev
inicializado en ese mismo objeto ficticio). No me gusta la idea deprev
señalar algo de diferente tipo, ya que estos trucos pueden introducir comportamientos indefinidos mediante alias y hacer que el código sea innecesariamente sensible al diseño de la estructura.prev
apuntar a Nodos, apunta a punteros. Siempre apunta a algo del mismo tipo, es decir, un puntero a un nodo. Su sugerencia es esencialmente la misma:prev
apunte a algo "con unnext
nodo". Si descarta el shell, solo obtiene ellist_head
puntero inicial . O, en otras palabras, algo que se define solo al tener un puntero al siguiente nodo, es semánticamente equivalente a un puntero a un nodo.list_head
ynext
tendrá el mismo "tipo" de puntero. No es un problema en C, quizás, pero quizás problemático en C ++.Ejemplo de código
Si necesitamos algo de lógica para destruir el nodo, entonces podemos agregar una línea de código al final:
fuente