Почему я получаю ошибку сегмента при попытке ввода строки? - PullRequest
0 голосов
/ 28 января 2019

Я использую цикл do while в качестве меню.Пользователь введет число, соответствующее команде.Затем должна появиться функция с этим номером.Мне нужна помощь для первого случая в моем операторе switch (ввод имени).Когда я запускаю код, возникает ошибка сегмента.Может кто-нибудь сказать мне, почему?

#include "defs.h"
#define MAX_CHAR 32

void nameInput(char name[MAX_CHAR + 1]);

int main (void)
{
        int choice = 0;
        char* name[MAX_CHAR + 1] = {0};

    do
    {
            printf("Menu\n");
            printf("1. Name\n");
            printf("2. Enter Years, Party, Office, and State \n");
            printf("3. Enter Age and Sex\n");
            printf("4. Enter Contacts \n");
            printf("5. Enter Contributions and Lies \n");
            printf("6. Display Data \n");
            printf("7. Clear all Data \n");
            printf("8. Quit\n");
            scanf("%d", &choice);

            switch (choice)
            {
                    case 1:
                            nameInput(name[MAX_CHAR + 1]);
                            break;
                    case 2:
                            break;
                    case 3:
                            break;
                    case 4:
                            break;
                    case 5:
                            break;
                    case 6:
                            break;
                    case 7:
                            break;
                    case 8:
                            choice = 8;
                            break;
                    default:
                            printf("Input is invalid\n");
                            break;
            }
    } while(choice != 8);

return (0);
}

void nameInput(char name[MAX_CHAR + 1])
{
        int i = 0;

        printf("Type in the politicians name (max 32 characters):");
        for (i = 0; i < 32; i++)
        {
                scanf("%c", &name[i]);
        }
        printf("\n\n");
}

Ответы [ 2 ]

0 голосов
/ 28 января 2019

Давайте начнем с самого начала.При вводе с scanf вы должны всегда проверять возврат.Вы должны каждый раз проверять три условия:

  1. EOF Отменил ли пользователь ввод, создав ручное руководство EOF, нажав Ctrl + C в Linux (или Ctrl + Z в окнах);
  2. Возвращается ли меньше, чем число спецификаторов преобразования , включенных в строку формата ?Если это так, ошибка совпадения или ошибка ввода произошла ошибка, и извлечение символов остановилось в точке ошибки, оставив все ошибочные символы в stdin непрочитанными;и, наконец,
  3. Возвращаемое значение равно числу спецификаторов преобразования , указывающих, что в каждой переменной сохранено действительное значение.

(вы все равно должны проверить, что любой вводполученный вами ответ удовлетворяет всем имеющимся ограничениям, например 1 <= choice && choice <= 8 и т. д.)

Сложное входное изображение scanf, все преобразования оставляют непрочитанным конец строки в stdin, а затем %cи %[...] спецификаторы преобразования не занимают начальные пробелы.Поэтому, если после любого другого преобразования используется %c или %[...], вы должны вручную обработать пробел, оставшийся в stdin.Вы можете сделать это, включив начальную 'space' в строку формата , или вы можете правильно выполнить очистку после себя, используя вспомогательную функцию для empty_stdin (поскольку нет гарантии, что следующий ввод будет использоватьсяscanf)

Для облегчения опустошения stdin необходим простой цикл, который читает все символы, оставшиеся до тех пор, пока не встретится '\n' или EOF.

void empty_stdin (void)
{
    int c = getchar();

    while (c != EOF && c != '\n')
        c = getchar();
}

Рассмотрим основы, давайте обратимся к вашей проблеме с name.Когда вы объявляете name, вам нужно объявить его как массив символов, а не как массив указателей.Для этого просто удалили дополнительные '*', например,

    int choice = 0;
    char name[MAX_CHAR + 1] = {0};

(хорошая работа в #define с использованием константы для MAX_CHAR)

При обращении к массиву,первый уровень косвенности (например, первый [..]) преобразуется в указатель на первый элемент массива (см .: C11 Standard - 6.3.2.1 Другие операнды - L-значения, массивы и указатели функций (p3) - и обратите внимание на 4 исключения).Это означает, что name преобразуется в указатель, в том числе когда он передается в качестве параметра в функцию.Таким образом, вы можете использовать следующее в объявлении функции:

int nameInput(char *name);

или

int nameInput(char name[]);

(оба эквивалентны и действительны)

На самом деле, вы даже можете включитьинформация о размере, как у вас, через нее не нужна.

Когда вы звоните nameInput, вы просто включаете name по той же причине - она ​​преобразуется в указатель на первый символ.Таким образом, ваш вызов будет:

case 1:  nameInput(name);

(хотя мы увидим, что вы можете это улучшить)

Когда вы создаете функцию, которая принимает ввод, она должна обеспечивать значимый возврат, а не void.Таким образом, вы можете использовать возврат к validate независимо от того, был ли ввод успешным, сбойным или пользователь отменил.Таким образом, вместо void, простой int работает нормально, например,

int nameInput (char name[])
{
    int rtn;

    printf("Type in the politicians name (max 32 characters): ");

    rtn = scanf (" %32[^\n]", name);

    if (rtn == EOF) {
        fputs ("(user canceled input)\n\n", stdout);
        return -1;
    }
    else if (rtn == 0) {
        empty_stdin();
        return 0;
    }

    empty_stdin();

    return 1;
}

(где -1 возвращается, если пользователь отменяет, 0 для сбоя или 1, указывающий на успешный ввод)

Сложив все вместе (и приведя в порядок тот факт, что one printf - это все, что нужно для вашего меню), вы можете сделать:

#include <stdio.h>

#define MAX_CHAR 32

void empty_stdin (void)
{
    int c = getchar();

    while (c != EOF && c != '\n')
        c = getchar();
}

int nameInput(char name[]);

int main (void)
{
    int choice = 0;
    char name[MAX_CHAR + 1] = {0};

    do {
            int rtn;    /* variable to hold scanf return */

            printf ("\nMenu\n"
                    "  1. Name\n"
                    "  2. Enter Years, Party, Office, and State \n"
                    "  3. Enter Age and Sex\n"
                    "  4. Enter Contacts \n"
                    "  5. Enter Contributions and Lies \n"
                    "  6. Display Data \n"
                    "  7. Clear all Data \n"
                    "  8. Quit\n\n"
                    "   choice: ");
            rtn = scanf ("%d", &choice);

            if (rtn == EOF) {
                fputs ("(user canceled input)\n", stdout);
                break;
            }
            else if (rtn != 1) {
                fputs ("  error: invalid integer input.\n", stderr);
                empty_stdin();
                continue;
            }
            else {
                putchar ('\n');
                empty_stdin();
            }

            switch (choice)
            {
                    case 1:
                            {
                                int nrtn = nameInput(name);
                                if (nrtn == -1)
                                    goto usercanceled;
                                else if (nrtn == 1)
                                    printf ("\n  stored: %s\n", name);
                            }
                            break;
                    case 2:
                            break;
                    case 3:
                            break;
                    case 4:
                            break;
                    case 5:
                            break;
                    case 6:
                            break;
                    case 7:
                            break;
                    case 8:
                            break;
                    default:
                            printf("  Input is invalid\n");
                            break;
            }
    } while (choice != 8);

    usercanceled:;

    return 0;
}

int nameInput (char name[])
{
    int rtn;

    printf("Type in the politicians name (max 32 characters): ");

    rtn = scanf (" %32[^\n]", name);

    if (rtn == EOF) {
        fputs ("(user canceled input)\n\n", stdout);
        return -1;
    }
    else if (rtn == 0) {
        empty_stdin();
        return 0;
    }

    empty_stdin();

    return 1;
}

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

Пример использования / Вывод

$ ./bin/scanf_menu

Menu
  1. Name
  2. Enter Years, Party, Office, and State
  3. Enter Age and Sex
  4. Enter Contacts
  5. Enter Contributions and Lies
  6. Display Data
  7. Clear all Data
  8. Quit

   choice: foo
  error: invalid integer input.

Menu
  1. Name
  2. Enter Years, Party, Office, and State
  3. Enter Age and Sex
  4. Enter Contacts
  5. Enter Contributions and Lies
  6. Display Data
  7. Clear all Data
  8. Quit

   choice: 1

Type in the politicians name (max 32 characters): Bleach-blond Cheeto

  stored: Bleach-blond Cheeto

Menu
  1. Name
  2. Enter Years, Party, Office, and State
  3. Enter Age and Sex
  4. Enter Contacts
  5. Enter Contributions and Lies
  6. Display Data
  7. Clear all Data
  8. Quit

   choice: 1

Type in the politicians name (max 32 characters): (user canceled input)

( примечание: пользователь может отменить, сгенерировав руководство EOF в любой момент и вызвать изящный выход в программу)

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

0 голосов
/ 28 января 2019

Функция nameInput ожидает указатель, и когда вы вызываете функцию с name в качестве аргумента, то есть nameInput(name), это эквивалентно передаче указателя на первый элемент массива name.

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