Estoy en una clase que usa C y mi instructor tiene desafortunadamente usó gets() en código de muestra.

Como obviamente se trata de un descuido atroz, es probable que cause un comportamiento indefinido y otros problemas varios (solo un poco sarcasmo), decidí implementar gets_s() , porque era un ejercicio divertido y, a veces, simplemente no vale la pena para realizar una verificación completa de errores con fgets() y solo desea truncar líneas inesperadamente largas.

No me preocupa si esto implementa completamente gets_s() como se especifica en el estándar C11; se supone que es un reemplazo directo de gets() que no» sobrepasa el búfer.

Sin embargo, lo que es muy importante es que esta función realmente hace lo que anuncia: es segura y no sobrepasa el búfer.

Esta es mi pregunta la primera vez que trabajo en C (normalmente uso o ), y agradezco todos consejos, aunque me gustaría al menos alguna mención de la seguridad de este código, y también estoy interesado en la portabilidad (para los compiladores actuales).

gets_s.h

#include <stdio.h> #include <string.h> #define GETS_S_OK 0 #define GETS_S_ERROR 1 #define GETS_S_OVERRUN 2 static inline int gets_s( char str[], int n ) { char *str_end, *fgets_return; int temp; fgets_return = fgets( str, n, stdin ); /* If fgets fails, it returns NULL. This includes the case where stdin is exhausted. */ if ( fgets_return == NULL ) { str[0] = "\0"; return GETS_S_ERROR; } str_end = str + strlen(str) - 1; if ( str_end == "\n" ) { *str_end = "\0"; return GETS_S_OK; } temp = fgetc( stdin ); if (temp == EOF || temp = "\n") return GETS_S_OK; do temp = fgetc( stdin ); while ( temp != EOF && temp != "\n" ); return GETS_S_OVERRUN; } 

y un pequeño archivo de prueba:

gets_s.c

#include "gets_s.h" #include <stdio.h> int main() { char buffer[10]; int gets_s_return; printf("Enter up to %d characters safely.\n", sizeof(buffer) - 1); gets_s_return = gets_s( buffer, sizeof(buffer) ); printf("buffer = %s", buffer); printf("gets_s return = %d", gets_s_return); return 0; } 

Se compila correctamente con gcc -Wall -Wextra -Wpedantic -Werror gets_s.c, por lo que «es un plus.

Respuesta

Primero, no lo llames gets_s ya que la firma y el comportamiento difieren en algunas formas sutiles y no tan sutiles, que simplemente conduce a la confusión y la frustración. De todos modos, realmente no querrías el gets_s -contrato.
Llámalo algo descriptivo como getline_truncated.

¿Sabe que n <= 0 es UB en su implementación?

if (temp == EOF || temp = "\n") ^ 

Estoy seguro de que su compilador le advierte sobre el error tipográfico anterior.
¿O no le pide todas las advertencias (-Wall -Wextra -std=...)?

Sugiero redefinir y cambiar el nombre de sus códigos de retorno para permitir mejores resultados de prueba:

#define GETS_S_TRUNCATED 1 // Because truncation is not neccessarily an error #define GETS_S_OK 0 #define GETS_S_ERROR EOF // Because we already have an appropriate negative constant 

De esta manera, puede probar ==0 o >=0 dependiendo de lo que decida es «éxito», respectivamente !=0 o <0 para «falla».

Comentarios

  • El temp = "\n" es un error tipográfico en este solo publicar; originalmente escribí el código en una máquina separada y luego lo reescribí para la pregunta. Estoy ' seguro de que había sido parte de mi código. uld han cometido errores (como compilé con -Wall -Wextra -Wpedantic -Werror.
  • @ CAD97 Esta es la razón por la que muchos programadores en C que valoran la corrección y la seguridad habrían escrito '\n' == temp – de esa manera, si escriben == en =, obtienes un error mucho más definido

Responder

  • El código puede leer demasiado:

     temp = fgetc( stdin ); if (temp == EOF || temp = "\n") return GETS_S_OK; 

    Si temp no es EOF ni \n , el personaje está perdido. Será mejor que lo ungetc().

  • El código sobrepasa el mandato. El bucle

     do temp = fgetc( stdin ); while ( temp != EOF && temp != "\n" ); 

    garantiza que la persona que llama nunca verá una cadena vacía. A veces, las cadenas vacías son semánticamente significativas.

  • man fgets:

    Las funciones fgets () y gets () no distinguen entre final de archivo y error, y las personas que llaman deben usar feof ( 3) y ferror (3) para determinar qué ocurrió.

    Estás en una excelente posición para hacer precisamente eso. En lugar de devolver ciegamente GETS_S_ERROR, determine qué sucedió y regrese en consecuencia. Por ejemplo, #define GETS_S_EOF 3.

Comentarios

  • Yo ' estoy imitando gets() en que ' m leyendo hasta el final de la línea, la idea es que eso ' es lo que alguien que hubiera usado quiere. Si desea el comportamiento de detenerse en n-menos-uno-caracteres, simplemente use fgets(). O al menos, esa fue mi razón para elegir ese comportamiento. (Si quieres decir que ' leo demasiado, di por qué elegirías ese comportamiento explícitamente. Elegí seguir leyendo para imitar gets() si no ' t escribió más allá de su búfer.)
  • @ CAD97 Entiendo que su objeción se refiere a mi primer punto. De hecho, su código imita gets, y de hecho no ' t desborda el búfer.Mi punto es que gets está roto en más de una forma. getline más o menos los resuelve todos.

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *