В вашем коде большое количество ошибок.Прежде всего, не зацикливайте for (i = 0; i < SIZE; i++)
, а только зацикливайте те заполненные элементы массива, например, for (i = 0; i < counter; i++)
.
Вам не нужна переменная new
(а это ключевое слово в C ++),Просто удали это.Вместо этого вы должны использовать counter
.
Вы не можете проверить возврат scanf
и, следовательно, колебаться на расстоянии одного нажатия клавиши от Неопределенное поведение при любом вводе пользователем.Всегда, ВСЕГДА, проверять весь ввод пользователя путем минимальной проверки return .
scanf
может использоваться, если используется правильно.Это означает, что вы несете ответственность за проверку возврата из scanf
каждый раз .Вы должны обработать три условия
(return == EOF)
, пользователь отменил ввод, сгенерировав руководство EOF
, нажав Ctrl + d (или в Windows Ctrl + z , но см. CTRL + Z не генерирует EOF в Windows 10 (более ранние версии) ); (return < expected No. of conversions)
a , соответствующий или ошибка ввода .Для сбоя match вы должны учитывать каждый символ, оставшийся в буфере ввода.(сканирование вперед во входном буфере с чтением и отбрасыванием символов до тех пор, пока не будет найдено '\n'
или EOF
);и, наконец, (return == expected No. of conversions)
, указывающий на успешное чтение - тогда вам нужно проверить, соответствует ли входные данные каким-либо дополнительным критериям (например, положительное целое число, положительная плавающая точка, в необходимом диапазоне и т. д.).).
Примечание: после неудачного совпадения или успешного чтения вы должны очистить входной буфер, чтобы убедиться, что он подготовлен для следующего пользователявход.С вашим кодом попробуйте ввести w
(как пропущенное нажатие клавиши для 3
) и посмотрите, что произойдет.Кроме того, что если пользователь вводит "3'"
, когда его палец пропускает клавишу '
, достигая Enter ?
Так как вы проверяете каждый ввод?В вашем случае просто добавьте еще одну переменную int rtn;
, чтобы каждый раз регистрировать возврат scanf
, а затем проверьте, отменил ли пользователь ввод, сгенерировав вручную EOF
, или перед возвращением будет 0
, например:
rtn = scanf ("%d", &option); /* always validate scanf return */
if (rtn == EOF)
option = 0; /* handle EOF by setting exit condition */
else if (rtn == 0) { /* otherwise throw error & empty stdin */
fputs ("error: invalid input.\n", stderr);
empty_stdin(); /* always empty stdin after error */
continue;
}
Что такое empty_stdin()
?Это просто вспомогательная функция, которую вы пишете, которая сканирует вперед во входном буфере (stdin
), отбрасывая символы, пока не будет найден '\n'
(созданный, когда пользователь нажал Enter ) или EOF
встречается, например,
void empty_stdin (void)
{
int c = getchar();
while (c != '\n' && c != EOF)
c = getchar();
}
Далее, ваши case 4:
, "Remove Employee"
никогда не будут работать, как только вы перестанете циклически повторять SIZE
каждый раз и просто будете использовать counter
, как вы должны.Как правило, вы не хотите просто устанавливать для каждого удаленного члена значение 0
, в результате чего вы не знаете, куда следует добавить следующее дополнение.Вместо этого просто выполните цикл из текущего индекса, где найдено id
, чтобы удалить его до конца заполненного массива (counter
), скопировав следующий элемент в текущий индекс в отдельном цикле, например,
/* only loop over filled struct */
for (i = 0; i < counter; i++) {
if (emp[i].id == srch) {
printf ("Employee %d will be removed", emp[i].id);
for (int j = i + 1; j < counter; j++) {
emp[j - 1].id = emp[j].id;
emp[j - 1].age = emp[j].age;
emp[j - 1].sal = emp[j].sal;
}
counter--;
don = 1;
printf ("\n\n");
}
}
( примечание: использование вами флага don
для определения того, был ли индекс найден в порядке)
Для очистки осталось несколько десятков маленьких гнид.Например:
putchar ('\n'); /* putchar outputs a single-character (not printf) */
Когда вы выводите текст, использовать printf
нужно только в том случае, если требуется преобразование, для которого требуется один из printf
спецификаторов преобразования , в других случаях просто используйтеputs
(или fputs
, если вам не нужен '\n'
конец строки).Кроме того, вам нужно всего лишь один вызов printf
(или puts
или fputs
, чтобы вывести столько строк, сколько вам нужно).Вам не нужен один вызов функции на линию.Например, хорошо работает следующее:
fputs ( "\n1. Display Employee Information\n"
"2. Add Employee\n"
"3. Update Employee Salary\n"
"4. Remove Employee\n"
"0. Exit\n\n"
" choice: ", stdout);
( примечание: начальное значение '\n'
исключает все ваши printf ("\n");
ниже)
Для case 3:
или case 4:
, что произойдет, если любой из этих двух вариантов будет выбран из меню, прежде чем что-либо будет добавлено в список? (например, когда counter == 0
). Вы как бы застряли, не так ли? Для любой операции, требующей наличия данных, всегда проверяйте наличие данных, прежде чем идти дальше. Простая проверка counter
- это все, что требуется, например,
case 3:
don = 0;
puts ( "Update Employee Salary\n"
"======================");
if (counter == 0) {
puts ("(list is empty)");
break;
}
...
( примечание: то же самое относится и к case 1:
)
#define SIZE 4
- хорошее и правильное использование #define
для определения целочисленных констант, но зачем ограничивать себя 4
служащими? Сделай это интересным. Используйте 128
или 2048
и т. Д. По крайней мере, тогда у вас будет солидная компания среднего размера. Кроме того, поскольку вы больше не зацикливаетесь на SIZE
в каждом цикле for
, вы не несете дополнительного штрафа, независимо от того, насколько велик предел.
С точки зрения факторинга кода вы должны стремиться сделать свой код более функциональным и менее повторяющимся, создавая функции для ваших общих задач. Вроде принятия целочисленного ввода. Обработка добавления, обновления и удаления сотрудников и т. Д. Я понимаю, что во время обучения полезно видеть весь код, написанный последовательно, чтобы вы не прыгали вверх и вниз по странице, следуя логике, когда управление передается от функции к функции, но ищите повторение областей по мере продвижения вперед (например, empty_stdin()
в примере) и работы над перемещением этих общих битов кода в функции.
Вероятно, есть ряд дополнительных nits, о которых я не упомянул, но если вы сделаете изменения, описанные выше, проверьте все свои входные данные, очистите все посторонние символы из stdin
перед вашим следующим вводом и т. Д., Ваш код будет работать правильно, и интерфейс будет гораздо более надежным. Короткий (но не такой короткий) пример внесения изменений:
#include <stdio.h>
#define SIZE 128
struct Emp {
int id;
int age;
double sal;
};
void empty_stdin (void)
{
int c = getchar();
while (c != '\n' && c != EOF)
c = getchar();
}
int main (void) {
int i, option = 0, counter = 0, srch, don = 0, rtn;
struct Emp emp[SIZE] = { {0} };
puts ("---=== EMPLOYEE DATA ===---\n"); /* no converison, puts if ok */
do { /* only 1 output call needed */
fputs ( "\n1. Display Employee Information\n"
"2. Add Employee\n"
"3. Update Employee Salary\n"
"4. Remove Employee\n"
"0. Exit\n\n"
" choice: ", stdout);
rtn = scanf ("%d", &option); /* always validate scanf return */
if (rtn == EOF)
option = 0; /* handle EOF by setting exit condition */
else if (rtn == 0) { /* otherwise throw error & empty stdin */
fputs ("error: invalid input.\n", stderr);
empty_stdin(); /* always empty stdin after error */
continue;
}
putchar ('\n'); /* putchar outputs a single-character */
switch (option) {
case 0: break;
case 1: // Display Employee Data
puts ( "EMP ID EMP AGE EMP SALARY\n"
"====== ======= ==========");
if (counter == 0) {
puts ("(list is empty)");
break;
}
/* only loop over counter employees */
for (i = 0; i < counter; i++)
printf ("%6d%9d%11.2lf\n", emp[i].id, emp[i].age,
emp[i].sal);
break;
case 2: // Adding Employee
puts ( "Adding Employee\n"
"===============");
if (counter < SIZE) {
fputs ("Enter Employee ID: ", stdout);
rtn = scanf ("%d", &emp[counter].id); /* save return */
if (rtn == EOF) { /* handle EOF */
option = 0; /* set exit condition */
break;
}
else if (rtn == 0) { /* otherwise matching failure */
fputs ("error: invalid input.\n", stderr);
empty_stdin();
continue;
}
fputs ("Enter Employee Age: ", stdout);
rtn = scanf ("%d", &emp[counter].age);
if (rtn == EOF) {
option = 0;
break;
}
else if (rtn == 0) {
fputs ("error: invalid input.\n", stderr);
empty_stdin();
continue;
}
fputs ("Enter Employee Salary: ", stdout);
rtn = scanf ("%lf", &emp[counter].sal);
if (rtn == EOF) {
option = 0;
break;
}
else if (rtn == 0) {
fputs ("error: invalid input.\n", stderr);
empty_stdin();
continue;
}
counter++;
break;
}
else {
printf ("ERROR!!! Max Number of Employees Reached\n\n");
}
break;
case 3:
don = 0;
puts ( "Update Employee Salary\n"
"======================");
if (counter == 0) {
puts ("(list is empty)");
break;
}
do {
fputs ("Enter Employee ID: ", stdout);
rtn = scanf ("%d", &srch); /* save return */
if (rtn == EOF) { /* validate */
option = 0;
break;
}
else if (rtn == 0) {
fputs ("error: invalid input.\n", stderr);
empty_stdin();
continue;
}
/* again, only loop over filled struct */
for (i = 0; i < counter; i++) {
if (emp[i].id == srch) {
printf ("The current salary is %.2lf\n",
emp[i].sal);
fputs ("Enter Employee New Salary: ", stdout);
rtn = scanf ("%lf", &emp[i].sal); /* save rtn */
if (rtn == EOF) { /* validate */
option = 0;
break;
}
else if (rtn == 0) {
fputs ("error: invalid input.\n", stderr);
empty_stdin();
continue;
}
don = 1;
}
}
if (don == 0) {
printf ("*** ERROR: Employee ID not found! ***\n");
}
} while (don != 1);
break;
case 4:
don = 0;
puts ( "Remove Employee\n"
"===============\n");
if (counter == 0) {
puts ("(list is empty)");
break;
}
do {
fputs ("Enter Employee ID: ", stdout);
rtn = scanf ("%d", &srch); /* save return */
if (rtn == EOF) { /* validate */
option = 0;
break;
}
else if (rtn == 0) {
fputs ("error: invalid input.\n", stderr);
empty_stdin();
continue;
}
/* only loop over filled struct */
for (i = 0; i < counter; i++) {
if (emp[i].id == srch) {
printf ("Employee %d will be removed", emp[i].id);
for (int j = i + 1; j < counter; j++) {
emp[j - 1].id = emp[j].id;
emp[j - 1].age = emp[j].age;
emp[j - 1].sal = emp[j].sal;
}
counter--;
don = 1;
printf ("\n\n");
}
}
if (don == 0) {
printf ("*** ERROR: Employee ID not found! ***\n");
}
} while (don != 1);
break;
default:
printf ("ERROR: Incorrect Option: Try Again\n\n");
}
} while (option != 0);
puts ("\nExiting Employee Data Program. Good Bye!!!");
return 0;
}
Рассматривает вещи и дает мне знать, если у вас есть дополнительные вопросы.