Translate

27 mar 2013

Programación de un menú con punteros.

Programación de un menú mediante memoria dinámica.

El menú se centrará en controlar el stock de productos de una tienda de móviles, no me centraré en el aspecto gráfico.

Lo primero a realizar serán las cabeceras y la biblioteca:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

Le he añadido el string.h porque en principio puedo imaginar que manejaré cadenas de caracteres, el stdio.h y el lib.h han venido por defecto así que no los tocaré.

El siguiente paso será pensar qué vamos a tratar con el programa: Pues unos móviles o celulares, así que tendré que implementar la estructura que tendrán.


struct estructura {
    int id;
    char marca[25];
    char modelo[25];
    char color[25];
    float precio;
    struct estructura *sig;
};
typedef struct estructura tMovil;

char password[7]="Entrar";

Cada unidad dispondrá de un id, de una marca, de un modelo, de un color y de un precio. El tipo de dato tMovil servirá principalmente para referirnos a la estructura, una manera de abreviar el "struct estructura". El puntero interno siguiente servirá para enlazar desde la memoria incontables móviles, controlarlos y realizar operaciones con ellos.

También he declarado una cadena de caracteres predefinida, será la contraseña para poder acceder al menú. Cuando un empleado desee acceder a la aplicación deberá insertar una contraseña, solamente la he definido como una global, influirá en todo el código. También está la opción de que el usuario introduzca una contraseña manualmente y se guarde en la variable, pero he preferido este método ya que el nuevo empleado no tendrá por qué introducir la contraseña que le parezca mejor, sino que la empresa ya disponga de una para el programa en sí. En esta ocasión la contraseña será la palabra "Entrar".

Entonces vamos a ir creando ya la inserción de la contraseña, que es lo primero que debe suceder en el programa:


void acceder() {
    char password2[7];
    printf("Introduzca la contraseña: ");
    scanf("%s", password2);
    if(strcmp(password, password2)==0) {
mostrarMenu();
    }
    else {
system("clear");
printf("\nLa contraseña no es válida.");
    }
}

La función "acceder" no recibirá parámetros, he declarado el vector de caracteres "password2", que será la destinada a teclear por teclado, en otras palabras, se le pedirá al usuario que introduzca una contraseña y ese valor se almacenará en la variable, ipso facto se comprobará si esa contraseña introducida es igual a la que está predefinida por la empresa (supuestamente), se compararía con la password global, si son iguales ejecutará la función de mostrar menú y ahí empezará la aplicación, sino coincide (se ha introducido una falsa o errónea), se limpiará la pantalla y dirá que la contraseña introducida no es válida. Y la siguiente pregunta es: ¿Se termina el programa con esto? Cierto es que no hay manera de continuar si se introduce una contraseña errónea, por lo que para mejorar la eficiencia del programa (pensemos que el usuario o empleado llevaba algo de prisa y tecleó mal una letra, ¿tendría que volver a iniciar el programa entero? Sería muy tedioso), crearé un bucle eterno para que siempre pida la contraseña.

Por tanto bajo la variable global de la contraseña declararé una variable numérica con un valor:


char password[7]="Entrar";
int bucle=1;

El valor del bucle será el número uno. Ahora volveré al fragmento de código que pedía la contraseña y añadiré las modificaciones que crea necesarias para que todo funcione:


int main(int argc, char** argv) {
    
    if(!acceder()) {
printf("\nAlgo ha ido mal.\n");
    }
    else {
printf("\nHa salido del programa con éxito.\n");
    }
    return (EXIT_SUCCESS);
}

int acceder() {
    char password2[7];
    int num;
    
    while(bucle!=0) {
printf("Introduzca la contraseña: ");
scanf("%s", password2);
if(strcmp(password, password2)==0) {
   mostrarMenu();
}
else {
   system("clear");
   printf("\nLa contraseña no es válida.\n"
   "¿Desea continuar?: --->[0=No - 1=Sí]<---\n");
   scanf("%d", &num);
   if(num==0) {
bucle=0;
return(1);
   }
   else {
system("clear");
   }
}
    }
}

int mostrarMenu() {
    printf("\nHola k ase.\n");
    return(1);
}




Después del susto, vamos a analizar esto, la función "main" es el programa en sí, cuando se abre un programa desde el escritorio o cualquier parte con un doble click se ejecuta lo que contiene la función "main". En esta se llama a la función acceder, el problema del C es que no tiene los valores "True" o "False", por tanto habrá que hacerlo todo con números, interpretaré que el 1 es true, y el 0 es false:


int main(int argc, char** argv) {
    
    if(!acceder()) {
printf("\nAlgo ha ido mal.\n");
    }
    else {
printf("\nHa salido del programa con éxito.\n");
    }
    return (EXIT_SUCCESS);
}


Si la función "acceder" no devuelve un true (nótese el signo de negación "!" al comienzo del if), dirá que algo ha ido mal, ha retornado un False (es decir, un 0), si sucede esto es que hay un problema gordote en el programa y la máxima prioridad será resolverlo. De lo contrario no andará por mucho que implemente o codifique. En caso contrario, si devuelve un True (un 1), saldrá en pantalla que se ha salido del programa con éxito y ahí finalizará todo.

En la función "acceder"...


int acceder() {
    char password2[7];
    int num;
    
    while(bucle!=0) {
printf("Introduzca la contraseña: ");
scanf("%s", password2);
if(strcmp(password, password2)==0) {
    mostrarMenu();
}
else {
    system("clear");
    printf("\nLa contraseña no es válida.\n"
    "¿Desea continuar?: --->[0=No - 1=Sí]<---\n");
    scanf("%d", &num);
    if(num==0) {
bucle=0;
return(1);
    }
    else {
system("clear");
    }
}
    }
}


He cambiado el valor de "void" por el de "int", es decir, devolverá un número en algún punto del bloque de código. He declarado un número tras el vector de caracteres y luego genero el bucle eterno. Mientras la variable bucle sea distinta de cero (le he asignado arriba el valor 1, por lo que siempre se repetirá) le pedirá al usuario por pantalla que introduzca la contraseña, esta se guardará en el vector de password2 y comparará mediante el strcmp lo introducido con la contraseña ya establecida arriba, el password global. Si son iguales (si el strcmp de ambos vectores es igual a cero, esto funciona mediante una resta de caracteres, cada uno de ellos en el código ASCII es un número, si la resta o el strcmp es positivo o negativo, es que los vectores o cadenas son diferentes, si es igual a cero son iguales), repito, si son iguales y el strcmp da cero se llamará a la función "mostrarMenú", en caso contrario se limpiará la pantalla y dirá que la contraseña no es válida.

Ahí no termina la cosa, preguntará también si se desea continuar o no, indicando que el 0 equivale al no y el 1 al sí, de ahí a que declarara arriba un número, que vendría a ser la variable protagonista en esta parte. Si se introduce un cero la variable bucle será cero y el bucle eterno del comienzo de la función se romperá, además retornará el valor 1, es decir, un True. Y si en el main esa función devuelve un True, saldrá el mensaje de que se ha salido con éxito del programa.

En caso de que no se introduzca un 0, sino un 1, se limpiará la pantalla y volverá a pedir contraseña, ojo a lo siguiente, en caso de ser un 1 el valor introducido funcionará perfectamente, pero en caso de ser una letra o cualquier otra cosa, podrían pasar cosas extrañas en el programa. En mi caso se salta la pregunta de contraseña y vuelve a pedir si se desea continuar o no. Ya que la condición del if solo valúa el 1, no el resto de valores (números o caracteres), y como no me quiero complicar más la existencia, lo dejaré así, que de momento funciona.

Respecto a este fragmento:


int mostrarMenu() {
    printf("\nHola k ase.\n");
    return(1);
}


Simplemente era por poner algo, para ver que se ejecutaba la función del menú correctamente aunque no hiciese nada útil. Todo hasta aquí funciona perfectamente (excepto la decisión de si se desea continuar o no, que funciona a medias, cumple con su finalidad pero no es perfecta), por lo que pasaré a desarrollar el menú:


int mostrarMenu() {
    int opcion;
    
    while(bucle!=0) {
printf("1. Agregar un nuevo artículo.\n2. Listar los artículos "
"disponibles.\n3. Buscar artículos por marca.\n4. Eliminar "
"artículo de la lista.\n5. Calcular el precio medio de una "
"marca.\n6. Crear oferta.\n0. Salir.\n\t¿Opción?: ");
scanf("%d", &opcion);
switch(opcion) {
   case 0:
system("clear");
printf("\nHa salido con éxito del menú.");
bucle=0;
break;
return(1);
   case 1:
system("clear");
printf("\nOpción uno válida.");
bucle=0;
break;
return(1);
   case 2:
system("clear");
printf("\nOpción dos válida.");
bucle=0;
break;
return(1);
   case 3:
system("clear");
printf("\nOpción tres válida.");
bucle=0;
break;
return(1);
   case 4:
system("clear");
printf("\nOpción cuatro válida.");
bucle=0;
break;
return(1);
   case 5:
system("clear");
printf("\nOpción cinco válida.");
bucle=0;
break;
return(1);
   case 6:
system("clear");
printf("\nOpción seis válida.");
bucle=0;
break;
return(1);
   default:
system("clear");
printf("\n¡Esa opción no es válida!\n");
}
    }
}


En el menú declaro un número cualquiera sin valor y luego generaré otro bucle eterno gracias a la variable bucle, que vale 1, mientras sea distinta de 0 (que lo es) seguirá repitiendo eso. La función mostrará en pantalla las distintas opciones del menú y pedirá una opción a elegir, quedando esto en pantalla, así tal cual:

1. Agregar un nuevo artículo.
2. Listar los artículos disponibles.
3. Buscar artículos por marca.
4. Eliminar artículo de la lista.
5. Calcular el precio medio de una marca.
6. Crear oferta.
0. Salir.
¿Opción?:

Voy a ejecutar el programa desde el comienzo, me pide la contraseña (debe ser la palabra "Entrar"), escribo un carácter cualquiera y me dice que es incorrecta, que si deseo continuar, introduzco un 0 y se cierra con éxito. Vuelvo a iniciar y marco otra contraseña incorrecta, en este caso cuando me pida si deseo continuar le daré el valor 1 (el sí deseo continuar) y me volverá a pedir contraseña, esta vez le introduzco la palabra correcta, "Entrar", y me mostrará en una nueva pantalla el menú con sus opciones. En el código del menú, tras el printf (la impresión en pantalla de todas las opciones) hay un switch case de la opción, habrá un total de 8 casos. Si se introduce como opción un número del 1 al 6 dirá que la opción es válida y saldrá del programa correctamente (aunque en el main diga que "algo ha ido mal", esto se debe a que la función "acceder" no devuelve un True pero se inicia y finaliza correctamente pues en la ventana OutPut dice "RUN SUCCESSFUL", y esto equivale a que nada ha ido mal internamente.), si se introduce un 0 (el case del 0), dirá que se sale correctamente del menú y repetirá el "algo ha ido mal" del main por la función "acceder". Si se introduce un número distinto del rango de 0 a 6, dirá que esa opción no es válida y repetirá el menú (esto vendría a ser el default del switch, la opción por defecto que no abarca las seleccionadas en los Case o casos).

Cabe aclarar también que en cada Case la variable bucle valdrá cero, el bucle eterno se romperá, hará un break y la función retornará un True (un 1).

Ahora toca hacer modificaciones al main, quedando así:


int main(int argc, char** argv) {
    
    acceder();
    return (EXIT_SUCCESS);
}


Simplemente se llamará a la función "acceder" y ya lo hará todo el menú. Si introducimos la opción 0 nos dirá que hemos salido correctamente del menú y todo finalizará. Esto lo he hecho así para evitar problemas al comienzo con la función "acceder", ahora que va todo bien no hay por qué comprobar algo que nos da más problemas que alegrías (problemas por el "Algo ha ido mal" cuando se supone que va bien).

Por tanto el programa ya pide una contraseña, verifica que es correcta o no, da a elegir si se desea continuar y en caso de ser correcta muestra el menú con sus opciones, donde la opción 0 cierra con éxito todo, la opción por defecto vuelve a mostrar el menú y las opciones del 1 al 6 todavía se deben implementar.

En primer lugar, ahora que tengo el esqueleto del menú hecho, voy a "rematar" (por decirlo de alguna manera) la estética del menú para que quede lo más cómodo posible. Antes de nada iré a la función de acceder y la modificaré tal que:


int acceder() {
    char password2[7];
    int num;
    
    while(bucle!=0) {
printf("Introduzca la contraseña: ");
scanf("%s", password2);
if(strcmp(password, password2)==0) {
   system("clear");
   if(mostrarMenu()) {
   }
}
else {
   system("clear");
   printf("\nLa contraseña no es válida.\n"
   "¿Desea continuar?: --->[0=No - 1=Sí]<---\n");
   scanf("%d", &num);
   if(num==0) {
bucle=0;
system("clear");
printf("\nHa salido con éxito del programa.");
return(1);
   }
   else {
system("clear");
   }
}
    }
}


Como al finalizar el programa ya no dice nada, pues he decidido modificar la parte en la que se selecciona mal la contraseña y no se desea continuar (se inserta un 0). Ahora cuando inserte el cero me dirá que he salido con éxito del programa. Lo siguiente es la "estética" del menú, el cual lo he modificado para que quede algo así:


int mostrarMenu() {
    int opcion;
    
    while(bucle!=0) {
printf("1. Agregar un nuevo artículo.\n2. Listar los artículos "
"disponibles.\n3. Buscar artículos por marca.\n4. Eliminar "
"artículo de la lista.\n5. Calcular el precio medio de una "
"marca.\n6. Crear oferta.\n0. Salir.\n\t¿Opción?: ");
scanf("%d", &opcion);
switch(opcion) {
   case 0:
system("clear");
printf("\nHa salido con éxito del menú.");
bucle=0;
break;
return(1);
   case 1:
printf("\nOpción uno válida.\n");
getchar(); getchar();
system("clear");
break;
   case 2:
printf("\nOpción dos válida.\n");
getchar(); getchar();
system("clear");
break;
   case 3:
printf("\nOpción tres válida.\n");
getchar(); getchar();
system("clear");
break;
   case 4:
printf("\nOpción cuatro válida.\n");
getchar(); getchar();
system("clear");
break;
   case 5:
printf("\nOpción cinco válida.\n");
getchar(); getchar();
system("clear");
break;
   case 6:
printf("\nOpción seis válida.\n");
getchar(); getchar();
system("clear");
break;
   default:
printf("\n¡Esa opción no es válida!\n\n");
getchar(); getchar();
system("clear");
}
    }
}


En esta ocasión, si se selecciona la opción 0 finalizará el programa y dirá que se ha salido con éxito del menú. En el resto de opciones, he logrado que cada una de ellas sea independiente y funcione a su manera, vamos a explicarlo bien... Si introduzco un 1, un 2, o cualquier otra opción disponible se activará un doble "getchar()", esto sirve para que en pantalla salga lo siguiente:

"Opción x válida."

Y espere a que se presione una tecla ("enter" generalmente, pero cualquiera es válida) para que se borre la pantalla mediante el "system("clear")" y se vuelva a reescribir el menú en vez de pasar a la siguiente opción, ya que el "break" de cada "Case" rompe el "Switch" de manera definitiva. Si introduzco una opción que en teoría no es válida sucederá lo mismo pero con el mensajito de se ha seleccionado algo que no es válido.

Ahora que el menú funciona perfectamente y es cómodo para el usuario, pasaré a la primera opción, "Agregar un nuevo artículo".

En esta tarea voy a tener que declarar un puntero en el propio menú junto a la variable numérica de la opción, debería quedar así al comienzo de la función "mostrarMenu()":


int mostrarMenu() {
    int opcion;
    tMovil *inicioLista=NULL;

.......


Un puntero llamado "inicioLista" que comenzará siendo nulo, no tendrá ningún valor, esto lo hago para evitar que se acumule basura desde la declaración y es algo muy importante en los punteros. El inicioLista estará apuntando o marcando una dirección de memoria en concreto que es donde comenzará la lista de artículos (está apuntando a un tipo de dato "tMovil", un tipo de dato para móviles). Por ahora no lo voy a utilizar pero esa sería su finalidad. Ahora tendré que declarar una función para pedir los datos, otra que cree un artículo y una que los vaya insertando todos de manera ordenada.

Vamos a la fundamental, la de pedir datos:


void pedirDatos(tMovil **inicioLista) {
    int numArticulos, i;
    tMovil datosMovil;
    
    system("clear");
    printf("\n¿Cuántos artículos desea añadir?:\n");
    scanf("%d", &numArticulos);
    for(i=1; i<=numArticulos; i++) {
system("clear");
idActualizado++;
datosMovil.id=idActualizado;
printf("\nArtículo nº: %d", i);
printf("\n¿Marca?:\n");
scanf("%s", datosMovil.marca);
printf("\n¿Modelo?:\n");
scanf("%s", datosMovil.modelo);
printf("\n¿Color?:\n");
scanf("%s", datosMovil.color);
printf("\n¿Precio?:\n");
scanf("%f", &datosMovil.precio);
insercionFinal(inicioLista, datosMovil);
    }
}


Esta función no devolverá ningún valor y recibirá como parámetro el doble puntero del inicio de la lista, ¿por qué un doble puntero? Pues porque se va a modificar la dirección de memoria a la que apuntará cuando la función finalice, es la dirección de una "cosa" (un puntero) que tiene marcada la dirección de memoria de un supuesto artículo que vaya a crear. Luego declaro el número de artículos, la variable "i", y una variable de tipo "tMovil" que tendrá acceso directo a todos los atributos o características de un móvil.

Primero se le preguntará al empleado cuántos artículos desea introducir, y a cada artículo que introduzca se irán pidiendo los datos. Antes de nada, debo aclarar que el Id de cada artículo se irá actualizando de manera automática, por tanto me he decidido por una variable numérica global que empiece en cero, vamos de nuevo a la zona superior del código fuente donde tengo declaradas las variables globales:


char password[7]="Entrar";
int bucle=1, idActualizado=0;


Ahí he añadido la variable o elemento "idActualizado" y es cero desde el comienzo. Ahora, en la función de pedir los datos al empleado nada más insertar el número de artículos la variable global se incrementará en uno y ese será el Id del artículo actual. Todos los datos insertados por teclado se almacenarán en la variable "datosMovil", y al final de cada artículo se llamará a la función "inserciónFinal" con el puntero y los datos introducidos, esa función la implementaré posteriormente.

Antes de ir a la función de insertar por el final, tengo que codificar la función de crear un artículo y aislarlo en la memoria RAM, vendría a ser la siguiente:


tMovil* crearArticulo(tMovil datosMovil) {
    tMovil *pAux=NULL;
    pAux=(tMovil*)malloc(sizeof(tMovil));
    
    strcpy(pAux->marca, datosMovil.marca);
    strcpy(pAux->modelo, datosMovil.modelo);
    strcpy(pAux->color, datosMovil.color);
    pAux->precio=datosMovil.precio;
    pAux->sig=NULL;
    
    return(pAux);
}


Esta función devolverá un puntero a un tipo de dato "tMovil", es decir, la dirección de memoria en la que se aloja un artículo, devuelve un número. Recibirá por parámetro los datos introducidos por teclado y declararé  un puntero auxiliar, con el que reservaré memoria mediante el malloc (memory allocate). Esta última operación es sencilla:

pAux [el puntero] = [será igual a..] (tMovil*) [un puntero de tipo tMovil] malloc [y reserva memoria] (sizeof(tMovil)) [de un elemento con el tamaño justo de una estructura tMovil, un struct estructura]

Por tanto, lo que queda es igualar los datos del parámetro (los introducidos por teclado) a los del elemento creado que tiene una memoria en la RAM reservada y es algo existente. Las cadenas de caracteres se copian con el comando "=" de los números, sino con el strcpy (string copy), copiaré los atributos que sean vectores de caracteres mediante el strcpy, del datosMovil al puntero pAux, y el precio al ser un float, un número muy grande, bastará con el "=". El puntero interno "siguiente" será nulo, por lo que el elemento o artículo quedará aislado en la memoria, localizado únicamente por el pAux. Y finalmente devuelvo el propio pAux, el número, la dirección de memoria en la que está localizado el artículo que acabo de crear.

Y ahora sí, la famosa función de insertar por el final que tampoco tiene mucho misterio:


void insercionFinal(tMovil **inicioLista, tMovil datosMovil) {
    tMovil *pAux=NULL;
    
    if(*inicioLista==NULL) {
*inicioLista=crearArticulo(datosMovil);
    }
    else {
pAux=*inicioLista;
while(pAux->sig!=NULL) {
   pAux=pAux->sig;
}
pAux->sig=crearArticulo(datosMovil);
    }
}


La inserción por el final no devolverá nada, recibirá como parámetros el puntero que marca el inicio de la lista y los datos del artículo introducido por teclado. Declaro un puntero auxiliar que me ayude a "moverme" por la lista de artículos.

Primero compruebo si en la lista hay algo o no, si no hay nada (que será el primer caso sí o sí) el puntero de inicio marcará a la dirección de memoria que ha devuelto la función "crearArticulo" mediante los datos del teclado y al finalizar la función, al ser un doble puntero el inicio de la lista queda con los cambios registrados de manera definitiva. Si ya hay al menos un elemento en la lista, el puntero auxiliar marcará la misma dirección de memoria que marca la del inicio de la lista, he necesitado de un puntero auxiliar porque de lo contrario, si muevo el inicio de la lista por la propia lista (valga la redundancia) se me quedará modificado y entonces no sería un inicio de nada.

Por tanto, mientras el puntero interno "siguiente" del primer elemento de la lista (en la primera "vuelta" del bucle while) sea distinto de nulo (es decir, apunta a una dirección de memoria en la que hay otro artículo), el puntero auxiliar apuntará a la dirección de memoria a la que apunta o marca el puntero interno, pasando así por todos los artículos de la lista hasta llegar al último.

Cuando el bucle se rompe significa que ha llegado al último, por lo que en esa situación el puntero "siguiente" del último elemento de la lista apuntará a la dirección de memoria a la que apunta el puntero devuelto por la función de crear a partir de los datos (¡aaaaaaaaaaaahh pedazo traba lenguas!).


Y por último y más importante de la primera opción del menú, modificar esto en la función de mostrarMenu():


   case 1:
pedirDatos(&inicioLista);
getchar(); getchar();
system("clear");
break;


En el caso de que se presione la opción 1 se llamará a la función "pedirDatos" y se le pasará la dirección de memoria del puntero inicioLista (ya que la función de pedir datos pide un doble puntero y esto equivale a eso). Tras introducir todos los datos tendremos en la memoria RAM una lista de elementos enlazados mediante direcciones de memoria y el puntero de inicioLista marcará al primero de todos ellos, al finalizar nos pedirá que presiones cualquier tecla mediante el doble "getchar()", la pantalla se limpiará y volverá a mostrar el menú.

En cuanto pueda paso a la segunda opción, paciencia please ^^

[[[PENDIENTE DE ACTUALIZAR]]]

24 mar 2013

Programación de listas con punteros.

Hoy voy a crear esta entrada con el objetivo de repasar programación avanzada en C, mediante punteros y direcciones de memoria, más que nada para entender el funcionamiento de todo esto y de cómo se opera con punteros.

Lo primero que hay que hacer es, obviamente, elaborar un nuevo proyecto en C, yo lo he llamado "ListasPunteros" con una única carpeta de código llamada "Main".

Lo primero a incluir serán las cabeceras (librería), por defecto ya me vienen stdio y stdlib, así que nada que añadir o modificar por el momento:

#include <stdio.h>  {Libre    }
#include <stdlib.h> {        ía}

Lo siguiente a realizar es una lista, debo crear primero la estructura de una lista, o elemento (por elemento se entiende que es una lista), la lista en principio solo contendrá un número, puesto que lo me interesa es practicar el uso y manejo de punteros y memoria dinámica, no de modificar los datos de la lista. Y obviamente, para que las listas se enlacen entre sí, en su propia estructura deben tener un puntero que "apunte" a la propia estructura, un puntero interno, éste será el encargado de enlazar listas. Esto queda así:

struct lista {
        int num;
        struct lista *sig;
};

Obviamente, eso de escribir "struct lista" cada vez que declaro un puntero es un coñazo, y algo tedioso, por lo que procederé a crear un tipo de dato que me simplifique trabajo:

typedef struct lista tLista;

O bien:

typedef struct lista {
       int num;
       struct lista *sig;
} tLista;

Lo siguiente que me atreveré a realizar es un puntero que marcará siempre el inicio de la lista, lo declararé en el main (función main) como: tLista *inicioLista=NULL;
He procurado en dejar el puntero inicializado a nulo para que no almacene basura o parásitos. Lo siguiente a llevar a cabo, es crear una lista, así de simple:

tLista* crearLista(int valor)
{
        tLista *p=NULL;
        p=(tLista*)malloc(sizeof(tLista));
        if(valor==NULL) {
                   printf("No ha introducido ningún valor, no se puede crear la lista.");
        else {
                   p->num=valor;
                   p->sig=NULL;
                   return(p);
}

¿Simple, verdad? Pues sí, bastante, la función "crearLista" me creará una sola lista cada vez que la llame, será la encargada de crear, nada más. Devolverá un puntero de tipo "tLista", es decir, una dirección de memoria en la que se aloja la estructura de una lista (un número y un puntero interno), dicha dirección de memoria que devolverá obviamente será la lista creada. La función recibirá como parámetro un número (int valor). Posteriormente declaro otro puntero (el destinado a apuntar la dirección de memoria creada), llamado "p" e igualado a nulo por las razones explicadas arriba con "*inicioLista", ese mismo puntero, p, reservará memoria para una estructura mediante el "malloc", y después la función comprobará si el valor introducido por parámetro será nulo o no, si es nulo (es decir, no tiene ningún valor), se mostrará en pantalla un aviso que dirá lo siguiente: "No ha introducido ningún valor, no se puede crear la lista.". Si no se cumple ese caso y el parámetro sí tiene valor numérico, el puntero "p" asignará al número de la lista reservada previamente (malloc), el valor del parámetro (p->num=valor), y asignará al puntero interno "sig" (siguiente) un valor nulo, ya que prefiero que primero la lista creada quede aislada. Finalmente devolverá la dirección de memoria en la que se aloja el puntero "p", con los datos de la lista.

Lo siguiente a realizar es la concatenación de listas, unir todas las listas que vaya a crear en un futuro, primero lo haré de manera "inicial", es decir, cada lista que cree se unirá a las que ya están creadas para formar así una especie de "fila" de listas unidas. Para eso voy a crear una función que se encargue de hacer dicha tarea, basada obviamente en la función previa "crearLista":


void insercionInicial(tLista **inicioLista, int valor)
{
    tLista *p=NULL;
    if(*inicioLista==NULL) {
p=crearLista(valor);
*inicioLista=p;
    }
    else {
p=crearLista(valor);
p->sig=*inicioLista;
*inicioLista=p;
    }
}

La función "inserciónInicial" simplemente no devolverá ningún valor, recibirá un doble puntero que esté marcando al inicio de la lista (doble puntero es la dirección de la dirección de memoria, por lo que el inicio de la lista quedará modificado cuando la función finalice), y un valor numérico. En primer lugar declararé un puntero a nulo, posteriormente preguntaré si el inicio de la lista apunta a nulo (la lista está vacía), si se da el caso, p apuntará a la dirección de memoria que se ha creado mediante el parámetro numérico y la función de crear, e inicioLista apuntará a ese elemento creado que es apuntado por el puntero local "p", creando así una lista de un solo elemento, este es el caso en el que no haya nada creado. Si no se da, crearé una estructura y p apuntará a su dirección de memoria, y gracias al puntero interno siguiente, apuntará a la dirección de memoria a la que apunta el puntero encargado de marcar el inicio de la lista, por tanto el elemento creado se ha insertado antes (va "detrás") del elemento inicial de la lista, y por último, el puntero que marca el inicio apuntará al nuevo elemento creado, siendo este el nuevo y primer elemento de la lista entera.

Para comprobar que todo ande como es debido, realizaré una función que me imprima en pantalla los elementos de las listas:


void imprimirLista(tLista *inicioLista)
{
    if(inicioLista==NULL) {
printf("La lista está vacía.");
    }
    else {
while(inicioLista!=NULL){
   printf("%d", inicioLista->num);
   if(inicioLista->sig!=NULL) {
printf("-->");
   }
   else {
printf("--//");
   }
   inicioLista=inicioLista->sig;
}
    }
}

Esta función se encargará de imprimirme una lista en pantalla. Recibirá como parámetro el inicio de la lista (el primer elemento), y comprobará si apunta a una dirección de memoria nula (donde no hay nada), o si en contraposición apuntará a una dirección de memoria en la que exista una estructura creada. Si apunta a la nada, dirá que la lista está vacía, si no, creará un bucle que se repetirá mientras la lista no finalice, esto se debe al algoritmo que he ideado en su interior:
Primero imprimirá el número de la estructura a la que apunta el inicio de la lista, si el puntero interno siguiente tiene una dirección de memoria (existe otro elemento, el segundo), mostrará en pantalla "-->" (esto), si no hay más elementos en la lista, mostrará esto "--//". Y por último, el puntero que apunta al inicio de la lista pasará a apuntar la dirección de memoria a la que apunta el puntero interno "siguiente", pasando de esta manera el segundo elemento y volviendo a repetir el bucle.

Otra manera de insertar elementos en la lista, será insertarlos en el final y no en el comienzo, para ello he ideado esta función:


void insercionFinal(tLista **inicioLista, int valor)
{
    tLista *p=NULL, *pAux=NULL;
    if(*inicioLista==NULL) {
p=crearLista(valor);
*inicioLista=p;
    }
    else {
pAux=*inicioLista;
p=crearLista(valor);
while((*inicioLista)->sig!=NULL) {
   *inicioLista=(*inicioLista)->sig;
}
(*inicioLista)->sig=p;
*inicioLista=pAux;
    }
}

La inserción Final (qNombre más chulo(?)). Recibirá como doble puntero el inicio de la lista, aunque realmente no sea estrictamente necesario hacerlo así, pero yo sí lo haré por comodidad, también se le pasará el valor de la nueva estructura a crear e insertar. Declararé dos punteros que apuntan a nulo y luego comprobaré si la lista está vacía o no, si lo está, creará una nueva estructura y esta pasará a ser el primer elemento de la lista, que no está vacía, pues el puntero Auxiliar (pAux), pasará a apuntar la dirección de memoria a la que apunta el inicio de la lista, luego p apuntará a un nuevo elemento creado y generaré un bucle, mientras el puntero interno "siguiente" no apunte a la nada (es decir, debe apuntar a otra dirección de memoria para que se repita el algoritmo), el puntero que marcaba al primer elemento pasará a apuntar al elemento al que apunta su puntero interno "siguiente", logrando con esto "moverse" a través de la fila de elementos hasta alcanzar el último. Una vez lo alcance, ese último elemento apuntará mediante el "siguiente" al elemento creado que hasta ahora estaba aislado, y el puntero destinado al inicio volverá a apuntar al primer elemento de la fila gracias al puntero Auxiliar que estaba allí.

De esta manera insertamos elementos al final de la lista.

Ahora voy a comprobar que todo lo que he hecho va perfectamente, así que haré varias modificaciones en la función "main", quedando esta así:


int main(int argc, char** argv)
{
    int array[]={5, 12, 1, 10, 32, 40}, i;
    const int MAXARRAY=6;
    tLista *inicioLista=NULL;
    for(i=0; i<MAXARRAY; i++) {
insercionInicial(&inicioLista, array[i]);
    }
    imprimirLista(inicioLista);
    printf("\n");
    insercionInicial(&inicioLista, 6);
    printf("\n");
    insercionFinal(&inicioLista, 100);
    imprimirLista(inicioLista);
 
    return (EXIT_SUCCESS);
}

Cuando se supera el susto inicial, uno se da cuenta de que no es para tanto, he creado una variable numérica   que hará de array, almacenando hasta 6 números que he puesto al azar. Luego declaro una constante que valdrá el mismo número de elementos que posee el array numérico, y además tengo el puntero que marca al inicio de la lista. Mediante un "for" llevaré a cabo un bucle hasta seis veces, y en cada pasada llamaré a la función de insertar por el inicio, utilizando el array numérico y el puntero de inicio. Luego llamo a la función de imprimir la lista creada (ha insertado los elementos en el bucle "for" previamente), vuelvo a insertar un número, me ha dado por insertar el 6, y luego llamo a la inserción final para insertar el número 100 por el final.

El resultado, lo que se debería mostrar en pantalla, vendría a ser algo así:



40-->32-->10-->1-->12-->5--//

6-->40-->32-->10-->1-->12-->5-->100--//

Primero muestro la lista de elementos insertados al principio, (array[]={5, 12, 1, 10, 32, 40}), la inserción inicial me insertó primero el 5, luego me insertó antes del cinco el 12, y así hasta el 40, siendo éste último el primer elemento de la lista. Inserto de nuevo al comienzo un seis, y luego al final inserto el número 100.