После удаления сотрудника, затем добавление нового сотрудника. При перечислении всех сотрудников сохранить, новый сотрудник не отображается - PullRequest
1 голос
/ 16 июня 2019

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

Это запись сотрудника, в которую можно добавлять ограниченных сотрудников, просматривать добавленных сотрудников, удалять их и обновлять их.Базовая запись сотрудника для начинающих с использованием языка Си и без указателей.Я сделал все правильно, за исключением того, что все испортилось.Вы можете видеть, что вывод, который я получаю, немного отличается от вывода, который я хочу.

После удаления сотрудника он удаляется, но когда я добавляю нового сотрудника после удаления старого, он не отображается, когда я хочу увидеть, сколько там сотрудников.Вы можете видеть из двух выводов, которые я предоставил, что все то же самое, за исключением последнего, когда я распечатываю сохраненных сотрудников.Я не понимаю, что не так с моим исходным кодом здесь.

Можно ли мне помочь с объяснением и отсутствующей строкой кода, которая заставит мой код работать и выведет нужный вывод?

Мы используем Visual Studio 2017 для нашего языка C (рекомендуется моим колледжем).И необходимо отправить вывод через pUTTY (Matrix Server).

Мой исходный код - можно найти по ссылке, приведенной здесь https://pastebin.com/spLhtrt5, а исходный код также приведен здесь

#include <stdio.h>

#define SIZE 4

struct Emp {
    int id;
    int age;
    double sal;
};

int main (void) {

    int i, option = 0, counter = 0, new = 0, srch, don = 0;
    struct Emp emp[SIZE] = { {0} };

    printf ("---=== EMPLOYEE DATA ===---\n\n");

    do {
        printf ("1. Display Employee Information\n");
        printf ("2. Add Employee\n");
        printf ("3. Update Employee Salary\n");
        printf ("4. Remove Employee\n");
        printf ("0. Exit\n\n");
        printf ("Please select from the above options: ");
        scanf ("%d", &option);
        printf ("\n");

        switch (option) {
        case 0:                // Exit the program

            break;
        case 1:                // Display Employee Data
            // @IN-LAB

            printf ("EMP ID  EMP AGE EMP SALARY\n");
            printf ("======  ======= ==========\n");

            // Use "%6d%9d%11.2lf" formatting in a   
            // printf statement to display
            // employee id, age and salary of 
            // all  employees using a loop construct 

            // The loop construct will be run for SIZE times 
            // and will only display Employee data 
            // where the EmployeeID is > 0
            for (i = 0; i < SIZE; i++) {
                if (emp[i].id > 0 && emp[i].age > 0 && emp[i].sal > 0) {
                    printf ("%6d%9d%11.2lf\n", emp[i].id, emp[i].age,
                            emp[i].sal);
                }
            }
            printf ("\n");
            break;
        case 2:                // Adding Employee
            // @IN-LAB

            printf ("Adding Employee\n");
            printf ("===============\n");
            if (counter < SIZE) {
                printf ("Enter Employee ID: ");
                scanf ("%d", &emp[new].id);
                printf ("Enter Employee Age: ");
                scanf ("%d", &emp[new].age);
                printf ("Enter Employee Salary: ");
                scanf ("%lf", &emp[new].sal);
                new++;
                counter++;
                printf ("\n");
                break;
            } else {
                printf ("ERROR!!! Maximum Number of Employees Reached\n\n");
            }
            break;
        case 3:
            don = 0;
            printf ("Update Employee Salary\n");
            printf ("======================\n");
            do {
                printf ("Enter Employee ID: ");
                scanf ("%d", &srch);
                for (i = 0; i < SIZE; i++) {
                    if (emp[i].id == srch) {
                        printf ("The current salary is %.2lf\n", emp[i].sal);
                        printf ("Enter Employee New Salary: ");
                        scanf ("%lf", &emp[i].sal);
                        don = 1;
                        printf ("\n");
                    }

                }

                if (don == 0) {
                    printf ("*** ERROR: Employee ID not found! ***\n");
                }

            } while (don != 1);
            break;
        case 4:
            don = 0;
            printf ("Remove Employee\n");
            printf ("===============\n");
            do {
                printf ("Enter Employee ID: ");
                scanf ("%d", &srch);
                for (i = 0; i < SIZE; i++) {
                    if (emp[i].id == srch) {
                        printf ("Employee %d will be removed", emp[i].id);
                        emp[i].id = 0;
                        emp[i].age = 0;
                        emp[i].sal = 0;
                        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);
    printf ("Exiting Employee Data Program. Good Bye!!!\n");
    return 0;
}

Вывод, который я хочу - https://pastebin.com/CcrXibB4.

Вывод, который я получаю - https://pastebin.com/mmvjBVL9.

Ответы [ 3 ]

1 голос
/ 16 июня 2019

Вы вставляете нового сотрудника в позицию индекса new. Когда вы удаляете после вставки 4 сотрудников, новый по-прежнему указывает на 4 вне массива, поэтому ваш новый сотрудник будет записан вне массива и никогда не будет отображаться, что также повредит ваш стек.

Возможное решение: при добавлении нового сотрудника отсканируйте массив, чтобы найти свободное место (например, найдите сотрудника с id == 0, если я правильно понимаю вашу логику) и используйте его положение в качестве индекса вставки.

У меня также есть пара общих советов относительно того, что касается вашего кода:

  1. Напишите небольшие функции, в вашем случае создайте функцию для вставки, одну для удаления и т. Д.
  2. Проверьте каждую функцию отдельно, поэтому вам не нужно проверять все вручную.
  3. Затем напишите тесты, чтобы увидеть, как функции взаимодействуют друг с другом
  4. Добавьте пользовательский интерфейс (ваше меню, регистр переключателя и т. Д.) Только в конце.

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

1 голос
/ 16 июня 2019

В вашем коде большое количество ошибок.Прежде всего, не зацикливайте for (i = 0; i < SIZE; i++), а только зацикливайте те заполненные элементы массива, например, for (i = 0; i < counter; i++).

Вам не нужна переменная new (а это ключевое слово в C ++),Просто удали это.Вместо этого вы должны использовать counter.

Вы не можете проверить возврат scanf и, следовательно, колебаться на расстоянии одного нажатия клавиши от Неопределенное поведение при любом вводе пользователем.Всегда, ВСЕГДА, проверять весь ввод пользователя путем минимальной проверки return .

scanf может использоваться, если используется правильно.Это означает, что вы несете ответственность за проверку возврата из scanf каждый раз .Вы должны обработать три условия

  1. (return == EOF), пользователь отменил ввод, сгенерировав руководство EOF, нажав Ctrl + d (или в Windows Ctrl + z , но см. CTRL + Z не генерирует EOF в Windows 10 (более ранние версии) );
  2. (return < expected No. of conversions) a , соответствующий или ошибка ввода .Для сбоя match вы должны учитывать каждый символ, оставшийся в буфере ввода.(сканирование вперед во входном буфере с чтением и отбрасыванием символов до тех пор, пока не будет найдено '\n' или EOF);и, наконец,
  3. (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;
}

Рассматривает вещи и дает мне знать, если у вас есть дополнительные вопросы.

0 голосов
/ 16 июня 2019

Удаление сотрудника не уменьшает ваш счетчик new.

Измените свой код для case 4: на этот:

don = 0;
printf("Remove Employee\n");
printf("===============\n");
do {
    printf("Enter Employee ID: ");
    scanf("%d", &srch);
    for (i = 0; i < SIZE; i++) {
        if (emp[i].id == srch) {
            printf("Employee %d will be removed", emp[i].id);
            emp[i].id = 0;
            emp[i].age = 0;
            emp[i].sal = 0;
            counter--;
            new--; // This is the important bit
            don = 1;
            printf("\n\n");
        }
    }if (don == 0) {
        printf("*** ERROR: Employee ID not found! ***\n");
    }
} while (don != 1);
break;

и должно работать.

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...