Publi

Por qué no debemos utilizar gets()

getsA veces me sorprendo (como profesor de programación) de que en muchos sitios siguen enseñando la función gets() para la entrada de datos desde teclado sin explicar lo que puede pasar.

gets() es una función peligrosa. Imaginemos que escribimos el siguiente programa:

1
2
3
4
5
6
7
8
9
10
11
12
#include <stdio.h>

int main()
{
  char cadena2[10];
  char cadena1[10];

  printf("c1: %x\nc2: %x\n", cadena1, cadena2);
  gets(cadena1);

  printf("Cadena 2: %s\n", cadena2);
}

Ahora la compilamos, ejecutamos e introducimos un texto de prueba:

$ gcc -o test3 test3.c
/tmp/ccK2P2ON.o: In function `main’:
test3.c:(.text+0x32): warning: the `gets’ function is dangerous and should not be used.
$ ./test3
c1: bfc81580
c2: bfc8158a
Escribo un texto de mas de 10 caracteres
Cadena 2: texto de mas de 10 caracteres
Violación de segmento

Ya de primeras nos avisa gcc de que la función no debería ser usada, a lo que muchos se sorprenden.
Luego, en la ejecución, tal como se indica en el código fuente, mostramos las direcciónes de memoria donde se ubicarán cadena1 (c1) y cadena2 (c2).
Tras ello escribimos: «Escribo un texto de mas de 10 caracteres«, que se introducirá en la variable cadena1 y mostramos la variable cadena2, que muestra parte de lo que escribimos antes… y además de regalo una violación de segmento, porque nuestro programa ha terminado accediendo a una región de memoria que no le pertenecía.

Todo esto sucede porque gets() no tiene ninguna manera de saber cuánto puede escribir (recordemos que cuando trabajamos con Arrays en C, sólo pasamos la dirección de memoria del primer elemento y no el tamaño) y por ello, escribirá todo lo que pulsemos por teclado en la variable, sin preocuparnos de si tengo sitio o no (por eso en el ejemplo de arriba, aunque hemos pedido cadena1, terminamos escribiendo en cadena2). Con esto quiero decir que podemos usar gets() para hacer una prueba de concepto rápida, cuando tengamos el entorno controlado, aunque nunca para un programa real, de todas formas no debemos darlo todo por perdido, tenemos una excelente función, muy parecida fgets(), aunque tiene más parámetros:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <stdio.h>

#define MAX_LON_CADENA 10

int main()
{
  char cadena2[MAX_LON_CADENA];
  char cadena1[MAX_LON_CADENA];

  printf("c1: %x\nc2: %x\n", cadena1, cadena2);

/*   gets(cadena1); */
  fgets(cadena1, MAX_LON_CADENA, stdin);
  printf("Cadena 1: "%s"\n", cadena1);
  printf("Cadena 2: %s\n", cadena2);
}
  1. El primer parámetro de fgets() es dónde vamos a almacenar lo leído. Es decir, nuestra cadena de caracteres.
  2. El segundo parámetro es cuántos caracteres leeremos como máximo (incluyendo el terminador de cadena). Y si hemos definido un valor máximo para la cadena, podemos utilizar éste. Aquí le decimos a fgets() que todo lo que sobrepase este tamaño lo ignore.
  3. El tercero es de dónde lo leemos, y si queremos hacerlo desde teclado, usaremos stdin. Esta función puede servir también para leer contenidos de un fichero o un dispositivo.

Actualización 10/11/2015: He cambiado algunas palabras y he extendido un poco más el post (pero muy poco)y he añadido una imagen de cabecera.

También podría interesarte....

There are 7 comments left Ir a comentario

  1. Pingback: Bitacoras.com /

  2. Fabricio /
    Usando Google Chrome Google Chrome 5.0.375.55 en Linux Linux

    Muchas gracias, muy útil información 😉

  3. Pingback: Tweets that mention Poesía binaria » Por qué no debemos utilizar gets() — Topsy.com /

  4. Edgardo /
    Usando Mozilla Firefox Mozilla Firefox 3.6.13 en Ubuntu Linux Ubuntu Linux

    Gracias, muy útil informacion y muy buena forma de explicarlo.

    1. admin / Post Author
      Usando Mozilla Firefox Mozilla Firefox 4.0 en Linux Linux

      Gracias a ti!

  5. DVD /
    Usando Mozilla Firefox Mozilla Firefox 70.0 en Linux Linux

    En el segundo parámetro lo mejor no es poner el tamaño máximo de la variable cadena que vamos a leer, sino poner un tamaño enorme para limpiar el búffer.
    Actualmente sí se está evitando que al escribir cadena1 se sobrepase, escribiendo la memoria que le sigue (con la variable que le sigue, en éste caso cadena2). Pero el búffer queda lleno y si luego hacemos otra lectura (por ejemplo leer cadena2), automáticamente se llenará con lo que quedaba en el búffer, es decir que sólo retrasamos el mismo problema.
    Repito, corríjanme si estoy mal.

    1. DVD /
      Usando Mozilla Firefox Mozilla Firefox 70.0 en Linux Linux

      Por cierto, buen artículo. Ciertamente abundan tutoriales, libros e intrucciones donde se siguen enseñando el uso de funciones que no deberían usarse, o que nisiquiera son estándar y luego si tiene suerte es que uno se entrera de los problemas. Y en las ocaciones en que se ven «sermones» de lo que no debe hacerse, rara vez se ve explicada una alternativa.

      Ojalá hayan más artículos como éste, con respecto a ésos otros problemas (por ejemplo, que se me venan a la mente: clrscr, fflush, gets).

Leave a Reply to admin Cancle Reply