|
Con este artículo quiero comenzar una serie de aportes filantrópicos (sin ánimo de lucro) a otros compañeros samurais de la informática, que en algún momento de su esforzada vida académica deben enfrentar cierto tipo de problemas típicos. En C, una lista enlazada simple es una estructura de datos compuesta por una serie de nodos, unidos a través de punteros. La lista se denomina “simple” porque cada nodo apunta al siguiente, pero no al que lo precede, por lo que sólo se puede recorrer la estructura de datos de manera unidireccional. Es evidente que el mayor bemol de este tipo de listas es que hay que pasar por los K primeros nodos para llegar al K-ésimo. Una buena opción es hacerle una “cabecera” a la lista, donde guardar cosas como el número de elementos y un puntero al último nodo; así se evitan recorridos innecesarios al agregar nuevos nodos y al averiguar el largo de la lista. Un esquema clarifica la situación:  Lista enlazada simple
La cabecera guarda 3 datos: - cantidad de nodos
- puntero al primer nodo
- puntero al ultimo nodo
Cada nodo guarda: - puntero al dato guardado
- puntero al siguiente nodo
Entonces, son necesarias dos estructuras: una que represente a la cabecera (ListHeader) y otra que represente al nodo (ListNode). typedef struct ListNode { void* data; struct ListNode* next; }ListNode; typedef struct ListHeader { int size; ListNode* first,*last; }ListHeader; typedef ListHeader* List; -
%0Atypedef%20struct%20ListNode%0A%7B%0A%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%20void%2A%20data%3B%0A%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%20struct%20ListNode%2A%20next%3B%0A%7DListNode%3B%0A%0Atypedef%20struct%20ListHeader%0A%7B%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%20%0A%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%20int%20size%3B%0A%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%20ListNode%2A%20first%2C%2Alast%3B%0A%7DListHeader%3B%0A%0Atypedef%20ListHeader%2A%20List%3B%0A%0A El último typedef tiene por objetivo mejorar la legibilidad del código y esconder a ojos del usuario la estructura interna de la lista. La idea es que el usuario en todo momento sólo interactúe con una variable de tipo “List”; nunca debería tener que manejar las estructuras ListHeader o ListNode directamente; recuerda uno de los dogmas del evangelio de Igeniería/Arquitectura de Software: Alta cohesión y bajo acoplamiento. Las operaciones más comunes sobre una lista, que implementaremos mediante funciones, son: - crear una lista
- agregar un dato al final
- insertar un dato en cierta posición
- borrar un dato
- ver cuantos datos tiene la lista
- recuperar el primer dato
- recuperar el ultimo dato
- recuperar el i-ésimo dato
- verificar si existe un dato
- borrar la lista
Con esto “las hacemos todas” . Sin embargo, nuestros sentidos ninja nos indican que recorrer la lista haciendo sucesivas llamas a “recuperar el i-ésimo dato” sería muy ineficiente... “eso no es bueno” (pues pasaríamos una y otra vez los primeros nodos de la lista). Lo deseable en esta situación sería tener una especie de puntero con el cual ir avanzando sin perder la posición actual entre cada “salto”. Acá es donde entra en escena el patrón Iterator. Un iterador es una variable que nos permitirá realizar un recorrido eficiente, manteniendo oculta la estructura interna de la lista al usuario. Entonces, agregamos un nuevo tipo (ListIterator) y unas cuantas funciones más: typedef ListNode* ListIterator; -
%0Atypedef%20ListNode%2A%20ListIterator%3B%0A%0A - Crear iterador
- recuperar dato al que apunta el iterador
- avanzar el iterador
Otra manera común de operar sobre todos los elementos de una lista es evaluar alguna función (que generalmente se denomina callback) con cada dato. La ventaja de este método es que el usuario sólo se preocupa de hacer la funcion callback y la lista es la que realiza el recorrido. Agregamos un nuevo tipo (puntero a callback) y una nueva función: typedef void (*ListCallback)(void* data,void* userData); -
%0Atypedef%20void%20%28%2AListCallback%29%28void%2A%20data%2Cvoid%2A%20userData%29%3B%0A%0A - evaluar una función con cada dato
Ahora, hay que implementar cada operación . Primero, haré una función que simplemente construye un nodo y lo inicializa con cierto valor: ListNode* newNode(void* data) { ListNode* node=malloc(sizeof(ListNode)); node->data=data; node->next=NULL; return node; } -
%0AListNode%2A%20newNode%28void%2A%20data%29%0A%7B%0A%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%20ListNode%2A%20node%3Dmalloc%28sizeof%28ListNode%29%29%3B%0A%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%20node-%3Edata%3Ddata%3B%0A%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%20node-%3Enext%3DNULL%3B%0A%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%20return%20node%3B%0A%7D%0A Crear una listaUna lista vacía en nuestro caso es simplemente una estructura ListHeader con el campo size en 0 y los punteros first y last en NULL. List ls_create() { ListHeader* list=malloc(sizeof(ListHeader)); list->size=0; list->first=NULL; list->last=NULL; return list; } -
%0AList%20ls_create%28%29%0A%7B%0A%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%20ListHeader%2A%20list%3Dmalloc%28sizeof%28ListHeader%29%29%3B%0A%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%20list-%3Esize%3D0%3B%0A%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%20list-%3Efirst%3DNULL%3B%0A%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%20list-%3Elast%3DNULL%3B%0A%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%20return%20list%3B%0A%7D%0A Agregar un dato al finalCreamos una estructura ListNode y la incializamos. Debemos tener cuidado de tratar correctamente el caso en que la lista está vacía, situación en la que hacemos que first y last apunten al nuevo nodo. Si la lista ya tiene elementos, hacemos que el ultimo nodo actual apunte al nuevo, luego dejamos al nuevo nodo como último haciendo que last lo apunte. void ls_append(List list,void* data) { ListNode* node=newNode(data); list->size++; if(list->last) { list->last->next=node; list->last=node; } else { list->first=node; list->last=node; } } -
%0Avoid%20ls_append%28List%20list%2Cvoid%2A%20data%29%0A%7B%0A%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%20ListNode%2A%20node%3DnewNode%28data%29%3B%0A%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%20list-%3Esize%2B%2B%3B%0A%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%20%0A%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%20if%28list-%3Elast%29%0A%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%20%7B%0A%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%20list-%3Elast-%3Enext%3Dnode%3B%0A%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%20list-%3Elast%3Dnode%3B%0A%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%20%7D%0A%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%20else%0A%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%20%7B%0A%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%20list-%3Efirst%3Dnode%3B%0A%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%20list-%3Elast%3Dnode%3B%0A%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%20%7D%0A%7D%0A%20 Insertar un dato en cierta posiciónEn el caso que el usuario desee insertar el nodo al inicio la tenemos fácil porque usamos el puntero “first” de la cabecera. En cambio, para insertar el nodo en cierto indice i>0 debemos recorrer la lista hasta el nodo i-1, el cual quedará apuntando al nuevo nodo cuando modifiquemos su campo next. En ambos casos, finalmente, hacemos que el nuevo nodo apunte al ex nodo i. void ls_insert(List list,int index,void* data) { list->size++; if(index==0) { ListNode* node=newNode(data); node->next=list->first; list->first=node; } else if(index<list->size) { int i; ListNode* p=list->first; ListNode* node=newNode(data); for(i=0;i<index-1;i++) p=p->next; node->next=p->next; p->next=node; } } -
%0A%20void%20ls_insert%28List%20list%2Cint%20index%2Cvoid%2A%20data%29%0A%7B%0A%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%20list-%3Esize%2B%2B%3B%0A%0A%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%20if%28index%3D%3D0%29%0A%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%20%7B%0A%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%20ListNode%2A%20node%3DnewNode%28data%29%3B%0A%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%20node-%3Enext%3Dlist-%3Efirst%3B%0A%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%20list-%3Efirst%3Dnode%3B%0A%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%20%7D%0A%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%20else%20if%28index%3Clist-%3Esize%29%0A%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%20%7B%0A%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%20int%20i%3B%0A%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%20ListNode%2A%20p%3Dlist-%3Efirst%3B%0A%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%20ListNode%2A%20node%3DnewNode%28data%29%3B%0A%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%20for%28i%3D0%3Bi%3Cindex-1%3Bi%2B%2B%29%0A%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%20p%3Dp-%3Enext%3B%0A%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%20node-%3Enext%3Dp-%3Enext%3B%0A%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%20p-%3Enext%3Dnode%3B%0A%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%20%7D%0A%7D%0A%20%20 Borrar un dato Para borrar un dato debemos posicionarnos en el nodo correspondiente de la misma forma que lo hicimos en la función ls_insert. En esta oportunidad el nodo i-1 debe quedar apuntando al nodo i+1, o sea hacer que el campo next del nodo i-1 guarde el valor del campo next del nodo i. Luego se hace un free del nodo i. void ls_remove(List list,int index) { if(index==0) { if(list->first) { ListNode* p=list->first; if(list->first==list->last) list->last=NULL; list->first=list->first->next; free(p); list->size--; } } else if(index<list->size) { int i; ListNode* p,*q; p=list->first; for(i=0;i<index-1;i++) p=p->next; q=p->next; p->next=p->next->next; if(list->last==q) list->last=p->next; free(q); list->size--; } } -
%0Avoid%20ls_remove%28List%20list%2Cint%20index%29%0A%7B%0A%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%20if%28index%3D%3D0%29%0A%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%20%7B%0A%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%20if%28list-%3Efirst%29%0A%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%20%7B%0A%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%20ListNode%2A%20p%3Dlist-%3Efirst%3B%0A%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%20if%28list-%3Efirst%3D%3Dlist-%3Elast%29%0A%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%20list-%3Elast%3DNULL%3B%0A%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%20list-%3Efirst%3Dlist-%3Efirst-%3Enext%3B%0A%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%20free%28p%29%3B%0A%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%20list-%3Esize--%3B%0A%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%20%7D%0A%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%20%7D%0A%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%20else%20if%28index%3Clist-%3Esize%29%0A%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%20%7B%0A%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%20int%20i%3B%0A%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%20ListNode%2A%20p%2C%2Aq%3B%0A%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%20p%3Dlist-%3Efirst%3B%0A%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%20for%28i%3D0%3Bi%3Cindex-1%3Bi%2B%2B%29%0A%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%20p%3Dp-%3Enext%3B%0A%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%20q%3Dp-%3Enext%3B%0A%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%20p-%3Enext%3Dp-%3Enext-%3Enext%3B%0A%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%20if%28list-%3Elast%3D%3Dq%29%0A%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%20list-%3Elast%3Dp-%3Enext%3B%0A%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%20free%28q%29%3B%0A%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%20list-%3Esize--%3B%0A%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%20%7D%0A%7D%0A%20%20 Ver cuantos datos tiene la listaSimplemente retornamos el campo size de la cabecera. int ls_size(List list) { return list->size; } -
%0A%20int%20ls_size%28List%20list%29%0A%7B%0A%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%20return%20list-%3Esize%3B%0A%7D%0A%20%20 Recuperar el primer datoSi first no es NULL, retornamos el valor que guarda. void* ls_first(List list) { if(list->first) return list->first->data; else return NULL; } -
%0Avoid%2A%20ls_first%28List%20list%29%0A%7B%0A%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%20if%28list-%3Efirst%29%0A%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%20return%20list-%3Efirst-%3Edata%3B%0A%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%20else%0A%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%20return%20NULL%3B%0A%7D%0A%20 Recuperar el ultimo datoSi last no es NULL, retornamos el valor que guarda. void* ls_last(List list) { if(list->last) return list->last->data; else return NULL; } -
%0A%20void%2A%20ls_last%28List%20list%29%0A%7B%0A%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%20if%28list-%3Elast%29%0A%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%20return%20list-%3Elast-%3Edata%3B%0A%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%20else%0A%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%20return%20NULL%3B%0A%7D%0A%20%20 Recuperar el i-ésimo dato Hay que buscar el nodo i de la misma manera que en la funciones ls_insert se buscó el i-1, y retornar el campo data. Podemos aprovechar los punteros first y last para los casos en que el indice es 0 o size-1. También debemos tener cuidado de tratar correctamente el caso en que el parámetro i es inválido. void* ls_dataAt(List list,int index) { if(index==0) { if(list->first) return list->first->data; else return NULL; } else if(index==list->size-1) { return list->last->data; } else if(index>=list->size) return NULL; else { int i; ListNode* p=list->first; for(i=0;i<index;i++) p=p->next; return p->data; } } -
%0A%20void%2A%20ls_dataAt%28List%20list%2Cint%20index%29%0A%7B%0A%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%20if%28index%3D%3D0%29%0A%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%20%7B%0A%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%20if%28list-%3Efirst%29%0A%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%20return%20list-%3Efirst-%3Edata%3B%0A%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%20else%0A%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%20return%20NULL%3B%0A%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%20%7D%0A%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%20else%20if%28index%3D%3Dlist-%3Esize-1%29%0A%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%20%7B%0A%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%20return%20list-%3Elast-%3Edata%3B%0A%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%20%7D%0A%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%20else%20if%28index%3E%3Dlist-%3Esize%29%0A%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%20return%20NULL%3B%0A%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%20else%0A%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%20%7B%0A%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%20int%20i%3B%0A%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%20ListNode%2A%20p%3Dlist-%3Efirst%3B%0A%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%20for%28i%3D0%3Bi%3Cindex%3Bi%2B%2B%29%0A%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%20p%3Dp-%3Enext%3B%0A%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%20return%20p-%3Edata%3B%0A%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%20%7D%0A%7D%0A%20%20 Verificar si existe un dato Sólo debemos recorrer la lista avanzando desde el nodo first al last, y verificar en cada iteración si el valor guardado por el campo data corresponde con el parámetro data. int ls_find(List list,void* data) { ListNode* p=list->first; while(p) { if(p->data==data) return 1; p=p->next; } return 0; } -
%0A%20int%20ls_find%28List%20list%2Cvoid%2A%20data%29%0A%7B%0A%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%20ListNode%2A%20p%3Dlist-%3Efirst%3B%0A%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%20while%28p%29%0A%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%20%7B%0A%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%20if%28p-%3Edata%3D%3Ddata%29%0A%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%20return%201%3B%0A%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%20p%3Dp-%3Enext%3B%0A%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%20%7D%0A%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%20return%200%3B%0A%7D%0A%20%20 Borrar la lista En este caso se recorre la lista, pero en cada paso se va liberando la memoria usada por el nodo actual, teniendo cuidado de no perder el resto de la lista. void ls_delete(List list,int freeData) { ListNode* p=list->first; while(p) { list->first=list->first->next; if(freeData) free(p->data); free(p); p=list->first; } free(list); } -
%0A%20void%20ls_delete%28List%20list%2Cint%20freeData%29%0A%7B%0A%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%20ListNode%2A%20p%3Dlist-%3Efirst%3B%0A%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%20while%28p%29%0A%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%20%7B%0A%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%20list-%3Efirst%3Dlist-%3Efirst-%3Enext%3B%0A%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%20if%28freeData%29%0A%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%20free%28p-%3Edata%29%3B%0A%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%20free%28p%29%3B%0A%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%20p%3Dlist-%3Efirst%3B%0A%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%20%7D%0A%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%20free%28list%29%3B%0A%7D%0A%20%20 Crear iterador Como un iterador es sólo un puntero a nodo, buscamos el i-ésimo nodo y lo retornamos. ListIterator ls_iterator(List list,int index) { if(index>=list->size) return NULL; else { int i; ListNode* p=list->first; for(i=0;i<index;i++) p=p->next; return p; } } -
%0AListIterator%20ls_iterator%28List%20list%2Cint%20index%29%0A%7B%0A%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%20if%28index%3E%3Dlist-%3Esize%29%0A%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%20return%20NULL%3B%0A%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%20else%0A%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%20%7B%0A%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%20int%20i%3B%0A%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%20ListNode%2A%20p%3Dlist-%3Efirst%3B%0A%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%20for%28i%3D0%3Bi%3Cindex%3Bi%2B%2B%29%0A%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%20p%3Dp-%3Enext%3B%0A%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%20return%20p%3B%0A%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%20%7D%0A%7D%0A%20%20 Recuperar dato al que apunta el iteradorRetornamos el campo data del iterador. Siempre cuidamos de que no nos pasen un gol con un puntero NULL. void* ls_data(ListIterator it) { if(it) return it->data; else return NULL; } -
%0Avoid%2A%20ls_data%28ListIterator%20it%29%0A%7B%0A%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%20if%28it%29%0A%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%20return%20it-%3Edata%3B%0A%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%20else%0A%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%20return%20NULL%3B%0A%7D%0A%20%20%20 Avanzar el iteradorVerificamos la validez del puntero al iterador y que el iterador mismo no sea NULL. Hacemos que el iterador quede apuntando a su campo next. void ls_next(ListIterator* it) { if(it && *it) (*it)=(*it)->next; } -
%0Avoid%20ls_next%28ListIterator%2A%20it%29%0A%7B%0A%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%20if%28it%20%26%26%20%2Ait%29%0A%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%20%28%2Ait%29%3D%28%2Ait%29-%3Enext%3B%0A%7D%0A%20%20%20 Evaluar una función con cada dato Por último, la función encargada de recorrer la lista llamando a un callback, donde la única novedad es el llamado a la función provista por el usuario (con el campo data como parámetro, además de userData) que tiene el prototipo definido al principio. void ls_callback(List list,ListCallback callback,void* userData) { ListNode* p=list->first; while(p) { (*callback)(p->data,userData); p=p->next; } } -
%0Avoid%20ls_callback%28List%20list%2CListCallback%20callback%2Cvoid%2A%20userData%29%0A%7B%0A%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%20ListNode%2A%20p%3Dlist-%3Efirst%3B%0A%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%20while%28p%29%0A%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%20%7B%0A%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%20%28%2Acallback%29%28p-%3Edata%2CuserData%29%3B%0A%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%20p%3Dp-%3Enext%3B%0A%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%20%7D%0A%7D%0A%20%20%20%20 Programa ejemplo Ahora, a poner en acción nuestra super lista . La función printLength es nuestra callback de prueba, que se limita a tirar por pantalla el largo de string que espera recibir en el parámetro data. #include <stdio.h> #include <string.h> #include "list.h" void printLength(void* data,void* userData) { printf("largo de \"%s\" es %d\n", (char* )data,strlen ((char* )data )); } int main() { char* sz1="uno"; char* sz2="dos"; char* sz3="tres"; char* sz4="cuatro"; List list; ListIterator it; list=ls_create(); ls_append(list,sz1); ls_append(list,sz3); ls_insert(list,1,sz2); ls_append(list,sz4); printf("hay %d elementos\n",ls_size (list )); printf("el primer elemento es \"%s\"\n" ,(char*)ls_first(list)); printf("el elemento con indice 1 es \"%s\"\n" ,(char*)ls_dataAt(list,1)); printf("el elemento con indice 2 es \"%s\"\n" ,(char*)ls_dataAt(list,2)); printf("el ultimo elemento es \"%s\"\n" ,(char*)ls_last(list)); printf("\nborrando el elemento con indice 2\n"); ls_remove(list,2); printf("\n***Listando con iterador:\n"); it=ls_iterator(list,0); while(it) { printf("%s\n", (char* )ls_data (it )); ls_next(&it); } if(!ls_find(list,sz3)) { ls_insert(list,2,sz3); printf("\ninsertando sz3 en indice 2\n"); } printf("\n***Recorriendo con callback\n"); ls_callback(list,printLength,NULL); ls_delete(list,0); return 0; } -
%0A%23include%20%3Cstdio.h%3E%0A%23include%20%3Cstring.h%3E%0A%23include%20%22list.h%22%0A%0Avoid%20printLength%28void%2A%20data%2Cvoid%2A%20userData%29%0A%7B%0A%C2%A0%C2%A0%C2%A0%20printf%28%22largo%20de%20%5C%22%25s%5C%22%20es%20%25d%5Cn%22%2C%28char%2A%29data%2Cstrlen%28%28char%2A%29data%29%29%3B%0A%7D%0A%0Aint%20main%28%29%0A%7B%0A%C2%A0%C2%A0%C2%A0%20char%2A%20sz1%3D%22uno%22%3B%0A%C2%A0%C2%A0%C2%A0%20char%2A%20sz2%3D%22dos%22%3B%0A%C2%A0%C2%A0%C2%A0%20char%2A%20sz3%3D%22tres%22%3B%0A%C2%A0%C2%A0%C2%A0%20char%2A%20sz4%3D%22cuatro%22%3B%0A%0A%C2%A0%C2%A0%C2%A0%20List%20list%3B%0A%C2%A0%C2%A0%C2%A0%20ListIterator%20it%3B%0A%0A%C2%A0%C2%A0%C2%A0%20list%3Dls_create%28%29%3B%0A%C2%A0%C2%A0%C2%A0%20ls_append%28list%2Csz1%29%3B%0A%C2%A0%C2%A0%C2%A0%20ls_append%28list%2Csz3%29%3B%0A%C2%A0%C2%A0%C2%A0%20ls_insert%28list%2C1%2Csz2%29%3B%0A%C2%A0%C2%A0%C2%A0%20ls_append%28list%2Csz4%29%3B%0A%0A%C2%A0%C2%A0%C2%A0%20printf%28%22hay%20%25d%20elementos%5Cn%22%2Cls_size%28list%29%29%3B%0A%C2%A0%C2%A0%C2%A0%20printf%28%22el%20primer%20elemento%20es%20%5C%22%25s%5C%22%5Cn%22%0A%C2%A0%C2%A0%C2%A0%20%C2%A0%C2%A0%C2%A0%20%C2%A0%C2%A0%C2%A0%20%C2%A0%C2%A0%C2%A0%20%C2%A0%C2%A0%C2%A0%20%C2%A0%C2%A0%C2%A0%20%C2%A0%C2%A0%C2%A0%20%C2%A0%C2%A0%C2%A0%20%C2%A0%C2%A0%C2%A0%20%2C%28char%2A%29ls_first%28list%29%29%3B%0A%C2%A0%C2%A0%C2%A0%20printf%28%22el%20elemento%20con%20indice%201%20es%20%5C%22%25s%5C%22%5Cn%22%0A%C2%A0%C2%A0%C2%A0%20%C2%A0%C2%A0%C2%A0%20%C2%A0%C2%A0%C2%A0%20%C2%A0%C2%A0%C2%A0%20%C2%A0%C2%A0%C2%A0%20%C2%A0%C2%A0%C2%A0%20%C2%A0%C2%A0%C2%A0%20%C2%A0%C2%A0%C2%A0%20%C2%A0%C2%A0%C2%A0%20%2C%28char%2A%29ls_dataAt%28list%2C1%29%29%3B%0A%C2%A0%C2%A0%C2%A0%20printf%28%22el%20elemento%20con%20indice%202%20es%20%5C%22%25s%5C%22%5Cn%22%0A%C2%A0%C2%A0%C2%A0%20%C2%A0%C2%A0%C2%A0%20%C2%A0%C2%A0%C2%A0%20%C2%A0%C2%A0%C2%A0%20%C2%A0%C2%A0%C2%A0%20%C2%A0%C2%A0%C2%A0%20%C2%A0%C2%A0%C2%A0%20%C2%A0%C2%A0%C2%A0%20%C2%A0%C2%A0%C2%A0%20%2C%28char%2A%29ls_dataAt%28list%2C2%29%29%3B%0A%C2%A0%C2%A0%C2%A0%20printf%28%22el%20ultimo%20elemento%20es%20%5C%22%25s%5C%22%5Cn%22%0A%C2%A0%C2%A0%C2%A0%20%C2%A0%C2%A0%C2%A0%20%C2%A0%C2%A0%C2%A0%20%C2%A0%C2%A0%C2%A0%20%C2%A0%C2%A0%C2%A0%20%C2%A0%C2%A0%C2%A0%20%C2%A0%C2%A0%C2%A0%20%C2%A0%C2%A0%C2%A0%20%C2%A0%C2%A0%C2%A0%20%2C%28char%2A%29ls_last%28list%29%29%3B%0A%0A%C2%A0%C2%A0%C2%A0%20printf%28%22%5Cnborrando%20el%20elemento%20con%20indice%202%5Cn%22%29%3B%0A%C2%A0%C2%A0%C2%A0%20ls_remove%28list%2C2%29%3B%0A%0A%C2%A0%C2%A0%C2%A0%20printf%28%22%5Cn%2A%2A%2AListando%20con%20iterador%3A%5Cn%22%29%3B%0A%C2%A0%C2%A0%C2%A0%20it%3Dls_iterator%28list%2C0%29%3B%0A%C2%A0%C2%A0%C2%A0%20while%28it%29%0A%C2%A0%C2%A0%C2%A0%20%7B%0A%C2%A0%C2%A0%C2%A0%20%C2%A0%C2%A0%C2%A0%20printf%28%22%25s%5Cn%22%2C%28char%2A%29ls_data%28it%29%29%3B%0A%C2%A0%C2%A0%C2%A0%20%C2%A0%C2%A0%C2%A0%20ls_next%28%26it%29%3B%0A%C2%A0%C2%A0%C2%A0%20%7D%0A%0A%C2%A0%C2%A0%C2%A0%20if%28%21ls_find%28list%2Csz3%29%29%0A%C2%A0%C2%A0%C2%A0%20%7B%0A%C2%A0%C2%A0%C2%A0%20%C2%A0%C2%A0%C2%A0%20ls_insert%28list%2C2%2Csz3%29%3B%0A%C2%A0%C2%A0%C2%A0%20%C2%A0%C2%A0%C2%A0%20printf%28%22%5Cninsertando%20sz3%20en%20indice%202%5Cn%22%29%3B%0A%C2%A0%C2%A0%C2%A0%20%7D%0A%0A%C2%A0%C2%A0%C2%A0%20printf%28%22%5Cn%2A%2A%2ARecorriendo%20con%20callback%5Cn%22%29%3B%0A%C2%A0%C2%A0%C2%A0%20ls_callback%28list%2CprintLength%2CNULL%29%3B%0A%0A%C2%A0%C2%A0%C2%A0%20ls_delete%28list%2C0%29%3B%0A%0A%C2%A0%C2%A0%C2%A0%20return%200%3B%0A%7D%0A%20%20%20%20 El programa produce la siguiente salida en nuestro amigo valgrind: totex@trauko ~/work/list $ valgrind --leak-check=yes ./test ==28663== Memcheck, a memory error detector. ==28663== Copyright (C) 2002-2007, and GNU GPL'd, by Julian Seward et al. ==28663== Using LibVEX rev 1732, a library for dynamic binary translation. ==28663== Copyright (C) 2004-2007, and GNU GPL'd, by OpenWorks LLP. ==28663== Using valgrind-3.2.3, a dynamic binary instrumentation framework. ==28663== Copyright (C) 2000-2007, and GNU GPL'd, by Julian Seward et al. ==28663== For more details, rerun with: -v ==28663== hay 4 elementos el primer elemento es "uno" el elemento con indice 1 es "dos" el elemento con indice 2 es "tres" el ultimo elemento es "cuatro"
borrando el elemento con indice 2
***Listando con iterador: uno dos cuatro
insertando sz3 en indice 2
***Recorriendo con callback largo de "uno" es 3 largo de "dos" es 3 largo de "tres" es 4 largo de "cuatro" es 6 ==28663== ==28663== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 7 from 1) ==28663== malloc/free: in use at exit: 0 bytes in 0 blocks. ==28663== malloc/free: 6 allocs, 6 frees, 52 bytes allocated. ==28663== For counts of detected errors, rerun with: -v ==28663== All heap blocks were freed -- no leaks are possible. |
Aunque uno no debe confiar ciegamente en este tipo de herramientas, al menos es un buen indicio de que las cosas funcionan relativamente bien. Cualquier falla, me la indicas discretamente por email o me dejas en verguenza públicamente con un comentario 
|