为什么在尝试输入字符串时会出现段错误?

时间:2019-01-28 18:58:03

标签: c string

我正在使用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 :(得分:5)

让我们从头开始。使用scanf输入信息时,您必须始终检查退货。您每次必须测试三个条件:

  1. EOF用户是否通过在Linux上按 Ctrl + C (或在Windows上按 Ctrl + Z )来生成手册EOF来取消输入? );
  2. 返回值是否少于 format字符串中包含的转换说明符的数量?如果是这样,则发生 matching failure input failure ,并且字符提取在错误发生时停止,从而使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 Other Operands - Lvalues, arrays, and function designators(p3)-并注意第4个例外)。这意味着name将转换为指针,包括将其作为参数传递给函数时。因此,您可以在函数声明中使用以下内容:

int nameInput(char *name);

int nameInput(char name[]);

(两者都是有效的)

实际上,您甚至可以通过它包括尺寸信息,而不必这样做。

调用nameInput时,出于相同的原因,只需包含name -它会转换为指向第一个字符的指针。因此您的电话将是:

case 1:  nameInput(name);

(尽管我们会看到您可以改善这一点)

创建接收输入的函数时,它需要提供有意义的回报,而不是void。这样,无论输入成功,失败还是用户取消,您都可以使用返回验证的方法。因此,例如,简单的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

将其全部放入(并整理菜单中只需要一个一个 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来取消操作,并正常退出该程序)

仔细检查一下,如果还有其他问题,请告诉我。

答案 1 :(得分:0)

函数nameInput需要一个指针,当您使用name作为参数调用函数时,即nameInput(name),这等效于传递 指向数组name的第一个元素的指针。