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

c casting

613 просмотра

4 ответа

3345 Репутация автора

Объявление fgetsфункции выглядит так:

char *fgets(char *str, int n, FILE *stream);

Это означает, что второй аргумент должен быть int.

Как правильно избежать этого приведения в следующей программе?

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

int main(void) {
    const char *buffer = "Michi";
    size_t len = strlen(buffer) + 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);
    }
}

Здесь я использовал, (int)lenкоторый выглядит хорошо, но что произойдет, если bufferхранит очень длинную строку?

Скажем так:

const char *buffer = "Very long long ..."; /* where length is beyond the range of the `int` type */

Я уже объявил длину типа, size_tчто хорошо здесь, но если я передам это, fgetsне в порядке из-за:

conversion to ‘int’ from ‘size_t {aka long unsigned int}’ may alter its value

и если я приведу его к intне подходит, потому что размер intменьше, чем размер, lengthи некоторая информация будет потеряна.

Может быть, я что-то здесь упускаю ...

В любом случае, как мне избежать этой ситуации?

Автор: Michi Источник Размещён: 18.07.2016 10:39

Ответы (4)


2 плюса

0 Репутация автора

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

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

#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;
}
Автор: user6274708 Размещён: 18.07.2016 11:06

0 плюса

6850 Репутация автора

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

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

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

Автор: Sergio Размещён: 18.07.2016 11:14

1 плюс

29724 Репутация автора

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

Вместо этого я бы гарантировал, что буфер, используемый для чтения, имеет длину, которая может быть представлена ​​с помощью int (скажем, не превышает INT_MAX). Если вы хотите быть действительно переносимым, убедитесь, что длина буфера не превышает 32767(стандарт указывает, что минимально допустимое значение INT_MAXравно 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] = '\0';    /*  clear arr so next line may be read */

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

           }
      }

Обратите внимание, что если перед концом файла не стоит сразу после a '\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)

Автор: Peter Размещён: 18.07.2016 11:43

5 плюса

89988 Репутация автора

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

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


size_t lenКод OP мог бы избежать intприведения lenпутем преобразования в строку и преобразования обратно в int, что просто расточительно. В ролях это правильно.

Вместо того, чтобы засорять код с помощью a (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(), есть как минимум две причины для fgets()возврата NULL: конец файла и ошибка ввода. Теперь рассмотрим угловой случай, используя код ОП

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 с коротким буфером, совместимым?

Автор: chux - Reinstate Monica Размещён: 18.07.2016 01:14
Вопросы из категории :
32x32