Как использовать fgets (), чтобы избежать приведения второго аргумента типа int?

декларация

4 ответов


#include <stdio.h>
char *fgets(char * restrict s, int n, FILE * restrict stream);

С fgets() входной буфер s[INT_MAX] и за его пределами нельзя использовать.


ОП size_t len код может избежать int литой len путем преобразования в строку и преобразовать обратно в int, это просто расточительно. Гипс-это правильно.

вместо того, чтобы засорять код с (int), уменьшите / контролируйте свое использование и оберните бросание в ограничиваясь вспомогательной функции.

int fgets_len(size_t len) {
  return (len < INT_MAX) ? (int) len : INT_MAX;
}


size_t len = something_big;
char *arr = malloc(len);

...   
if ( fgets(arr, fgets_len(len), stdin) == NULL){
    printf("Error, Fgets\n");
    exit(1);
}else{
    printf("Arr = '%s'\n", arr);
}

если код действительно должен читать долго строки, считать ssize_t getline(char **lineptr, size_t *n, FILE *stream); как определено здесь. Обратите внимание, что это нестандартная функция библиотеки C, но ее исходный код легко доступен.


относительно педантичного использования fgets(), есть по крайней мере 2 причины для fgets() вернуться NULL: ошибка конца файла и ввода. Теперь рассмотрим угловой случай с использованием кода OP

size_t len = strlen("") + 1;
char arr[len];
if ( fgets(arr, (int)len, stdin) == NULL){

и патологические случаи вроде

if ( fgets(arr, 0, stdin) == NULL){
if ( fgets(arr, -1, stdin) == NULL){

оба обсуждаются на будет fgets() возвращает значение null с коротким уступчивый bufffer?


если вы хотите избежать проблемы переполнения целого числа, вы можете сделать проверку значения и действовать соответственно. Обратите внимание, что, так как strlen() возвращает значение типа size_t у вас есть 2 варианта. Либо объявите переменную типа int и присвоить возвращаемое значение strlen(), которая будет выполнять неявное преобразование из типа size_t to int, или cast, как вы сделали в вас вызов функции.

вот возможное решение:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h>

int main(void)
{
    const char *buffer = "Michi";
    //size_t len = strlen(buffer) + 1;
    size_t len = 9999999999;

    if (len > INT_MAX) {
        fprintf(stderr, "Error, length exceeded desired value.Aborting...\n");
        return 1;
    }

    char arr[len];

    printf("Type your Input:> ");
    if (fgets(arr, (int)len, stdin) == NULL) {
        printf("Error, Fgets\n");
        exit(1);
    }
    else {
        printf("Arr = %s\n", arr);
    }
    return 0;
}

я бы не стал связывать размерность аргумента с fgets() С размером buffer на всех.

вместо этого я гарантировать, что буфер используется для чтения имеет длину, которая может быть представлена с помощью int (скажем, не превышает INT_MAX). Если вы хотите быть действительно портативным, убедитесь, что длина буфера не превышает 32767 (стандарт указывает, что минимально допустимое значение INT_MAX is 32767).

тогда используйте этот факт это fgets() будет считывать строку по частям, если длина строки превышает длину буфера.

например, предполагая, что len превышает длину любой строки читаются из stdin;

char arr[len] = {0};   
char read_buffer[10];    /*   I'm reasonably confident that 10 < 32767 */

while (fgets(read_buffer, 10, stdin) != NULL)
{
      size_t read_length = strlen(read_buffer);
      if (read_length > 0)
      {
           if (read_buffer[read_length-1] != `\n`)
           {
               strcat(arr, read_buffer);
           }
           else
           {
               strncat(arr, read_buffer, read_length-1);

               printf("Arr = %s\n", arr);

               arr[0] = '';    /*  clear arr so next line may be read */

               /*  break here if want to stop reading after the first line */

           }
      }

обратите внимание, что, если конец файла не предшествует '\n', то выше будет отбрасывать текст после последнего '\n'.

в вышеуказанном, заменяя fgets(read_buffer, 10, stdin) С fgets(read_buffer, sizeof read_buffer, stdin) безопасно, так как size_t со значением меньше или равно INT_MAX всегда можно безопасно преобразовать в int. Итак, если вы хотите закрыть компилятор от выдачи предупреждений, Вы можете безопасно бросить т. е. fgets(read_buffer, (int)(sizeof read_buffer), stdin)


проект говорит, что

результат преобразования целого в более короткое целое число, или результат преобразования целого числа без знака целое число со знаком из равной длины, если значение не может быть представлено

реализация-определено. В результате, если вы хотите безопасно преобразовать значение типа unsigned в signed - вы должны убедиться, что все возможные исходные значения могут быть представлены целевым типом signed.