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]]]
No hay comentarios:
Publicar un comentario