Подтвердить что ты не робот

Поскольку комитет Standard C не стандартизовал простую замену gets(), что это должно быть?

Функция gets была сначала устарела на C99 и, наконец, удалена в C11. Однако в C-библиотеке нет прямой замены.

fgets() не является замещающей заменой, поскольку он не разделяет окончательный '\n', который может отсутствовать в конце файла. Многие программисты тоже ошибаются.

Существует один слой для удаления строки: buf[strcspn(buf, "\n")] = '\0';, но это нетривиально и часто вызывает объяснение. Это может быть и неэффективным.

Это контрпродуктивно. Многие новички по-прежнему используют gets(), потому что их учителя хромают или их устаревшие устаревшие.

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

Как BSD, так и GNU libc имеют getline, стандартизованный в POSIX, который выделяет или перераспределяет буфер через realloc...

Каков наилучший способ научить новичков об этом беспорядке?

4b9b3361

Ответ 1

Характер вопроса таков, что будут спекуляции и мнения. Но мы могли найти некоторую информацию из соображений C99 и стандарта C11.

Обоснование C99, когда gets() было устаревшим, говорится о следующей причине для его обесценивания:

Поскольку получение не проверяет переполнение буфера, оно обычно небезопасно для использования, когда его вход не находится под контролем программистов. Это вызвало у некоторых вопрос о том, должно ли оно появляться в все. Комитет решил, что получение было полезным и удобным в те особые обстоятельства, когда у программиста есть адекватные контроля над входными данными, и как многолетняя существующая практика, требуется стандартная спецификация. В целом, однако, предпочтительный функция - fgets (см. §7.19.7.2).

Я не думаю, что gets_s() можно рассматривать как альтернативу. Потому что gets_s() является дополнительным интерфейсом. C11 фактически рекомендует fgets() над gets_s():

§K.3.5.4.1, проект C11

Функция fgets позволяет правильно написанным программам безопасно обрабатывать слишком строгие строки ввода для хранения в массиве результатов. В целом это требует, чтобы вызывающие лица из fgets обращали внимание на присутствие или отсутствие символа новой строки в массиве результатов. Рассмотрите возможность использования fgets (наряду с любой необходимой обработкой на основе символов новой строки) вместо gets_s.

Таким образом, мы оставим нас с fgets() как единственную реальную замену для gets() в ISO C. fgets() эквивалентно gets(), за исключением того, что он будет читать в новой строке, если есть буферное пространство. Так стоит ли вводить новый интерфейс, который имеет незначительное улучшение по сравнению с давним и широко используемым (fgets())? ИМО, №.

Кроме того, многие приложения реального мира не ограничиваются только ISO C. Таким образом, есть возможность использовать расширения и POSIX getline() и т.д. В качестве замены.

Если вам нужно найти решение для записи в ISO C, то в любом случае довольно легко написать оболочку вокруг fgets(), например my_fgets(), которая удалит новую строку, если она есть.

Конечно, преподавание fgets() для новичков предполагает объяснение потенциальной проблемы новой строки. Но ИМО, это не так сложно понять, и кто-то, кто собирается учиться, должен уметь быстро схватывать его. Это (поиск последнего символа и замена его, если он является символом "X" ), можно даже считать хорошим упражнением для новичка.

Таким образом, в свете вышеизложенных причин, я бы сказал, что нет необходимости подавать новую функцию в ISO C как истинную замену gets().

Ответ 2

Этот вопрос во многом требует спекуляции, не содержащей цитаты из протоколов комиссии или чего-то еще, но в качестве общего принципа комитет (РГ14) обычно избегает изобретать новые интерфейсы и предпочитает документировать и делать строгую существующую практику (например, snprintf, long long, типы inttypes.h и т.д.), а иногда и другие определения стандартов/интерфейсов вне C (например, сложная математика из плавающей точки IEEE, атомная модель из С++ и т.д.). gets не имеет такой замены для принятия, вероятно, потому что fgets обычно считается превосходным (он не потерян, когда файл заканчивается без новой строки). Если вам действительно нужна прямая замена, что-то вроде этого работает:

char buf[100];
scanf("%99[^\n]%*1[\n]", buf);

Конечно, это klunky для использования, особенно когда размер буфера является переменным.

Ответ 3

IMO, любая замена должна будет передать size, а также пункт назначения char *, требующий изменения кода, которые в значительной степени зависят от каждого случая. Совместимость с одним размером не считалась возможной, так как size часто теряется/не проходит по временному коду до gets(). Учитывая, что у нас было 12-летнее предупреждение (C99 по C11), подозревайте, что комитет считает, что проблема не исчезнет к 2011 году.

Ха!

Комитет Standard C должен был сделать замену, которая также была передана в размере места назначения. Как и следующее. (это, вероятно, имеет проблему столкновения имен)

char *gets_replacement(char *s, size_t size);

Я попытался заменить fgets() на замену, которая использует VLA (необязательно в C11)

char *my_gets(char *dest, size_t size) {
  // +2 one for \n and 1 to detect overrun
  char buf[size + 2];

  if (fgets(buf, sizeof buf, stdin) == NULL) {
    // improve error handling - see below comment
    if (size > 0) {
      *buf = '\0';
    }
    return NULL;
  }
  size_t len = strlen(buf);
  if (len > 0 && buf[len - 1] == '\n') {
    buf[--len] = '\0';
  }

  // If input would have overrun the original gets()
  if (len >= size) {
    // or call error handler
    if (size > 0) {
      *buf = '\0';
    }
    return NULL;  
  }
  return memcpy(dest, buf, len + 1);
}