Отдых кошки в C

У меня есть школьный проект для создания программы, которая работает так же, как Linux cat. Код работает, но мне просто нужно выяснить, где я могу улучшить его или обработать ошибки. Программа имеет 3 файла .c и 1 файл заголовка. Мне также не разрешено использовать системные функции; Я должен использовать read(), close(), open() и write() и while.

ft_read.c

#include "ft_read_file.h"

 int    ft_len(char *str)
 {
     int    i;

     i = 0;
     while (str[i] != '\0')
     i++;
     return (i);
  }

  void  ft_error(char *file_name)
  {
       ft_writeln(1, file_name, ft_len(file_name));               
       ft_writeln(1, ": ", 2);
       ft_writeln(1, file_name, ft_len(file_name));
       ft_writeln(1, ":", 1);
       ft_writeln(1, " No such file or directory", 26);
       ft_writeln(1, ".\n", 2);
   }

   int  ft_read(int files, char **file_name)
   {
       int  fd;
       int  i;
       char buf[4096];
       int  size;

       fd = 1;
       i = 1;
       size = 0;
       while (i < files)
       {
           fd = open(file_name[i], O_RDONLY);
           if (fd == -1)
           {
               ft_error(file_name[i]);
                return (1);
            }
             size = read(fd, buf, 4095);
             buf[4095] = '\0';
             ft_writeln(1, buf, size);
             close(fd);
             i++;
          }
          return (0);
     }

main.c

#include "ft_read_file.h"

int main(int argc, char **argv)
{
    if (argc >= 1)
        ft_read(argc, argv);
    return (0);
 }

ft_writeln.c

#include "ft_read_file.h"

void    ft_writeln(int fd, void *buf, int size)
{
       write(fd, buf, size);
}
11 голосов | спросил julekgwa 15 AMpFri, 15 Apr 2016 02:41:40 +030041Friday 2016, 02:41:40

1 ответ


8

Используйте стандартную библиотеку C

Ваш код может быть значительно упрощен, если вы использовали стандартные библиотечные функции C fopen , fclose , fread , fwrite и fprintf вместо низкоуровневых системных вызовов POSIX. Стандартная библиотека также обеспечивает strlen , который убирает вашу функцию ft_len, но если вы используете функции, упомянутые ранее, вам не нужно будет подсчитывать в любом случае длина строки. Если эти стандартные функции библиотеки верботены в вашем задании, укажите это в своем вопросе.

Будьте готовы к read и write вернуться раньше

read и write не гарантированно прочитал или написал все байты, на которые они были запрошены. Поэтому надлежащий способ их использования заключается в том, чтобы вызвать их в цикле, пока не будут прочитаны или записаны все байты или ошибка встречается.

Будьте готовы к read и write сбой

read и write может не работать в этом случае они return отрицательное значение и установите errno , чтобы узнать, почему операция не удалась. Если вы не можете узнать причину, сообщить общую ошибку, например «Не удается открыть файл ... для чтения», все же лучше, чем угадать потенциально неправильную причину. Сообщение о неправильной причине хуже, чем отсутствие каких-либо причин, потому что это приведет к обману ваших пользователей.

read все необходимые заголовки

Я не вижу никаких кодов read s в вашем коде. Ваш код зависит от следующих заголовков.

  • '\0' для buf[size] = '\0';, open, errno и #include
  • #include и <unistd.h> для соответствующих флагов

Если у вас есть open d в файле заголовка, который вы не показывали: не делайте этого. Какие функции необходимы для реализации, это детализация реализации. Заголовки должны открывать публичный API вашего модуля.

Использовать соответствующие типы

Длина массива должна быть измерена в read (определяется в write) вместо close. Тип <sys/stat.h> <fcntl.h> и #include есть size_t.

Избегайте неинициализированных переменных

Это не в 1980-х годах, где нам приходилось писать такой код.

<stddef.h>

Вместо этого мы можем написать это.

int

Чем больше вы ограничиваете область действия переменной, тем меньше риск для ее неправильного использования.

Переменные return и read может быть объявлено только внутри тела цикла. write может быть объявлен ssize_t then.

int i;
// …
i = 1;
// …
while (i < files)
  {
    // …
    ++i;
  }

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

Избегайте магических чисел

Ваш выбор для размера буфера 4096 является разумным, но все же произвольным. В один прекрасный день вы можете выбрать другой размер. Тем не менее, константные или, что еще хуже, константы, полученные из него, которые вы не можете найти, повторяются несколько раз в коде. Вместо жесткого кодирования таких магических чисел используйте именованную константу.

for (int i = 1; i < files; ++i)
  {
    // …
  }

Вы также можете использовать size.

Так как C теперь поддерживает массивы переменной длины, вы также можете использовать переменную fd вместо макроса fd

Вычитание 1 действительно не требуется, поскольку окончание NUL не требуется, как указано выше. Но когда вам нужно получить размер, использование такого выражения автоматически обновляет зависимое значение, если значение зависит от изменений.

Линия

const

- еще один пример, который ждет для кого-то, чтобы изменить текст и забыть обновить номер. Даже не ясно, что означает 26.

Чтобы дополнительно уменьшить количество магических констант, рассмотрите возможность использования макросов const int fd = open( … ); , #define BUFFER_SIZE 4096 char buffer[BUFFER_SIZE]; const ssize_t count = read(fd, buffer, BUFFER_SIZE - 1); и sizeof(buffer) , которые const d в #define до 1, 2 и 0 соответственно. Преимущество заключается не столько в том, что эти константы будут меняться в ближайшее время, либо люди не знают, к чему относится дескриптор файла 1, а в вызове функции

ft_writeln(1, " No such file or directory", 26);

STDOUT_FILENO может означать почти все, а в

STDERR_FILENO

смысл первого аргумента гораздо яснее.

Печатать сообщения об ошибках в стандартный вывод ошибки

Программы должны выводить свои результаты на стандартный вывод и любые записи, предупреждения или сообщения об ошибках на стандартный вывод ошибки. Одной из причин этого является то, что стандартный вывод часто перенаправляется. Если я выполняю

$ cat bar.c baz.c> foo.c

весь стандартный вывод перейдет к STDIN_FILENO, но я все еще хочу видеть сообщения об ошибках. (И даже если я не хочу их видеть, я, конечно, не хочу, чтобы они попадали в #define.)

Отдельные проблемы

Аргументы, переданные в <unistd.h>, включают имя программы как print(1, "hello, world\n"); . Это нормально, но ваша функция 1 действительно не заботится о том, откуда пришли аргументы. Вместо этого вызов в print(STDOUT_FILENO, "hello, world\n"); должен передавать только соответствующие аргументы

foo.c

и foo.c должны учитывать все элементы (начиная с индекса 0) массива в качестве имен файлов для работы.

Сообщить об ошибках полностью назад

Если вашпрограмма терпит неудачу в любой момент, она должна сообщить об этом через свой статус выхода. Поэтому, если вы столкнулись с ошибкой, которую невозможно восстановить, main должен argv[0] ft_read (который является main d в ft_read(argc - 1, argv + 1); ) или небольшое положительное целое число.

Структурируйте код таким образом, чтобы минимизировать неожиданности

Вы разделили логику реализации из файла, который содержит функцию ft_read. Это хорошо, потому что позволяет проводить тестирование модулей и повторное использование кода. Но на самом деле нет веской причины поместить функцию main в отдельный файл. На самом деле, почему у вас есть эта функция вообще?

Объявлять функции return, если они не должны быть внешне видимыми

Если функция используется только в блоке перевода (файл EXIT_FAILURE), который определяет его, объявите его #define. Это сделает его недоступным из других единиц перевода и, следовательно, улучшит инкапсуляцию и предотвратит загрязнение пространства имен. Это также может помочь повысить производительность.

Используйте имена, которые правильно отражают функцию, выполняемую

<stdlib.h> звучит так, как если бы функция записывала строку. Но на самом деле это не волнует линии. Он просто записывает байты.

Не путайте main с помощью функции

Оператор ft_writeln не является вызовом функции. Вам не нужна скобка вокруг значения, которое должно быть static ed.

Использовать правильный отступ

Ваш код отформатирован правильно в большинстве случаев. Но в некоторых случаях вы забыли отделить тело петли или неуместные фигурные скобки. Нет смысла участвовать в пламенной войне о том, сколько пробелов является правильным выбором, но не может быть никакого спора о том, что вы должны использовать отпечаток some и последовательно использовать один и тот же стиль. Хороший редактор позаботится об этом для вас автоматически.

Прочитайте документацию для *.c

Если ваша цель должна быть совместима с POSIX static , прочитайте его документацию, чтобы узнать, что именно он делает. Реализация всех его опций может быть слишком большой, но интересный аспект ее поведения в настоящее время не отражен вашей версией. Если ft_writeln заданы нулевые аргументы, вместо этого он считывается из стандартного ввода. В этом случае ваша программа просто ничего не делает.

ответил 5gon12eder 15 AMpFri, 15 Apr 2016 07:52:21 +030052Friday 2016, 07:52:21

Похожие вопросы

Популярные теги

security × 330linux × 316macos × 2827 × 268performance × 244command-line × 241sql-server × 235joomla-3.x × 222java × 189c++ × 186windows × 180cisco × 168bash × 158c# × 142gmail × 139arduino-uno × 139javascript × 134ssh × 133seo × 132mysql × 132