Может быть, самая полезная функция для вас:
#include <stdio.h>
#include <stdarg.h>
extern void error_exit(int rc, const char *format, ...); /* In a header */
void error_exit(int rc, const char *format, ...)
{
va_list args;
va_start(args, format);
vfprintf(stderr, format, args);
va_end(args);
exit(rc);
}
Вы можете написать:
if (stat(argv[2], &inode) != -1)
error_exit(2, "Warning: The file %s exists. Not going to overwrite\n",
argv[2]);
Что имеет смысл краткости.
Вы пишете функции для выполнения подзадач. Решить, где разбить ваш код на функции, сложно - столько же, сколько наука. Ваш код не настолько велик, что просто ужасно оставлять его как есть - одну функцию (хотя обработку ошибок можно упростить, как указано выше).
Если вы хотите попрактиковаться в написании функций, рассмотрите возможность его разделения:
open_input_file()
open_output_file()
checked_read()
checked_write()
checked_close()
Эти функции позволят записать ваш основной код в виде:
int main(int argc, char **argv)
{
int bytes_read;
int input_fd, output_fd;
char buffer[64];
if (argc != 3)
error_exit(1, "Usage: %s <fromfile> <tofile>\n", argv[0]);
input_fd = open_input_file(argv[1]);
output_fd = open_output_file(argv[2]);
while ((bytes_read = checked_read(input_fd, buffer, sizeof(buffer)) > 0)
check_write(output_fd, buffer, bytes_read);
checked_close(input_fd);
checked_close(output_fd);
return 0;
}
Поскольку вы убрали обработку ошибок с глаз долой, теперь гораздо проще увидеть структуру программы. Если у вас еще недостаточно функций, вы можете похоронить цикл в функции void file_copy(int fd_in, int fd_out)
. Это удаляет больше беспорядка из main()
и оставляет вас с очень простым кодом.
При первоначальной попытке функции открыть выходной файл:
void outputFile(int argc, char **argv)
{
/* Check that the output file doesnt exist */
if (stat(argv[argc-1], &inode) != -1)
{
printf("Warning: The file %s already exists. Not going to overwrite\n", argv[argc-1]);
return -1;
}
/*Opening ouput files*/
file_desc_out = open(argv[i],O_CREAT | O_WRONLY | O_EXCL , S_IRUSR|S_IWUSR);
if(file_desc_out == -1)
{
printf("Error: %s cannot be opened. \n",argv[i]); //insted of argv[2] have pointer i.
return -1;
}
}
Критика:
- Вы должны определить переменные, используемые функцией в функции (вам нужно как можно больше избегать глобальных переменных, и в этом коде нет вызова для любой глобальной переменной).
- Вы должны определить тип возвращаемого значения. Вы открываете файл - как дескриптор файла будет возвращен вызывающему коду? Итак, тип возвращаемого значения должен быть
int
.
- Вы передаете только информацию, необходимую для функции - простая форма «сокрытия информации». В этом случае вам нужно только передать имя файла; информация о режимах файлов и тому подобном подразумевается в названии функции.
- В общем, вы должны решить, как обрабатывать ошибки. Если у вас нет других указаний от вашего сеттера, разумно выйти с ошибкой с соответствующим сообщением. Если вы возвращаете индикатор ошибки, то вызывающий код должен проверить его и решить, что делать с ошибкой.
- Ошибки и предупреждения должны быть записаны в
stderr
, а не в stdout
. Основной вывод программы (если есть) идет в stdout
.
- Ваш код не понимает, является ли
argv[i]
или argv[argc-1]
именем выходного файла. В некотором смысле эта критика не имеет значения, когда вы передаете только имя файла функции. Однако согласованность является основным достоинством в программировании, и использование одного и того же выражения для определения одного и того же обычно является хорошей идеей.
- Согласованность макета также важна. Не используйте в своих программах
if(
и if (
; используйте каноническое обозначение if (
, используемое отцами-основателями языка, K & R.
- Аналогичным образом, не допускайте пробелов перед запятыми, пробелов после запятой и совместите с пробелами вокруг операторов, таких как '
|
'. Последовательность делает ваш код легче для чтения, и вы будете читать его гораздо чаще, чем пишете (по крайней мере, после того, как вы закончите курс, вы будете читать больше, чем писать).
- Вы не можете иметь
return -1;
внутри функции, которая не возвращает значения.
Когда вы разбиваете код на функции, вам нужно скопировать / переместить извлекаемые абзацы кода, оставив после себя вызов новой функции. Вам также необходимо скопировать соответствующие локальные переменные из вызывающей функции в новую функцию - возможно, исключив переменные в вызывающей функции, если они там больше не используются. Вы компилируете с большинством включенных предупреждений, не так ли? Вы хотите знать о неиспользуемых переменных и т. Д.
Когда вы создаете новую функцию, одной из наиболее важных частей является определение правильной сигнатуры функции.Это возвращает значение?Если да, то какое значение и каков его тип?Если нет, то как он обрабатывает ошибки?В этом случае вы, вероятно, захотите, чтобы функция выручилась (завершила программу), если она сталкивается с ошибкой.В больших системах вам может потребоваться последовательно возвращать индикатор ошибки (0 означает успех, отрицательный означает сбой, разные отрицательные значения указывают на разные ошибки).Когда вы работаете с функцией, которая возвращает индикатор ошибки, почти всегда важно проверять индикаторы ошибок в вызывающем коде.Для больших программ большие участки кода могут быть связаны с обработкой ошибок.Точно так же вам нужно выяснить, какие значения передаются в функцию.
Я опускаю советы о таких вещах, как «будьте верны», как излишний для вашего этапа обучения программированию на C.