Autor Tema: Lenguaje C ==> free() siguiente tamaño, no válido // doble free.  (Leído 3967 veces)

0 Usuarios y 1 Visitante están viendo este tema.

Desconectado Daniel1952

  • PIC16
  • ***
  • Mensajes: 127
Hola, gente ¿cómo están?
Por ser mi primer hilo voy a ser una pequeña reseña de mi vida como estudiante, hoy precisamente cumplo mis primeros 65 años (nací el 18/01/1952) como podrán suponer no tengo
amigos que pueda consultar, en mi juventud no se había inventado la computadora de escritorio y soy el único del grupo de amigos que me dedique a esto y como deducirán tampoco pienso concurrir a universidades a aprender programación, por lo tanto, lo único que me queda ante la duda son los foros. -


Bueno al caso, les dejo el programa que me causa el error, no tengo mucho para agregar, los que puse en el enunciado son los 2 errores que me da, les dejo una imagen para completar. -

Código: C
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <string.h>
  4.  
  5. typedef struct{
  6.         char nombre[30];
  7.         char direccion[30];
  8.         int edad;
  9. }alumno;
  10.  
  11. void ajustes( char *tmp );
  12.  
  13. int main( void ){
  14.         alumno *b=NULL;
  15.         int max=1, i, opcion, ch, ok;
  16.  
  17.         do{
  18.                 system("reset");               
  19.                 b = ( alumno * )realloc( b, (max)*sizeof(alumno));             
  20.                 if( b==NULL ){
  21.                         printf("\n No se pudo asignar la memoria!");
  22.                         exit(EXIT_FAILURE);
  23.                 }
  24.                 printf("\n Ingresa el nombre del alumno [%d]......:", max);
  25.                 fgets( (b+max)->nombre, 30, stdin );
  26.                 ajustes( (b+max)->nombre );
  27.                
  28.                 printf("\n Ingresa la direccion del alumno [%d]...:", max);
  29.                 fgets( (b+max)->direccion, 30, stdin );
  30.                 ajustes( (b+max)->direccion );
  31.  
  32.                 do{
  33.                         printf("\n Ingresa la edad del alumno [%d]........:", max);
  34.                         ok = scanf("%d", &(b+max)->edad) == 1 && (b+max)->edad > 0;
  35.                         while ((ch = getchar()) != EOF && ch != '\n');
  36.                 }while(!ok);
  37.  
  38.                 do{
  39.                         printf( "\n 1 - Finaliza \n 2 - Agrega alumno\n Ingrese opcion...: " );
  40.                         ok = scanf("%d", &opcion) == 1 && opcion >= 1 && opcion <= 2;
  41.                         while ((ch = getchar()) != EOF && ch != '\n');
  42.                 }while(!ok);
  43.                
  44.                 max++; 
  45.         }while( opcion == 2 );
  46.  
  47.         for ( i=0; i<max; i++ ){
  48.                 printf( "%s\t%s\t%d\n",(*(b+i)).nombre, (*(b+i)).direccion, (*(b+i)).edad );
  49.         }
  50.  
  51.         free( b );
  52.  
  53.         return 0;
  54. }
  55.  
  56. void ajustes( char *tmp ){
  57.         char *p = NULL;
  58.         int ch;
  59.  
  60.         if(( p=strchr( tmp , '\n'))){
  61.                 *p='\0';
  62.         }else{
  63.                 while((ch = getchar()) !='\n' && ch!=EOF);
  64.         }
  65. }
Espero puedan ayudarme. -
Saludos.
Daniel
abraza las cosas y personas malas como si fueran tu mas preciada joya,Son tus mas grandes maestros de paciencia sabiduría y amor y cuando lo abrazas dejan de causar dolor.-

Desconectado KILLERJC

  • Colaborador
  • DsPIC33
  • *****
  • Mensajes: 8242
Re:Lenguaje C ==> free() siguiente tamaño, no válido // doble free.
« Respuesta #1 en: 18 de Enero de 2017, 14:18:24 »
Esto es lo unico que encuentro por ahora, antes de irme a dormir un rato.

-----------

Tengo entendido que realloc se comporta como malloc si el puntero que se le pasa es null.
Pero tal ves sea mejor probar de hacer un malloc antes en ves de asignarle NULL, total la primer vuelta va a dejarlo con el mismo tamaño.

---------

Solamente se asigno 1 espacio de para la estructura y el comienzo lo apunta b.
Hacer (b+max), cuando max es 1.
Es como si tuvieras 2 estructuras alojadas, es decir con b+max estarias saltando a la 2da estructura, la cual es un espacio de memoria incorrecto, ya que no se pidio esa memoria.
Deberias quitar el max.

Aunque apliques el realloc, estarias dejando 1 espacio de estructura sin usar. Por eso mismo deberias comenzar con un indice 0, para mostrar luego le sumas 1. Es decir en los printf.

----------------------------------

Otra cosa que me causa ruido es esta:

Código: C
  1. scanf("%d", &(b+max)->edad)

supuestamente la idea es pasar el puntero de edad.. pero me parece que estas equivocado. Si observas tenes que b esta definido y es un puntero que ocupa lugar en memoria, se le suma 1 y luego encima se le saca la direccion a eso (imagino que obtendras la direccion de donde se calculo de forma temporal), ahora la direccion de eso es de donde se encuentra alojado el puntero b+1, por lo cual si lo deferencias a eso ( con el -> ) simplemente accedes al contenido del puntero o direccion que te dio el malloc, pero NO la direccion de la estructura. Y por ende no tenes elementos de estructura alli.

Imagino que deberia quedar asi:

Código: C
  1. scanf("%d", &(b->edad))

b->edad = Contenido de edad
&(b->edad) = Direccion del elemento edad

-------------------------------------------

Existe alguna otra forma de realizar todo esto sin tener que hacer realloc? Ya que su costo computacional de mover TODO cada ves que se ingresa un alumno mas a la larga es grande.
SI, podrias crear un puntero a la proxima estructura, de esa forma solo mantenes 1 puntero que es el primero, y luego creando memorias y asignandoselo a las estructuras anteriores.
O lo que le llaman "Linked List"

Código: C
  1. struct alumno {
  2.         char nombre[30];
  3.         char direccion[30];
  4.         int edad;
  5.         struct alumno* ptrNext;
  6. };
  7.  
  8. typedef struct alumno alumno;

Al momento de liberar por supuesto vas a tener que pasar elemento a elemento. Y liberar cada uno de ellos.
Es una idea que te doy.

http://www.cprogramming.com/tutorial/c/lesson15.html
« Última modificación: 18 de Enero de 2017, 15:00:48 por KILLERJC »

Desconectado KILLERJC

  • Colaborador
  • DsPIC33
  • *****
  • Mensajes: 8242
Re:Lenguaje C ==> free() siguiente tamaño, no válido // doble free.
« Respuesta #2 en: 18 de Enero de 2017, 19:49:36 »
Corregido:

- Corregido max para que comienze de 0
- Corregido las variables de los scanf y que siga usandose el max.
- Modifique el printf, para que use el -> en ves de *
- Comente el system reset por que no le gustaba a mi PC al momento de debugearlo.
- No asignar el realloc a b, sino a otro puntero, ya que si realloc falla devuelve NULL y se pierde el puntero a la direccion de memoria a liberar.
- La salida que ocurre  cuando b es NULL porque el realloc no pudo tomar mas memoria, puede suceder cuando ya hay una memoria reservada, supongamos tenemos 1 estructura completa y intentemos agrandarlo para 2 estructuras. Falla por lo cual no mueve nada y devuelve NULL, detecta y sale, pero nos estamos olvidando de liberar la memoria que tomo la 1er estructura. Es decir le faltaba un free(b) solo si es distinto de NULL.
- Quite el cast del realloc, es void asi que se promociona solo, y en caso de cambiarle el nombre a la estructura no tenes que cambiar el cast (Aunque esto termina siendo cuestion de gustos al final)

Faltaria:

- Checkear que fgets tenga algo, usar algun pointer y fijarse si es NULL, para saber si por alguna casualidad alguien envio algo que no debia ( Como un EOF )

Codigo:

Código: C
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <string.h>
  4.  
  5. typedef struct{
  6.         char nombre[30];
  7.         char direccion[30];
  8.         int edad;
  9. }alumno;
  10.  
  11. void ajustes( char *tmp );
  12.  
  13. int main(void)
  14. {
  15.         alumno *b=NULL;
  16.         alumno *ptrtmp = NULL;
  17.         int max=0, i, opcion, ch, ok;
  18.  
  19.         do
  20.         {
  21.                 //system("reset");
  22.                 ptrtmp = realloc( b, (max+1)*sizeof(alumno));
  23.                 if( ptrtmp == NULL )
  24.                 {
  25.                         printf("\n No se pudo asignar la memoria!");
  26.                         if(b!=NULL)
  27.                         {
  28.                                 printf("\n Liberando b antes de salir");
  29.                                 free(b);
  30.                         }
  31.                         exit(EXIT_FAILURE);
  32.                 }
  33.                 else
  34.                 {
  35.                         b = ptrtmp;
  36.                 }
  37.  
  38.                 printf("\n Ingresa el nombre del alumno [%d]......:", max+1);
  39.                 fgets( (b+max)->nombre, 30, stdin );
  40.                 ajustes( (b+max)->nombre );
  41.  
  42.                 printf("\n Ingresa la direccion del alumno [%d]...:", max+1);
  43.                 fgets( (b+max)->direccion, 30, stdin );
  44.                 ajustes( (b+max)->direccion );
  45.  
  46.                 do
  47.                 {
  48.                         printf("\n Ingresa la edad del alumno [%d]........:", max+1);
  49.                         ok = scanf("%d", &((b+max)->edad)) == 1 && (b+max)->edad > 0;
  50.                         while ((ch = getchar()) != EOF && ch != '\n');
  51.                 }while(!ok);
  52.  
  53.                 do
  54.                 {
  55.                         printf( "\n 1 - Finaliza \n 2 - Agrega alumno\n Ingrese opcion...: " );
  56.                         ok = scanf("%d", &opcion) == 1 && opcion >= 1 && opcion <= 2;
  57.                         while ((ch = getchar()) != EOF && ch != '\n');
  58.                 }while(!ok);
  59.  
  60.                 max++;
  61.         }while( opcion == 2 );
  62.  
  63.         for ( i=0; i<max; i++ )
  64.         {
  65.                 printf( "\n%s\t%s\t%d",(b+i)->nombre, (b+i)->direccion, (b+i)->edad );
  66.         }
  67.  
  68.         printf("\n Liberando b antes de salir");
  69.         free( b );
  70.  
  71.         return EXIT_SUCCESS;
  72. }
  73.  
  74. void ajustes( char *tmp ){
  75.         char *p = NULL;
  76.         int ch;
  77.  
  78.         if(( p=strchr( tmp , '\n'))){
  79.                 *p='\0';
  80.         }else{
  81.                 while((ch = getchar()) !='\n' && ch!=EOF);
  82.         }
  83. }

Salida:

Código: [Seleccionar]
Ingresa el nombre del alumno [1]......:HOLA

 Ingresa la direccion del alumno [1]...:HOLA2

 Ingresa la edad del alumno [1]........:12

 1 - Finaliza
 2 - Agrega alumno
 Ingrese opcion...: 2

 Ingresa el nombre del alumno [2]......:HOLA3

 Ingresa la direccion del alumno [2]...:HOLA4

 Ingresa la edad del alumno [2]........:34

 1 - Finaliza
 2 - Agrega alumno
 Ingrese opcion...: 1
HOLA HOLA2 12
HOLA3 HOLA4 34

« Última modificación: 19 de Enero de 2017, 01:42:26 por KILLERJC »

Desconectado Daniel1952

  • PIC16
  • ***
  • Mensajes: 127
Re:Lenguaje C ==> free() siguiente tamaño, no válido // doble free.
« Respuesta #3 en: 19 de Enero de 2017, 13:53:20 »
Hola, KILLERJC. -
Que decir del trabajo que te tomaste tengo tantos adjetivo calificativo (buenos) que no estoy seguro si me alcanzaría la página para escribirlos. - ((:-))
Lo que más me gusta de lo que has hecho es que sin tantos preámbulos corregiste todo lo que había que corregir y no te das una idea como me encanta esa forma, yo estoy aprendiendo y cuando me corrigen hasta el mínimo detalle es una manera que me ayuda muchísimo a entender y tomar nota de los errores. - ((:-))
En cuanto a reset lo que hace, es limpiar la terminal y la deja sin scroll a diferencia de clear que si lo deja, solo funciona en Linux. -
Citar
- Checkear que fgets tenga algo, usar algun pointer y fijarse si es NULL, para saber si por alguna casualidad alguien envio algo que no debia ( Como un EOF )
Me parece que te referís a por Ej. hacer un do while y no dejar salir si el tamaño de la cadena es cero, decidme si ese es el caso. -

Me parece que no queda nada pendiente solo para mi memorizar los errores cometidos. -

Un fuerte abrazo y muchas gracias. -
Daniel
abraza las cosas y personas malas como si fueran tu mas preciada joya,Son tus mas grandes maestros de paciencia sabiduría y amor y cuando lo abrazas dejan de causar dolor.-

Desconectado KILLERJC

  • Colaborador
  • DsPIC33
  • *****
  • Mensajes: 8242
Re:Lenguaje C ==> free() siguiente tamaño, no válido // doble free.
« Respuesta #4 en: 19 de Enero de 2017, 14:43:45 »
Citar
En cuanto a reset lo que hace, es limpiar la terminal y la deja sin scroll a diferencia de clear que si lo deja, solo funciona en Linux. -

Lo hice en Ubuntu. Pero como lo estaba haciendo desde el IDE Eclipse tal ves por eso no le gustaba.

Citar
Me parece que te referís a por Ej. hacer un do while y no dejar salir si el tamaño de la cadena es cero, decidme si ese es el caso. -

https://www.tutorialspoint.com/c_standard_library/c_function_fgets.htm

Si te fijas en donde dice RETURN VALUE tenes que:

Cuando recibio algo y es correcto: Devuelve la direccion del puntero donde se almaceno, que es el mismo que le pasamos como parametro
Cuando recibe un EOF, y por ende no recibe nada: Devuelve NULL
Cuando ocurre un error: Devuelve NULL


Código: C
  1. while( fgets( (b+max)->direccion, 30, stdin ) == NULL );

Para detectar la falta de entrada o cualquier otro error. Yo aca hice que siga pidiendo una entrada si es NULL, pero lo mas seguro es que poseas un if preguntando si el puntero devuelto es NULL y actuar en consecuencia del error.

-----------------------------

Acabo de ver ajustes, la cual no le habia prestado atencion hasta el momento porque lo tuyo era problema de memoria.
Por lo que parece tu idea con ajustes es quitarle el '\n' del string si es que existe. Sigo sin entender cual fue tu objetivo del else de esa funcion.
Ya que con la parte del if era suficiente. Es decir, lo podes hacer "a mano":

Código: C
  1. void ajustes( char *tmp ){
  2.  
  3.         while(*tmp != '\0')
  4.         {
  5.                 if(*tmp == '\n')
  6.                 {
  7.                         *tmp = '\0';
  8.                         break;
  9.                 }
  10.                 tmp++;
  11.         }
  12. }

Observa que no necesito ningun puntero ni nada mas por el estilo, simplemente uso esa variable tmp para recorrer el string.
O usar la funcion que usaste.

Código: C
  1. void ajustes( char *tmp )
  2. {
  3.         if( tmp=strchr( tmp , '\n'))
  4.         {
  5.                 *tmp='\0';
  6.         }
  7. }
(Funcion que no probe, pero imagino que deberia funcionar correctamente)

A las funciones anteriores se podria agregar un argumento mas para que proceda no supere el tamaño maximo de array. Esto es por si las dudas la memoria no tiene ningun caracter nulo o 0, y podria ocurrir solo si fgets falla.
« Última modificación: 19 de Enero de 2017, 20:23:02 por KILLERJC »

Desconectado Daniel1952

  • PIC16
  • ***
  • Mensajes: 127
Re:Lenguaje C ==> free() siguiente tamaño, no válido // doble free.
« Respuesta #5 en: 21 de Enero de 2017, 10:48:47 »
Hola, KILLERJC ¿cómo estas?
Bueno ahora me toca explicar el porqué hago ciertas cosas, me cuesta un tanto porque como estoy estudiando algunas líneas de código si veo que funciona por el momento las utilizo y ya, tengo claro que en algún momento tendré que repasar todo y tratar de interpretar lo más posible -
Me refiero al else de la función ajustes, en la imagen que dejo a continuación refleja fielmente como actúa fgets -
En el caso concreto de mi programa, si se ingresa menos de 29 caracteres (28-27-26-25) lo que ara fgets es poner '\n' '\0' hasta hay todo bien con el bucle del if buscara el salto de línea '\n' y lo remplazara por el fin de cadena '\0'. -
Pero que pasa si el ingreso es 30-31-32-33....50 caracteres, copiara al puntero p los primeros 29 caracteres y en el elemento 29 pondrá el fin de cadena '\0', el resto quedara en el buffer de teclado y tengo entendido que en la próxima lectura fgets al encontrar el salto de línea dejada del anterior ingreso no te dejara ingresar nada por teclado y hay más (esto lo comprobé yo mismo) si no pongo el else y con el if no encuentra el '\n' (por lo mensionado anteriormente) el programa no continua queda a espera de un <Enter> . -

Bueno tú me dirás si es que estoy equivocado. -
Saludos.
Daniel
abraza las cosas y personas malas como si fueran tu mas preciada joya,Son tus mas grandes maestros de paciencia sabiduría y amor y cuando lo abrazas dejan de causar dolor.-

Desconectado KILLERJC

  • Colaborador
  • DsPIC33
  • *****
  • Mensajes: 8242
Re:Lenguaje C ==> free() siguiente tamaño, no válido // doble free.
« Respuesta #6 en: 21 de Enero de 2017, 10:54:23 »
Tenes razon no estas equivocado. Comprendo como funciona el buffer de entrada y el fgets.
No se por que pense que luego del fgets tenias algo que limpiaba el buffer.

Como luego de los scanf limpias el buffer de entrada, pense que hacias lo mismo con los fgets. Ahi fue mi confusion.

Ademas mezclar el reemplazo del '\n' con el vaciado del buffer no se me cruzo nunca. Ya que es el main quien se ocupa del tomar datos del buffer y limpiarlos, segun tenes los scanf, asumi erroneamente que ocurria lo mismo con el fgets.
« Última modificación: 21 de Enero de 2017, 11:00:20 por KILLERJC »

Desconectado Daniel1952

  • PIC16
  • ***
  • Mensajes: 127
Re:Lenguaje C ==> free() siguiente tamaño, no válido // doble free.
« Respuesta #7 en: 21 de Enero de 2017, 13:53:27 »
Hay 2 cosas que me dejaron intrigado y con muchas ganas de llevarlas a la practica, la primera es el trozo de código (mas abajo) que no me funciona, en realidad no parece estar mal pero hay algo con el comportamiento de fgets que desconozco, te lo dejo por si en algún momento desear rebizarlo y decirme que esta mal. -

Código: C
  1. do{
  2.                         ok=1;
  3.                         printf("\n Ingresa el nombre del alumno [%d]......:", max+1);
  4.                         if( fgets( (b+max)->nombre, 30, stdin ) == NULL ){
  5.                                 ok=0;                  
  6.                                 printf( "\n El dato es obligatorio..." );
  7.                                 printf( "\n Pulsa <Enter> para intentarlo nuevamente..." ); getchar();
  8.                         }
  9.                 }while(!ok);           
  10.                 ajustes( (b+max)->nombre );
Saludos.
abraza las cosas y personas malas como si fueran tu mas preciada joya,Son tus mas grandes maestros de paciencia sabiduría y amor y cuando lo abrazas dejan de causar dolor.-

Desconectado KILLERJC

  • Colaborador
  • DsPIC33
  • *****
  • Mensajes: 8242
Re:Lenguaje C ==> free() siguiente tamaño, no válido // doble free.
« Respuesta #8 en: 21 de Enero de 2017, 22:46:21 »
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:

Código: C
  1. char * test;
  2.         char testA[30];
  3.  
  4.         while(1)
  5.         {
  6.                 printf("\nIngrese algo :");
  7.                 test = fgets(testA,sizeof testA, stdin);
  8.                 printf("\n Retorno: 0x%X",test);
  9.         }

En el IDE no podia enviar las cosas, pero en la consola de Linux si. Despues de ejecutar el codigo salio asi:

Código: [Seleccionar]
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.
Código: C
  1. #include <stdio.h>
  2. #include <limits.h>
  3. #include <stdlib.h>
  4. #include <string.h>
  5.  
  6. typedef struct{
  7.         char nombre[30];
  8.         char direccion[30];
  9.         int edad;
  10. }alumno;
  11.  
  12. enum NumError
  13. {
  14.         NUM_OK,
  15.         NUM_OUT_OF_RANGE,
  16.         NUM_NOT_FOUND
  17. };
  18.  
  19. enum StrError
  20. {
  21.         STR_OK,
  22.         STR_NULL,
  23.         STR_EMPTY
  24. };
  25.  
  26. enum StrError get_StringCin(char * tmp , int size);
  27. enum NumError get_NumCin(int * num, int limitL, int limitH);
  28. void flushCin(void);
  29.  
  30. int main(void)
  31. {
  32.         alumno *ptrStructAlumno= NULL;          // Puntero a estructura ubicada en el heap
  33.         int max = 0;                                            // Mantiene la cantidad de alumnos ingresados
  34.         int itmp;                                                       // Variable auxiliar
  35.  
  36.         do
  37.         {
  38.                 //system("reset");
  39.  
  40.                 /*
  41.                  * Pedido de memoria del heap
  42.                  *
  43.                  */
  44.  
  45.                 alumno *ptrtmp = NULL;                  // Puntero auxiliar
  46.  
  47.                 ptrtmp = ( alumno * )realloc( ptrStructAlumno, (max+1)*sizeof(alumno));
  48.                 if( ptrtmp == NULL )
  49.                 {
  50.                         /*
  51.                          * ERROR, no es posible asignar la memoria, limpiamos y salimos.
  52.                          */
  53.  
  54.                         printf("\n No se pudo asignar la memoria!");
  55.                         if(ptrStructAlumno!=NULL)
  56.                         {
  57.                                 free(ptrStructAlumno);
  58.                         }
  59.                         exit(EXIT_FAILURE);
  60.                 }
  61.                 else
  62.                 {
  63.                         /*
  64.                          * Fue posible asignar la memoria, procedemos a reasignar el puntero
  65.                          */
  66.                         ptrStructAlumno = ptrtmp;
  67.                 }
  68.  
  69.  
  70.                 /*
  71.                  * Comenzamos con los pedidos de datos string
  72.                  */
  73.  
  74.                 printf("\n Ingresa el nombre del alumno [%d]......:", max+1);
  75.                 while(get_StringCin((ptrStructAlumno+max)->nombre,sizeof (ptrStructAlumno+max)->nombre) != STR_OK)
  76.                 {
  77.                         printf("\n Ingresa el nombre del alumno de forma correcta [%d]...:", max+1);
  78.                 }
  79.  
  80.  
  81.                 printf("\n Ingresa la direccion del alumno [%d]...:", max+1);
  82.                 while(get_StringCin((ptrStructAlumno+max)->direccion,sizeof (ptrStructAlumno+max)->direccion) != STR_OK)
  83.                 {
  84.                         printf("\n Ingresa la direccion del alumno de forma correcta [%d]...:", max+1);
  85.                 }
  86.  
  87.  
  88.                 /*
  89.                  * Pedidos de datos numericos
  90.                  */
  91.  
  92.                 printf("\n Ingresa la edad del alumno [%d]........:", max+1);
  93.                 while(get_NumCin(&(ptrStructAlumno->edad),1,100) != NUM_OK)
  94.                 {
  95.                         // Numero ERRONEO
  96.                         printf("\n Por favor Ingresa edad del alumno con numeros desde 1-100 [%d]........:", max+1);
  97.                 }
  98.  
  99.                 printf( "\n 1 - Finaliza \n 2 - Agrega alumno\n Ingrese opcion...: " );
  100.                 while(get_NumCin(&itmp,1,2) != NUM_OK)
  101.                 {
  102.                         //Numero ERRONEO
  103.                         printf( "\n El dato ingresado no es una opcion valida, reingrese su opcion" );
  104.                         printf( "\n 1 - Finaliza \n 2 - Agrega alumno\n Ingrese opcion...: " );
  105.                 }
  106.  
  107.                 max++;
  108.         }while( itmp == 2 );
  109.  
  110.         for ( itmp = 0; itmp < max; itmp++ )
  111.         {
  112.                 printf( "\n%s\t%s\t%d",(ptrStructAlumno+itmp)->nombre, (ptrStructAlumno+itmp)->direccion, (ptrStructAlumno+itmp)->edad );
  113.         }
  114.  
  115.         // Liberamos memoria
  116.         free( ptrStructAlumno );
  117.  
  118.         return EXIT_SUCCESS;
  119. }
  120.  
  121.  
  122. /*
  123.  *  Funcion que toma un string desde CIN
  124.  *  Ademas procede a limpiar CIN
  125.  *
  126.  */
  127.  
  128. enum StrError get_StringCin(char * str, int size)
  129. {
  130.         char * tmp;
  131.         tmp = fgets( str, size, stdin );
  132.  
  133.         // Chequeo que no se presiono Ctrl+D (Unix) o Ctrl+Z (Windows)
  134.         if(tmp == NULL)
  135.         {
  136.                 return STR_NULL;
  137.         }
  138.  
  139.         // Falta chk de linea vacia o con solo espacios en blancos.
  140.         // Falta eliminacion de whitespaces al comienzo y/o final de la trama
  141.  
  142.         tmp = strchr( str , '\n');
  143.         if(tmp)
  144.         {
  145.                 *tmp='\0';
  146.         }
  147.         else
  148.         {
  149.                 flushCin();
  150.         }
  151.         return STR_OK;
  152. }
  153.  
  154. /*
  155.  * Funcion que lee la entrada CIN esperando un numero y luego procede
  156.  * a comprobar que se encuentra dentro de los valores limites dados.
  157.  *
  158.  * Return:
  159.  *
  160.  * Devuelve los siguientes valores:
  161.  *
  162.  * NUM_OK : El numero fue encontrado y se encuentra dentro del rango
  163.  * NUM_OUT_OF_RANGE : Se encontro un numero pero esta fuera del rango dado
  164.  * NUM_NOT_FOUND : Caso en que no se encuentro ningun numero en cin
  165.  *
  166.  *
  167.  * Para eliminar los limites, usar:
  168.  *
  169.  * Con signo:
  170.  *              get_NumCin(&numero,INT_MIN,INT_MAX);
  171.  * Sin signo:
  172.  *              get_NumCin(&numero,0,INT_MAX);
  173.  */
  174.  
  175.  
  176. enum NumError get_NumCin(int * num, int limitL, int limitH)
  177. {
  178.  
  179.         int j;
  180.         j = scanf("%d", num);
  181.         flushCin();
  182.         if(j == 1)
  183.         {
  184.                 if((*num >= limitL) && (*num<=limitH))
  185.                 {
  186.                         return NUM_OK;
  187.                 }
  188.                 else
  189.                 {
  190.                         return NUM_OUT_OF_RANGE;
  191.                 }
  192.         }
  193.  
  194.         return NUM_NOT_FOUND;
  195. }
  196.  
  197. /*
  198.  * Funcion que permite la limpieza de CIN
  199.  */
  200.  
  201. void flushCin(void)
  202. {
  203.         char ch;
  204.         while ((ch = getchar()) != '\n' && ch != EOF);
  205. }

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?
« Última modificación: 22 de Enero de 2017, 02:40:37 por KILLERJC »

Desconectado Daniel1952

  • PIC16
  • ***
  • Mensajes: 127
Re:Lenguaje C ==> free() siguiente tamaño, no válido // doble free.
« Respuesta #9 en: 23 de Enero de 2017, 08:01:20 »
hola, muy buen día. -
Estuve revisando el programa para interpretar y aprender lo más posible porque hay muchas cosas nuevas como utilizar el tipo enum en una función, pero me lleve la sorpresa cuando lo corri y en los campos nombre y dirección si pulso <Enter> sin ingresar ningún dato no pasa nada, ¿no está contemplado el error o hay algo que falla?. -

EDITO: Esta es la manera con la cual lo había solucionado, por supuesto faltan validaciones. -

Código: C
  1. do{
  2.                         ok=1;
  3.                         printf("\n Ingresa la direccion del alumno [%d]......:", max+1);
  4.                         if( fgets( (b+max)->direccion, 30, stdin ) == NULL || (b+max)->direccion[0] == '\n'){
  5.                                 ok=0;                  
  6.                                 printf( "\n El dato es obligatorio..." );
  7.                                 printf( "\n Pulsa <Enter> para intentarlo nuevamente..." ); getchar();
  8.                                 system("reset");
  9.                         }
  10.                 }while(!ok);           
  11.                 ajustes( (b+max)->direccion );
saludos.
Daniel
« Última modificación: 23 de Enero de 2017, 08:21:18 por Daniel1952 »
abraza las cosas y personas malas como si fueran tu mas preciada joya,Son tus mas grandes maestros de paciencia sabiduría y amor y cuando lo abrazas dejan de causar dolor.-

Desconectado KILLERJC

  • Colaborador
  • DsPIC33
  • *****
  • Mensajes: 8242
Re:Lenguaje C ==> free() siguiente tamaño, no válido // doble free.
« Respuesta #10 en: 23 de Enero de 2017, 09:06:15 »
Citar
¿no está contemplado el error o hay algo que falla?.

No, no esta contemplado, y lo comente para que si querias lo agregues. En esta funcion, donde dice que falta el check de linea vacia y de los espacios

Código: C
  1. enum StrError get_StringCin(char * str, int size)
  2. {
  3.         char * tmp;
  4.         tmp = fgets( str, size, stdin );
  5.  
  6.         // Chequeo que no se presiono Ctrl+D (Unix) o Ctrl+Z (Windows)
  7.         if(tmp == NULL)
  8.         {
  9.                 return STR_NULL;
  10.         }
  11.  
  12.         // Falta chk de linea vacia o con solo espacios en blancos.
  13.         // Falta eliminacion de whitespaces al comienzo y/o final de la trama
  14.  
  15.         tmp = strchr( str , '\n');
  16.         if(tmp)
  17.         {
  18.                 *tmp='\0';
  19.         }
  20.         else
  21.         {
  22.                 flushCin();
  23.         }
  24.         return STR_OK;
  25. }

Citar
tipo enum en una función

El enum pensalo como si fuera un numero. Seria equivalente que haga algo asi:

Código: C
  1. #define        STR_OK        0
  2. #define        STR_NULL       1
  3. #define        STR_EMPTY       2
  4.  
  5. // Ahora la funcion devolvia un int en ves de un enum
  6. int get_StringCin(char * tmp , int size)
  7. {
  8.     //.... Aca codigo
  9.     return STR_OK;
  10. }

Observaras que es lo mismo..

Desconectado Daniel1952

  • PIC16
  • ***
  • Mensajes: 127
Re:Lenguaje C ==> free() siguiente tamaño, no válido // doble free.
« Respuesta #11 en: 24 de Enero de 2017, 15:58:14 »
Hola  KILLERJC.
Mil disculpas por no prestar la debida atención, ahora voy a intentar hacer todas las validaciones necesarias y cuando las finalice me gustaría ponerlas en un archivo .h dado que no lo hice nunca y me interesa aprender a hacerlo, estarás deduciendo que ni bien termine te voy a pedir algún ejemplo. -   

Saludos
abraza las cosas y personas malas como si fueran tu mas preciada joya,Son tus mas grandes maestros de paciencia sabiduría y amor y cuando lo abrazas dejan de causar dolor.-

Desconectado KILLERJC

  • Colaborador
  • DsPIC33
  • *****
  • Mensajes: 8242
Re:Lenguaje C ==> free() siguiente tamaño, no válido // doble free.
« Respuesta #12 en: 24 de Enero de 2017, 19:09:17 »
Solo para que tengas referencia, en un .h pones todo aquello que no ocupe lugar fisicamente de memoria. Tales como enum, definicion de estructuras, typedef, include, prototipos de funciones.

Nunca deberias poner la declaracion de una variable adentro.

Desconectado Daniel1952

  • PIC16
  • ***
  • Mensajes: 127
Re:Lenguaje C ==> free() siguiente tamaño, no válido // doble free.
« Respuesta #13 en: 28 de Enero de 2017, 07:32:16 »
Hola, un saludo para todos.
Bueno algo logre, dejo el programa para que me digas que otras validaciones necesitaría hacer, las que ya hice. -
Si se ingresa <enter> con la cantidad de espacios que sea o sin espacios el programa no lo permite. -
Se eliminan los espacios al principio y al final (trim). -
En cuanto a Ctrl+C o Ctrl+Z en Linux lo que hace es finalizar el programa así que me parece que no tiene sentido validarlo. -

Código: C
  1. #include <stdio.h>
  2. #include <limits.h>
  3. #include <stdlib.h>
  4. #include <string.h>
  5. #include <ctype.h>    
  6.  
  7. void get_String( char *tmp );
  8. void ajustes( char *tmp );
  9. void mensajeError();
  10.      
  11. int main( void ){
  12.         char tmp[30];        
  13.                
  14.         get_String( tmp );
  15.        
  16. return EXIT_SUCCESS;
  17. }
  18.      
  19. void get_String( char *tmp ){
  20.         int ok, i=0, j=0, espacios=0, espIzquierda=0;
  21.  
  22.         do{
  23.                 ok=1;
  24.                 printf("\n Ingresa el nombre completo del alumno ......:" );
  25.                 fgets( tmp, 30, stdin );
  26.                 ajustes( tmp );        
  27.                 while( tmp[i] != '\0' ){
  28.                         if( tmp[i] == 32 ){
  29.                                 espacios++;
  30.                         }else{
  31.                                 if( tmp[0] == 32 && espIzquierda == 0 ){
  32.                                         espIzquierda = i;                              
  33.                                 }                      
  34.                         }
  35.                         i++;           
  36.                 }
  37.                 if( i == espacios ){
  38.                         ok=0; espacios=0; i=0;
  39.                         mensajeError();                                
  40.                 }
  41.         }while( !ok );
  42.  
  43.         if( espIzquierda > 0 ){
  44.                 while( tmp[j] != '\0' ){
  45.                         tmp[j] = tmp[j+espIzquierda];
  46.                         j++;
  47.                 }
  48.         }
  49.         j=i-1;
  50.         while( j >= 0 ){
  51.                 if( isalpha(tmp[j])){
  52.                         tmp[j+1] = '\0';                       
  53.                         break;         
  54.                 }
  55.                 j--;
  56.         }
  57. }      
  58.  
  59. void ajustes( char *tmp ){
  60.         char *p = NULL;
  61.         int ch;
  62.  
  63.         if(( p=strchr( tmp , '\n'))){
  64.                 *p='\0';
  65.         }else{
  66.                 while((ch = getchar()) !='\n' && ch!=EOF);
  67.         }
  68. }
  69.  
  70. void mensajeError(){
  71.         printf( "\n El dato es obligatorio" );
  72.         printf( "\n\t Pulsa <Enter> para intentarlo nuevamente..." ); getchar();
  73. }
Es todo, un fuerte abrazo. -
Daniel
abraza las cosas y personas malas como si fueran tu mas preciada joya,Son tus mas grandes maestros de paciencia sabiduría y amor y cuando lo abrazas dejan de causar dolor.-

Desconectado KILLERJC

  • Colaborador
  • DsPIC33
  • *****
  • Mensajes: 8242
Re:Lenguaje C ==> free() siguiente tamaño, no válido // doble free.
« Respuesta #14 en: 28 de Enero de 2017, 20:00:23 »
Esta bien. Solo te recuerdo que si vas a aplicar la validaciones tanto a la direccion como al nombre, no deberias poner el printf dentro del get_string, me refiero a que el get_string pueda se utilizado con cualquier string que deseemos. Eso te va a llevar a pasarle el tamaño maximo del array donde guardas. Por eso en mi implementacion lo hice asi.

El script funciona correctamente como para un NOMBRE y la limpieza del string, estos fueron mis resultados al poner "Hola como" con espacios en distintos lados:

Código: [Seleccionar]
test varios espacios al frente:
 Valor string "Hola como"

 test 1 espacio al frente:
 Valor string "Hola como"

 test varios espacios atras:
 Valor string "Hola como"

 test 1 espacio atras:
 Valor string "Hola como"

 test varios espacios:
 Valor string "Hola como"

 test 1 espacio atras y adelante:
 Valor string "Hola como"

Pero estos son para una direccion "Hola 123":

Código: [Seleccionar]
test varios espacios al frente:
 Valor string "Hola"

 test 1 espacio al frente:
 Valor string "Hola"

 test varios espacios atras:
 Valor string "Hola"

 test 1 espacio atras:
 Valor string "Hola"

 test varios espacios:
 Valor string "Hola"

 test 1 espacio atras y adelante:
 Valor string "Hola"

Es cierto que Ctrl+C no deberias preocuparte, ya que es el simbolo para terminar el programa lo cual deberia liberar automaticamente cualquier memoria que tengas y no te importaria en nada lo que se reciba ya que el programa se termino. Pero Ctrl+Z es para suspender el trabajo, con lo cual despues lo podes resumir. Ejemplo,

en mi terminal de Linux abro gedit, mientras no lo cierro al gedit la terminal queda corriendo el mismo, pero al presionar Ctrl+Z me muestra lo siguiente:

Código: [Seleccionar]
test@test-desktop:~$ gedit
^Z
[1]+  Detenido                gedit

Si uso jobs puedo ver todos los trabajos y sus estados.

Código: [Seleccionar]
test@test-desktop:~$ jobs
[1]+  Detenido                gedit

Finalmente puedo resumirlo, tanto que vuelva a ser parte de la consola como estaba (fg) o que se ejecute en el background (bg):

Código: [Seleccionar]
test@test-desktop:~$ fg 1
gedit

Ahora no se si esto queda guardado en el buffer al suspenderse, o es atrapado primero antes de enviarlo al buffer.
-----------------------------------------------------------------------------

Una cosita mas acerca de tu codigo sigo viendo que la limpieza del buffer ocurre en ajuste, lo cual complica la lectura del codigo a simple vista. La idea general es que el codigo sea facilmente legible a la vista y sin mayores complicaciones. Por eso lo removi del lugar ese en el codigo que te habia pasado, pero nuevamente esto es cuestion de gustos. Ya que para mi gusto esta demasiado alejado de las funciones que manejan el buffer de entrada como fgets.

La otra es que cuando pedis que se presione un Enter para continuar, pedis simplemente por un getchar(), esto.... es un tema.. Mira lo que ocurre si presiono un Enter:

Citar
Ingresa el nombre completo del alumno ......:<Enter>

 El dato es obligatorio
    Pulsa <Enter> para intentarlo nuevamente...No voy a presionar un Enter

 Ingresa el nombre completo del alumno ......:
Resultado o voy a presionar un Enter

Remarque lo que fueron mis entradas. Y le agregue un printf para ver el valor que quedaba de la string.

-------------------------------------------------------

Reitero, si ya tu codigo cumple con tus expectativas/requerimiento entonces esta correcto.
Por mi mientras mas filtrado sea las entradas mejor. Pero eso va a depender de lo que vas a hacer con esas entradas.
« Última modificación: 28 de Enero de 2017, 20:14:19 por KILLERJC »


 

anything