У вас есть фундаментальное неправильное понимание использования массивов в C:
int dailyTrainCount[5]; // variable for daily train count
...
scanf("%d", &dailyTrainCount[5]);
int dailyTrainCout[5];
объявляет целочисленный массив с 5 элементами. Допустимые индексы для этих элементов: [0-4]
, C / C ++ использует ноль массивов. Присваивая dailyTrainCount[5]
, вы назначаете вход в ячейку памяти 1 после конца массива dailyTrainCount
, вызывая Неопределенное поведение . (определенное выполнение вашей программы прекращается в этот момент, и может произойти что-то от появления нормального режима до SegFaulting) Более фундаментально, будет только 1 dailyTrainCount
, поэтому для начала массив не нужен.
Далее, хотя это и не ошибка, избегайте использования глобальных переменных. Они редко требуются. Вместо этого объявите ваши переменные в области, где они необходимы, и передайте переменные в качестве параметров любой функции, которая должна работать с их данными.
Использование scanf
для ввода новых программистов на С составляет значительный процент вопросов на этом сайте. Злоупотребление scanf
является распространенным явлением. Самым распространенным неправильным использованием является невозможность проверки возврата . Вторым наиболее распространенным является отсутствие учета того, какие символы остаются во входном буфере, непрочитанные, после ошибки .
scanf
можно использовать, если используется правильно. Это означает, что вы несете ответственность за проверку возврата из scanf
каждый раз . Вы должны справиться с тремя условиями
(return == EOF)
пользователь отменил ввод, сгенерировав руководство EOF
, нажав Ctrl + d (или Ctrl + z в окнах);
(return < expected No. of conversions)
a соответствие или вход произошла ошибка. Для сбоя match вы должны учитывать каждый символ, оставшийся в вашем входном буфере (сканирование вперед во входном буфере с чтением и отбрасыванием символов до тех пор, пока не будет найдено '\n'
или EOF
); и наконец
(return == expected No. of conversions)
указывает на успешное чтение - тогда вам нужно проверить, соответствует ли входные данные каким-либо дополнительным критериям (например, положительное целое число, положительная плавающая точка, в требуемом диапазоне и т. Д.).
Примечание: после неудачного совпадения или успешного чтения вы должны очистить буфер ввода, чтобы убедиться, что он подготовлен для следующего ввода пользователя, независимо от того, какая функция используется принять вход. Например, если вы успешно используете scanf
для получения целочисленного ввода, но оставляете завершающий '\n'
в буфере ввода, а затем позже пытаетесь ввести с помощью fgets
, ваш ввод не удастся, и ваш буфер будет содержать пустым -string независимо от того, сколько символов вводит пользователь ...
Чтобы упростить правильное использование scanf
, вы можете использовать короткую вспомогательную функцию для очистки символов из stdin
после каждого ввода. После каждого ввода вы просто вызываете getchar()
для чтения до тех пор, пока не будет найден '\n'
или EOF
, например,
/* simple function to empty stdin */
void empty_stdin (void)
{
int c = getchar();
while (c != '\n' && c != EOF)
c = getchar();
}
Затем, как минимум , вы должны подтвердить, что возвращение scanf
соответствует количеству конверсий, указанному в вашей строке формата , например,
char day[MAXDAY];
int /* dailyTrainCount[5],*/ trainsperday,
trainTimeFrame[MAXTRAIN], i = 0;
fputs ("Welcome to TMS (TRAIN MANAGEMENT SYSTEM)\n\n"
" Enter day of Operation: ", stdout);
if (scanf ("%14s", day) != 1) { /* VALIDATE EVERY INPUT - check return */
fputs ("(user canceled day input.)\n", stderr);
return 1;
}
empty_stdin(); /* empty remaining characters in stdin */
( примечание: ваш массив dailyTrainCount[]
был заменен на одну целочисленную переменную trainsperday
, чтобы сделать изменение массива из int
на одно целое число ясным. empty_stdin()
защищает от любых посторонних введенных символов, таких как ввод пользователя "Monday April 22, 2019"
. Посмотрите, что произойдет, если вы попробуете это без очистки stdin
...)
Чтобы полностью проверить каждый требуемый вход, вы должны проверить и адекватно обработать все три условия, изложенные выше, например,
for (;;) { /* loop continually until valid input received */
printf ("\n How many trains to operate on %s?"
" [1-5]: ", day);
int rtn = scanf ("%d", &trainsperday); /* save scanf return */
if (rtn == EOF) { /* handle EOF */
fputs ("(user canceled day input.)\n", stderr);
return 1;
}
empty_stdin(); /* empty remaining characters in stdin */
if (rtn < 1) /* if matching failure */
fputs (" (error: invalid integer input.)\n", stderr);
else if (trainsperday < 0 || 5 < trainsperday) /* out of range */
fputs (" (error: trainsperday exceeds 5.)\n", stderr);
else /* good input, break input loop */
break;
}
Таким образом, вы можете выполнять заполнение расписания поездов, обрабатывая все ошибки ввода или значения вне диапазона, в то же время позволяя пользователю отменять ввод в любой точке и обеспечивая плавное завершение вашей программы. Переделка вашего кода может быть:
#include <stdio.h>
/* my global variables - don't use them, declare in scope needed, but
* do #define any constants needed to avoid magic-numbers in your code
*/
#define MAXTRAIN 5
#define MAXDAY 15
/* simple function to empty stdin */
void empty_stdin (void)
{
int c = getchar();
while (c != '\n' && c != EOF)
c = getchar();
}
int main (void)
{
char day[MAXDAY];
int /* dailyTrainCount[5],*/ trainsperday,
trainTimeFrame[MAXTRAIN], i = 0;
fputs ("Welcome to TMS (TRAIN MANAGEMENT SYSTEM)\n\n"
" Enter day of Operation: ", stdout);
if (scanf ("%14s", day) != 1) { /* VALIDATE EVERY INPUT - check return */
fputs ("(user canceled day input.)\n", stderr);
return 1;
}
empty_stdin(); /* empty remaining characters in stdin */
for (;;) { /* loop continually until valid input received */
printf ("\n How many trains to operate on %s?"
" [1-5]: ", day);
int rtn = scanf ("%d", &trainsperday); /* save scanf return */
if (rtn == EOF) { /* handle EOF */
fputs ("(user canceled day input.)\n", stderr);
return 1;
}
empty_stdin(); /* empty remaining characters in stdin */
if (rtn < 1) /* if matching failure */
fputs (" (error: invalid integer input.)\n", stderr);
else if (trainsperday < 0 || 5 < trainsperday) /* out of range */
fputs (" (error: trainsperday exceeds 5.)\n", stderr);
else /* good input, break input loop */
break;
}
/* show train menu to user */
printf ("\n%d Train(s) will operate on %s\n\n"
" Available time frames\n"
" (1) - 7AM-10PM\n"
" (2) - 10AM-1PM\n"
" (3) - 1PM-4PM\n"
" (4) - 4PM-7PM\n\n", trainsperday, day);
while (i < trainsperday) { /* use while, not for, to handle errors */
printf (" Enter time frame for train #%d: ", i + 1);
int rtn = scanf ("%d", &trainTimeFrame[i]); /* save scanf return */
if (rtn == EOF) { /* handle EOF */
fputs ("(user canceled day input.)\n", stderr);
return 1;
}
empty_stdin(); /* empty remaining characters in stdin */
if (rtn < 1) /* if matching failure */
fputs (" (error: invalid integer input, valid input [1-4].)\n",
stderr);
else if (trainTimeFrame[i] < 0 || 4 < trainTimeFrame[i]) /* valid? */
fputs (" (error: invalid time frame, valid input [1-4].)\n",
stderr);
else /* good input, now increment i */
i++;
}
/* output train schedule */
printf ("\nTrain Schedule:\n\n");
for (i = 0; i < trainsperday; i++)
printf (" Train #%d is set to time frame #%d\n",
i + 1, trainTimeFrame[i]);
return 0;
}
( примечание: использование цикла while
вместо цикла for
при заполнении таймфреймов для каждого поезда. Если используется for
и происходит сбой, восстановление невозможно Использование while
и только увеличение счетчика на допустимом вводе дает решение)
Теперь каждый раз, когда вы пишете программу, которая принимает ввод - попробуйте сломать ее. Введите недопустимый ввод специально для обработки ошибок вашего кода. Если вы сломаете это, исправьте это и попробуйте снова. После того, как вы попробовали все недопустимые данные, о которых вы можете подумать, сделайте шаг клавиатуры на клавиатуре. Если он пройдет все тесты, вы можете быть уверены, что рассмотрели наиболее вероятные сценарии неправильного использования, которые предоставят ваши пользователи.
Пример использования / Вывод
С преднамеренным неверным вводом для этой цели.
$ ./bin/trainsperday
Welcome to TMS (TRAIN MANAGEMENT SYSTEM)
Enter day of Operation: Tuesday April 23, 2019
How many trains to operate on Tuesday? [1-5]: Ten Trains on Tuesday
(error: invalid integer input.)
How many trains to operate on Tuesday? [1-5]: 7
(error: trainsperday exceeds 5.)
How many trains to operate on Tuesday? [1-5]: 3
3 Train(s) will operate on Tuesday
Available time frames
(1) - 7AM-10PM
(2) - 10AM-1PM
(3) - 1PM-4PM
(4) - 4PM-7PM
Enter time frame for train #1: 7AM-10PM
(error: invalid time frame, valid input [1-4].)
Enter time frame for train #1: 5
(error: invalid time frame, valid input [1-4].)
Enter time frame for train #1: 3
Enter time frame for train #2: 1
Enter time frame for train #3: 2
Train Schedule:
Train #1 is set to time frame #3
Train #2 is set to time frame #1
Train #3 is set to time frame #2
Посмотрите вещи и дайте мне знать, если у вас есть дополнительные вопросы.