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 java o kotlin ), 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
Responder
-
El código puede leer demasiado:
temp = fgetc( stdin ); if (temp == EOF || temp = "\n") return GETS_S_OK;
Si
temp
no esEOF
ni\n
, el personaje está perdido. Será mejor que loungetc()
. -
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 usefgets()
. 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 imitargets()
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 quegets
está roto en más de una forma.getline
más o menos los resuelve todos.
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
.'\n' == temp
– de esa manera, si escriben==
en=
, obtienes un error mucho más definido