Estou em uma aula que usa C e meu instrutor infelizmente usou gets() no código de amostra.

Como isso é obviamente um descuido hediondo, provavelmente causará comportamento indefinido e outros vários problemas (apenas um pouco sarcasmo), decidi implementar gets_s() , porque foi um exercício divertido e às vezes não vale a pena para fazer a verificação completa de erros com fgets() e você só deseja truncar linhas inesperadamente longas.

Não estou preocupado se isso implementa totalmente gets_s() conforme especificado no padrão C11 – isso é apenas um substituto imediato para gets() que não sobrecarrega seu buffer.

No entanto, o que é muito importante é que esta função realmente faz o que anuncia: é segura e não sobrecarrega o buffer.

Este é o meu fi pela primeira vez trabalhando em C (geralmente uso ou ), e agradeço todos dicas, embora eu “queira pelo menos alguma menção à segurança deste código e também esteja interessado na portabilidade (para compiladores atuais).

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; } 

e um pequeno arquivo de teste:

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; } 

Compila com sucesso com gcc -Wall -Wextra -Wpedantic -Werror gets_s.c, de modo que “sa plus.

Resposta

Primeiro, não chame isso de gets_s, pois a assinatura e o comportamento diferem de algumas maneiras sutis e não tão sutis, que apenas leva à confusão e frustração. De qualquer forma, você realmente não iria querer o gets_s -contrato.
Chame-o de algo descritivo como getline_truncated.

Você sabia que n <= 0 é UB em sua implementação?

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

Tenho certeza de que seu compilador avisa sobre o erro de digitação acima.
Ou você não pergunta para todos os avisos (-Wall -Wextra -std=...)?

Eu sugiro redefinindo e renomeando seus códigos de retorno para permitir um melhor teste de resultados:

#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 

Desta forma, você pode testar ==0 ou >=0 dependendo do que você decidir ser “sucesso”, respectivamente !=0 ou <0 para “falha”.

Comentários

  • O temp = "\n" é um erro de digitação neste post apenas; originalmente escrevi o código em uma máquina separada e depois o redigitei para a pergunta. Eu ' tenho certeza de que fazia parte do meu código, uld errou (conforme compilei com -Wall -Wextra -Wpedantic -Werror.
  • @ CAD97 É por isso que muitos programadores C que valorizam correção e segurança teriam escrito '\n' == temp – dessa forma, se eles digitarem == a =, você obterá um erro muito mais definitivo

Resposta

  • O código pode ler muito:

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

    Se temp não for EOF nem \n , o personagem está perdido. É melhor ungetc() isso.

  • O código ultrapassa o mandato. O loop

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

    garante que o chamador nunca verá uma string vazia. Às vezes, strings vazias são semanticamente significativas.

  • man fgets:

    As funções fgets () e gets () não distinguem entre final de arquivo e erro, e os chamadores devem usar feof ( 3) e ferror (3) para determinar o que ocorreu.

    Você está em uma ótima posição para fazer exatamente isso. Em vez de retornar cegamente GETS_S_ERROR, determine o que aconteceu e retorne de acordo. Por exemplo, #define GETS_S_EOF 3.

Comentários

  • Eu ' m imitando gets() em que eu ' estou lendo até o fim da linha, a ideia é que isso ' é o que alguém que teria usado deseja. Se você deseja o comportamento de parar em n-menos um caracteres, basta usar fgets(). Ou, pelo menos, esse foi o meu motivo para escolher esse comportamento. (Se você quiser dizer que ' estou lendo demais, diga por que você escolheria esse comportamento explicitamente. Decidi continuar lendo para imitar gets() se não ' não escreveu além do seu buffer.)
  • @ CAD97 Eu entendo que sua objeção aborda meu primeiro ponto. Seu código realmente imita gets, e realmente não ' t sobrecarrega o buffer.Meu ponto é que gets está quebrado em mais de uma maneira. getline mais ou menos resolve todos eles.

Deixe uma resposta

O seu endereço de email não será publicado. Campos obrigatórios marcados com *