0
  • Hola, soy programador autodidacta y en resumidas cuentas empecé con C hace mas o menos dos meses y venia utilizando gets() y fflush() siempre que quería utilizar cadenas, el problema es que ahora estaba haciendo un pequeño programa para repasar memoria dinámica y estaba teniendo un error muy raro (mas adelante dejare el código), por azar entre en una pagina de C en la que se aseveraban unos cuantos errores de principiantes, resulta que aparentemente gets() es bastante pernicioso para el desempeño del programa y no es muy funcional que digamos, y recomendaban el uso de fgets()...

  • Por otro lado, queria preguntar si es mas recomendable usar variables globales que locales, en lo personal siempre utilizo globales ( a menos que se traten de iteradores en funciones), mi hermano esta en la universidad estudiando ingeniería informática y me sirve de mucho apoyo a veces, pero he de decir que es hasta contradictorio que en algunas ocasiones tienda a darme información falsa o simplemente no sabe de lo que habla, me preocupa bastante que ni siquiera yendo a la universidad si pueda encontrar información veraz ( si se lo pregunta mi hermano tiene muy buenas calificaciones, cosa que claro me preocupa aun mas), pero para reducirlo todo un poco, necesito información veraz :-).

  • Aquí dejo el código para ver si el error tiene en realidad relacion con el uso de gets() o no...

      #include<stdlib.h>
      #include<string.h>
      // variables 
      char **num;
      int i,cadenas=0;
      char eleccion[2];
      char **string;
      char cadena1[2][30];


// prototipos 
void menu_inicio();
void guardar();
void mostrar();
void registar(char **arreglo, int cadenas);


int main (){
    
menu_inicio();
return 0;
}
void registrar(char **arreglo, int cadenas){
char **pivote;
int i=0;

pivote = (char **) calloc(cadenas,sizeof(char *));
for(i=0;i<cadenas;i++){
pivote[i] = (char *) calloc(30,1 ); 
}

for(i=0;i<cadenas;i++){
strcpy(pivote[i], arreglo[i]);  
}
delete arreglo;

arreglo = (char **) calloc(cadenas+1,sizeof(char *));
for(i=0;i<cadenas+1;i++){
arreglo[i] = (char *) calloc(30,1 );    
}
for(i=0;i<cadenas;i++){
strcpy(arreglo[i], pivote[i]);  
}
delete pivote;
}



void menu_inicio(){
system("cls");
printf("\n\n\t\tIntroduce tu eleccion...\n\n\t\t");
printf("a)  Registrar cadenas\n\t\tb)  Ver cadenas registradas\n\t\tc)  Salir\n\t\t\t\t\t\t%c%c>>  ",205,205);;fflush(stdin);gets (eleccion);
if (eleccion[0]==97){
guardar();  
}
else{
if(eleccion[0]==98){
mostrar();
}
else{
if(eleccion[0]==99){
exit(-1);   
}   
}
}
    
}
void guardar(){
system("cls");
        if(cadenas>0){
        system("cls");
        registrar(string,cadenas);
        cadenas++;
        printf("\n\n\t\tIntroduce tu cadena: ");fflush(stdin);gets(string[cadenas-1]);
        system("cls");
        printf("\n\n\t\tAhora que deseas hacer?...\n\n\t");
        printf("a)  Volver al menu\n\tb)  Ver cadenas\n\tc)  Seguir Guardando\n\td)  Salir\n\t\t\t\t\t\t\t\t%c%c>> ",205,205);fflush(stdin);gets (eleccion);
        if(eleccion[0]==97){
        menu_inicio();  
        }
        else{
        if(eleccion[0]==98){
        mostrar();  
        }   
        else{
        if(eleccion[0]==99){
        guardar();  
        }
        else{
        if(eleccion[0] == 100){
        exit(-1);
        }   
        }   
        }
        }
        }
        else{
        string = (char **) malloc(sizeof(char *));
        *(string) = (char *) calloc(30,1); 
        cadenas++;
        printf("\n\n\t\tIntroduce tu cadena: ");fflush(stdin);fflush(stdin);gets (string[0]);
        system("cls");
        printf("\n\n\t\tAhora que deseas hacer?...\n\n\t");
        printf("a)  Volver al menu\n\tb)  Ver cadenas\n\tc)  Seguir Guardando\n\td)  Salir\n\t\t\t\t\t\t\t\t%c%c>> ",205,205);fflush(stdin);gets (eleccion);
        if(eleccion[0]==97){
        menu_inicio();  
        }
        else{
        if(eleccion[0]==98){
        mostrar();  
        }   
        else{
        if(eleccion[0]==99){
        guardar();  
        }
        else{
        if(eleccion[0] == 100){
        exit(-1);
        }   
        }   
        }
        }
        }
}
void mostrar(){
int i=0;
system("cls");
printf("\n\n\t\t");
for(i=0;i<cadenas;i++){
printf("Cadena %i  %c%c>>  %s\n\t\t",i+1,205,205,string[i]);    
}
printf("\n\n\t\t\t\t\t\tPulse enter para continuar... ");getchar();
system("cls");
printf("\n\n\t\tAhora que deseas hacer?...\n\n\t");
printf("a)  Volver al menu\n\tb)  Volver a ver cadenas\n\tc)  Salir\n\t\t\t\t\t\t\t\t%c%c>> ",205,205);fflush(stdin);gets (eleccion);
if(eleccion[0]==97){
menu_inicio();  
}
else{
if(eleccion[0]==98){
mostrar();  
}   
else{
if(eleccion[0]==99){
exit(-1);   
}   
}
}
}```

MrDave1999
  • 7,491
  • 1
  • 7
  • 22
Santiago
  • 19
  • 5
  • 1
    3 cosas: 1 pregunta por publicación, por favor. ¿ De que error hablas ? ¿ O hay que ejecutar el código para comprobarlo ? y por último, revisa [¿ Por qué es considerado una mala práctica utilizar variables globales ?](https://es.stackoverflow.com/questions/29177/por-qu%c3%a9-es-considerado-una-mala-pr%c3%a1ctica-utilizar-variables-globales?r=SearchResults&s=6|74.0182). Bienvenido al sitio. – Juanjo Feb 11 '21 at 19:03
  • ¿Cuál sería el error? ¿Te muestra alguna advertencia o mensaje de error? – Mauricio Contreras Feb 11 '21 at 19:09
  • @MauricioContreras no, simplemente justo después de almacenar la 4ta cadena el programa simplemente explota... – Santiago Feb 11 '21 at 19:29
  • ¿Cómo sabes que explota? Algún error o mensaje debe arrojar. De lo contrario es como decir que una bomba explota pero no produce ruido ni estropicio. ¿O es que se queda en un bucle infinito? – Mauricio Contreras Feb 11 '21 at 20:27
  • @MauricioContreras me retorna 3221225477, sin embargo el compilador no me avisa de ningún error antes de ejecutar – Santiago Feb 11 '21 at 21:12
  • el compilador no revisa errores en tiempo de ejecucion... – gbianchi Feb 11 '21 at 21:17
  • Me refiero a que no me da ningún error de sintaxis... – Santiago Feb 11 '21 at 21:58
  • Mucho código y de paso todo está desordenado. Como recomendación: **siempre aplica sangría al código para que se vea mas legible** y por cierto, en la pregunta solo debes agregar el código donde está el problema. – MrDave1999 Feb 11 '21 at 23:57
  • Con respecto a lo otro, **1.** la función `gets` es obsoleta e insegura, puesto es propenso a que ocurra desbordamiento de búfer y esto ya es un comportamiento indefinido. Para pedir cadenas de forma segura, usa la función `fgets` puesto te permite especificar el tamaño del array, por lo que nunca podría ocurrir un desborde. **2.** La función `fflush` no acepta como argumento un `stdin`, es incorrecto y si lo haces, es comportamiento indefinido. En este [enlace](https://es.stackoverflow.com/a/233448/105299) puedes encontrar como es la manera estándar de limpiar el buffer `stdin` en C. – MrDave1999 Feb 12 '21 at 00:01
  • Entonces para resumir... El gets() es ineficiente ya que dependes de la cantidad de caracteres que introduzca el usuario, digamos que no lo interesa la que el usuario introduzca simplemente lo toma y lo almacena en la variable (arreglo para ser exactos) que le indiques independientemente de la capacidad del mismo. Por otro lado, con el fgets() no limitas lo que el usuario introduce sino lo que se va a tomar de lo que introduce, con lo que te aseguras de al menos no se desborde nada en la memoria, cierto? – Santiago Feb 12 '21 at 00:49
  • @Santiago. No es asunto de eficiencia, si no de seguridad. `gets` no valida que haya espacio reservado para lo ingresado. `fgets` si valida que no ingreses más de la capacidad reservada. – Candid Moe Feb 12 '21 at 07:10
  • @Santiago Te escribí una respuesta, espero te sirva y de paso cambié el título de tu pregunta para que sea mas rápida su búsqueda. – MrDave1999 Feb 12 '21 at 15:37

1 Answers1

2

Citado de Wikipedia:

Removal of the gets function (in favor of safer fgets), which was deprecated in the previous C language standard revision, ISO/IEC 9899:1999/Cor.3:2007(E).

La función gets fue eliminada en el estándar C11, esto significa que si usas esta función, puede que no funcione en un compilador que siga las normas de C11, por lo que tu código perdería portabilidad. Entonces por razones de portabilidad no deberías usar esta función, sino, fgets.

Ahora, veamos un ejemplo práctico donde se visualice la falencia de esta función:

char text[6];
gets(text);

Imagínate que ingreso por teclado la siguiente cadena: Hello!. En la memoria tendríamos la cadena guardada de esta manera (es solo un ejemplo):

          //--------- array text ---------//
Indice:    (0)   (1)   (2)   (3)   (4)   (5)   (6)
Direccion: 0x08  0x09  0x0A  0x0B  0x0C  0x0D  0x0E
            H     e     l     l     o     !     \0

Como verás, la función gets lee en el búfer de entrada hasta encontrar un salto de linea, por lo que no tiene idea de cual es el tamaño del array text. En este ejemplo esta función provoca un desbordamiento de búfer.

Los índices válidos del array text va de 0 hasta 5 y como toda cadena termina con un caracter nulo (que indica el fin de la cadena), ha desbordado el array (se paso de su límite).

Tal como ves arriba, el caracter nulo terminó en la dirección 0x0E y aquí el programa se puede comportar de varias maneras:

  • Si la dirección 0x0E le pertenece al programa, puede que estemos sobrescribiendo la memoria del programa y esto es una barbaridad, solo imagínate que en la dirección 0x0E tienes guardado una dirección de memoria que hace referencia al primer nodo de una lista enlazada... Se perdería dicha dirección y ahora como accedemos después a la lista? Esto sería difícil de depurar, yo lo veo difícil.

  • No sabemos si la dirección 0x0E es solo de lectura (pero si le pertenece a tu programa) y si lo fuera, el sistema operativo no dejará que escribas en ella, por lo que tu programa abortará.

  • La dirección 0x0E puede que no le pertenezca a tu programa, el sistema operativo no permitirá que escribas en ella, por lo que tu programa dejará de funcionar.

Como sabrás, tu programa podría pasarle cualquier cosa y todo por causa de un simple desbordamiento de búfer y recalco que esto es un comportamiento indefinido (tu programa puede que funcione o puede que no).

En cambio, si hubiera usado la función fgets:

char text[6];
fgets(text, 6, stdin);

El cuento sería otro. Nunca sucederá un desborde, puesto fgets ya sabe cuantos caracteres debe leer (y también incluye el caracter nulo).

Conclusión: Por razones de portabilidad y seguridad, no uses gets, sino, fgets.

Observaciones:

  • La función fflush no acepta como argumento un stdin. No limpia el búfer de entrada.

La forma estándar que conozco es de esta manera:

//Limpia el buffer stdin.
int ch;
while((ch = getchar()) != '\n' && ch != EOF);
MrDave1999
  • 7,491
  • 1
  • 7
  • 22