Bueno, tengo un codigo para vos. Pienso que es hora de ir moviendo cosas a un .h por ahora no lo hice pero me quedo con las ganas.
El problema que no funciona es porque una linea vacia (presionar un enter solo) es lo mismo que se almacene en nombre "\n" Por lo tanto
SI devuelve algo, y es el salto de linea.
Entonces ¿Cuando devuelve NULL ? Cuando se encontro un error o se envio un EOF.
Para probar esto cree un pequeño codigo:
char * test;
char testA[30];
while(1)
{
test
= fgets(testA
,sizeof testA
, stdin
); printf("\n Retorno: 0x%X",test
); }
En el IDE no podia enviar las cosas, pero en la consola de Linux si. Despues de ejecutar el codigo salio asi:
Ingrese algo :h
Retorno: 0xD98CA5C0
Ingrese algo :a
Retorno: 0xD98CA5C0
Ingrese algo :
Retorno: 0x0
Ingrese algo :
Retorno: 0x0
Ingrese algo :a
Retorno: 0xD98CA5C0
Ingrese algo :
Retorno: 0xD98CA5C0
Ingrese algo :
Retorno: 0xD98CA5C0
Ingrese algo :
Los valores usados en el anterior codigo (que podes ver solo las letras) son los siguientes: h, a, Ctrl+D , Ctrl+D, a, <Enter>, <Enter>
Observaras que cuando envie un Ctrl+D lo que me devolvio es un 0 o NULL, pero cuando presione un Enter, me devolvio la direccion de testA[]
Con esto podras ver como funciona el fgets
--------------------------------------------------
Mirando un poco el codigo, empeze a pensar que existia demasiado codigo repetido, porque no quitarlo y llevarlo a funciones ?
Algo como esto:
(Aunque faltaria mover a funciones la limpieza y pedido de memoria tambien. Asi queda mas claro.
#include <stdio.h>
#include <limits.h>
#include <stdlib.h>
#include <string.h>
typedef struct{
char nombre[30];
char direccion[30];
int edad;
}alumno;
enum NumError
{
NUM_OK,
NUM_OUT_OF_RANGE,
NUM_NOT_FOUND
};
enum StrError
{
STR_OK,
STR_NULL,
STR_EMPTY
};
enum StrError get_StringCin(char * tmp , int size);
enum NumError get_NumCin(int * num, int limitL, int limitH);
void flushCin(void);
int main(void)
{
alumno *ptrStructAlumno= NULL; // Puntero a estructura ubicada en el heap
int max = 0; // Mantiene la cantidad de alumnos ingresados
int itmp; // Variable auxiliar
do
{
//system("reset");
/*
* Pedido de memoria del heap
*
*/
alumno *ptrtmp = NULL; // Puntero auxiliar
ptrtmp
= ( alumno
* )realloc( ptrStructAlumno
, (max
+1)*sizeof(alumno
)); if( ptrtmp == NULL )
{
/*
* ERROR, no es posible asignar la memoria, limpiamos y salimos.
*/
printf("\n No se pudo asignar la memoria!"); if(ptrStructAlumno!=NULL)
{
}
}
else
{
/*
* Fue posible asignar la memoria, procedemos a reasignar el puntero
*/
ptrStructAlumno = ptrtmp;
}
/*
* Comenzamos con los pedidos de datos string
*/
printf("\n Ingresa el nombre del alumno [%d]......:", max
+1); while(get_StringCin((ptrStructAlumno+max)->nombre,sizeof (ptrStructAlumno+max)->nombre) != STR_OK)
{
printf("\n Ingresa el nombre del alumno de forma correcta [%d]...:", max
+1); }
printf("\n Ingresa la direccion del alumno [%d]...:", max
+1); while(get_StringCin((ptrStructAlumno+max)->direccion,sizeof (ptrStructAlumno+max)->direccion) != STR_OK)
{
printf("\n Ingresa la direccion del alumno de forma correcta [%d]...:", max
+1); }
/*
* Pedidos de datos numericos
*/
printf("\n Ingresa la edad del alumno [%d]........:", max
+1); while(get_NumCin(&(ptrStructAlumno->edad),1,100) != NUM_OK)
{
// Numero ERRONEO
printf("\n Por favor Ingresa edad del alumno con numeros desde 1-100 [%d]........:", max
+1); }
printf( "\n 1 - Finaliza \n 2 - Agrega alumno\n Ingrese opcion...: " ); while(get_NumCin(&itmp,1,2) != NUM_OK)
{
//Numero ERRONEO
printf( "\n El dato ingresado no es una opcion valida, reingrese su opcion" ); printf( "\n 1 - Finaliza \n 2 - Agrega alumno\n Ingrese opcion...: " ); }
max++;
}while( itmp == 2 );
for ( itmp = 0; itmp < max; itmp++ )
{
printf( "\n%s\t%s\t%d",(ptrStructAlumno
+itmp
)->nombre
, (ptrStructAlumno
+itmp
)->direccion
, (ptrStructAlumno
+itmp
)->edad
); }
// Liberamos memoria
return EXIT_SUCCESS;
}
/*
* Funcion que toma un string desde CIN
* Ademas procede a limpiar CIN
*
*/
enum StrError get_StringCin(char * str, int size)
{
char * tmp;
tmp
= fgets( str
, size
, stdin
);
// Chequeo que no se presiono Ctrl+D (Unix) o Ctrl+Z (Windows)
if(tmp == NULL)
{
return STR_NULL;
}
// Falta chk de linea vacia o con solo espacios en blancos.
// Falta eliminacion de whitespaces al comienzo y/o final de la trama
if(tmp)
{
*tmp='\0';
}
else
{
flushCin();
}
return STR_OK;
}
/*
* Funcion que lee la entrada CIN esperando un numero y luego procede
* a comprobar que se encuentra dentro de los valores limites dados.
*
* Return:
*
* Devuelve los siguientes valores:
*
* NUM_OK : El numero fue encontrado y se encuentra dentro del rango
* NUM_OUT_OF_RANGE : Se encontro un numero pero esta fuera del rango dado
* NUM_NOT_FOUND : Caso en que no se encuentro ningun numero en cin
*
*
* Para eliminar los limites, usar:
*
* Con signo:
* get_NumCin(&numero,INT_MIN,INT_MAX);
* Sin signo:
* get_NumCin(&numero,0,INT_MAX);
*/
enum NumError get_NumCin(int * num, int limitL, int limitH)
{
int j;
flushCin();
if(j == 1)
{
if((*num >= limitL) && (*num<=limitH))
{
return NUM_OK;
}
else
{
return NUM_OUT_OF_RANGE;
}
}
return NUM_NOT_FOUND;
}
/*
* Funcion que permite la limpieza de CIN
*/
void flushCin(void)
{
char ch;
while ((ch
= getchar()) != '\n' && ch
!= EOF
); }
Podes observar que ahora el main, solo se ocupa de lo que importa en el programa, tomar dato, dar respuesta en caso de que no sea correcto.
Mientras que las demas funciones son las que se encargan de la toma de datos, del depurado, proteccion, limpieza del buffer, etc.
Tambien me parecio conveniente ya que lo usas en las opciones, que la toma de numeros tenga un check de limites. Por eso mismo tiene 2 parametros que limitan esos valores.
Los enum, estan realizados para presentar respuestas provenientes de estas funciones. Podria haber sido una respuesta booleana, pero a veces uno quiere o desea saber el por que del error, el de numeros por ejemplo, cuando no encuentra ninguno simplemente devuelve NUM_NOT_FOUND, y en caso de querer saber si esta fuera de los limites podes preguntar por NUM_OUT_OF_RANGE. Es interesante recalcar que a pesar de estar fuera de rango dado, el valor se almacena de todas formas.
Respecto a la funcion encargada del string, faltan mas protecciones, algunos datos interesantes a filtrar serian:
"\n" -> Deberia devolver STR_EMPTY
" \n" -> Deberia devolver STR_EMPTY
" \r\n" -> Deberia devolver STR_EMPTY
" Roberto" -> Deberia devolver "Roberto"
"Roberto " -> Deberia devolver "Roberto"
" Roberto " -> Deberia devolver "Roberto"
Tambien esta la opcion de seguir restringiendo el comportamiento para que solo acepte caracteres alfanumericos. Aunque a esto te lleve a usar scanf y no fgets
Pero todo esto ultimo va a depender de que el programa cumpla con los requerimientos que tenes. Sino hace falta proteccion o no esperas un Ctrl+D por ejemplo. Entonces ¿Por que usarlo?