【问题标题】:Problems regarding an multiple choice question program关于多项选择题程序的问题
【发布时间】:2020-12-11 10:31:53
【问题描述】:

我创建了一个程序来生成多项选择考试的结果。该程序应该显示错误总数、空白答案和错误回答的问题的数量。对于以下输入: 6

1..223 (这里.表示空白答案)

123124

输出应该是:

Your result:

Mistakes: 3

Blanks: 2

Your mistakes are following:

4 5 6

Your blanks are following:

2 3

但是代码显示了未定义的行为。它似乎经历了无限循环。期待很快解决我的问题。提前致谢。

#include <stdio.h>
#include <stdlib.h>



typedef struct node
{
    char data;
    struct node* next;
}node;

void printNode(node* head)
{
    node* local = head;
    int i = 0;
    if(local -> data == 0)
    {
        printf("0");
        return;
    }

    while(local != NULL)
    {
        if(i == 3)
        {
            i = 0;
            printf("\n");
        }


        printf("%d\t", local -> data);
        local = local -> next;
        ++i;

    }
}

void freeNode(node** head)
{
    node* temp = (*head);

    while((*head) != NULL)
    {
        (*head) = (*head) -> next;
        free(temp);
        temp = (*head);
    }
}
int main()
{
    int n, i, flagB, flagM, blnk, mstk;
    blnk = mstk = flagB = flagM = 0;

    printf("Enter the number of questions: ");
    scanf("%d", &n);

    char ques[n], ans[n];

    if(n == 0)
        return 0;

    node* headM = (node*)malloc(sizeof(node));

    node* nodeM;

    node* headB = (node*)malloc(sizeof(node));

    node* nodeB;

    printf("Enter your given answers: ");
    fflush(stdin);
    for(i = 0; i < n; ++i)
    {
        scanf("%c", &ques[i]);
    }

    fflush(stdin);
    ques[n] = '\0';

    printf("Enter the solution: ");

    for(i = 0; i < n; ++i)
    {
        scanf("%c", &ans[i]);
    }

    ans[n] = '\0';

    for(i = 0; i < n; ++i)
    {
        if(ques[i] == '.')
        {
            ++blnk;
            if(flagB == 0)
            {
                headB -> data = i + 1;
                headB -> next = NULL;
                nodeB = headB;
                continue;
            }

            nodeB -> next = (node*)malloc(sizeof(node));
            nodeB = nodeB -> next;
            nodeB -> data = i + 1;
            nodeB-> next = NULL;
            flagB = 1;
        }
        else if(ques[i] != ans[i])
        {
            ++mstk;

            if(flagM == 0)
            {
                headM -> data = i + 1;
                headM -> next = NULL;
                nodeM = headM;

                continue;
            }

            nodeM -> next = (node*)malloc(sizeof(node));
            nodeM = nodeM -> next;
            nodeM -> data = i;
            nodeM-> next = NULL;
            flagM = 1;

        }

    }



    printf("Your result:\n\tMistakes: %d\n\tBlanks: %d\n", mstk, blnk);

    printf("Your mistakes are follwing:\n");
    printNode(headM);

    printf("\nYour blanks are follwing:\n");
    printNode(headB);

    freeNode(&headM);
    freeNode(&headM);

    return 0;
}

【问题讨论】:

  • if(i == 3) .. i = 0; -- 这似乎可能会持续很长时间:) 如果您只使用三个结构实例 -- 为什么不创建一个包含 3 个结构的数组?另外,为什么看起来您正在尝试nul-terminate 使用ques[n] = '\0';int 数组?
  • 为什么不直接将所有内容读取为字符串,然后使用 strtol() 或使用带有偏移量的 sscanf() 解析字符串中的整数?
  • 对于初学者,您需要修复scanf("%d", &amp;ques[i]);scanf("%d", &amp;ans[i]); 中的转换说明符和指针不匹配,这会在您的代码中调用Undefined Behavior。给我看一下循环问题——你的逻辑是——我们该怎么说——不太清楚......
  • "%d" 更正为"%c" 我没有得到任何无限循环.. 使用61..223123124 结果是Mistakes: 4Blanks: 2 "Your mistakes... 5""Your blanks... 3"。你在哪里遇到无限循环?
  • char ques[n], ans[n]; 有更多未定义的行为——两者都太短了,无法容纳 n 字符的字符串。那应该是char ques[n+1], ans[n+1];'\0' 提供空间。 (我会使用,例如char ques[2*n],让您可以使用fgets() 读取整个输入字符串,然后使用ques[strcspn (ques, "\r\n")] = 0; (#include &lt;string.h&gt;) 修剪'\n',这将消除您使用scanf() 的循环(不好的做法)

标签: c undefined infinite-loop undefined-behavior infinite


【解决方案1】:

这里有一些额外的想法。是什么让您的代码非常复杂且难以调试并保持逻辑清晰,是您将链表 Add 函数与空白和错误的逻辑混合在一起,并使用特殊条件来处理添加第一个节点和后续节点。这使得测试和调试变得困难。如果您需要将节点添加到链表中,请编写一个 add() 函数,您可以在将其用于您的代码之前对其进行彻底的测试和调试。

您的 VLA quesans 太短,无法容纳 n 字符的字符串,它们至少必须是 n + 1 字符长才能为 nul 终止提供存储空间标记字符串结尾的字符。理想情况下,您将使它们至少长 2 个字符以同时保存 '\n',这将允许您使用 fgets() 进行输入,而不是一次循环 scanf() 一个字符——这简直是疯了。

您不需要将指针的地址传递给freeNode(),只需传递一个指针即可。当然freeNode() 将收到指针的副本——但它将包含原始地址——并且由于您不必对该指针进行任何更改,以便返回给调用者,因此无需传递地址指针(完成后不会有任何列表需要担心......)

因此,将这些部分放在一起,添加一个 add() 函数以添加到您的链接列表(请参阅 Linus on Understanding Pointers 了解为什么使用指向指针的指针迭代到末尾),并添加一个简单的 empty_stdin()在稍后为quesans 调用fgets() 之前,从阅读nscanf() 中删除留在stdin 中的'\n' 的功能,您可以这样做:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

/* simple function to empty stdin to end-of-line */
void empty_stdin (void)
{
    int c = getchar();
    
    while (c != '\n' && c != EOF)
        c = getchar();
}

typedef struct node
{
    int data;
    struct node *next;
} node;

node *add(node **head, int v)
{
    node **ppn = head,                      /* pointer to pointer to head */
          *pn = *head,                      /* pointer to head */
          *newn = malloc (sizeof *newn);    /* allocate new node */
 
    if (!newn) {                            /* validate allocation */
        perror ("malloc-node");
        return NULL;
    }
    newn->data = v;                         /* initialize members values */
    newn->next = NULL;
 
    while (pn) {                            /* iterate to end of list */
        ppn = &pn->next;
        pn = pn->next;
    }
 
    return *ppn = newn;                     /* add & return new node */
}

void printNode (node *head)
{
    for (; head; head = head->next)
        printf (" %d", head->data);
    putchar ('\n');
}

void freeNode(node *head)
{
    while (head != NULL)
    {
        node *victim = head;
        head = head->next;
        free(victim);
    }
}

int main()
{
    int n, i, blnk, mstk;
    blnk = mstk = 0;
    node *headM = NULL;         /* declare pointers and initialize NULL */
    node *headB = NULL;

    printf ("Enter the number of questions: ");
    /* you must VALIDATE every user-input */
    if (scanf ("%d", &n) != 1) {
        fputs ("error: invalid integer input.\n", stderr);
        return 1;
    }
    empty_stdin();              /* remove '\n' (and any other chars from user) */
                                /* before calling fgets() below */
    
    if (n == 0)                 /* check 0 BEFORE VLA declaration */
        return 0;
    
    char ques[2*n], ans[2*n];   /* declare question/answer VLAs, don't skimp */
    
    printf("Enter your given answers: ");

    if (!fgets(ques, sizeof ques, stdin))   /* read ques from stdin */
        return 1;

    ques[strcspn(ques, "\r\n")] = 0;        /* trim '\n' from end of ques */
    
    printf("Enter the solution: ");

    if (!fgets(ans, sizeof ans, stdin))     /* read ans from stdin */
        return 1;

    ans[strcspn(ans, "\r\n")] = 0;          /* ditto for ans */
    
    for(i = 0; i < n; ++i)                  /* loop n times */
    {
        if(ques[i] == '.')                  /* if blank */
        {
            add (&headB, i + 1);            /* add to list headB */
            ++blnk;                         /* increment counter */
        }
        else if(ques[i] != ans[i])          /* if mistake */
        {
            add (&headM, i + 1);            /* add to list headM */
            ++mstk;                         /* increment counter */
        }
    }

    printf ("Your result:\n\tMistakes: %d\n\tBlanks: %d\n"
            "Your mistakes are following:\n", mstk, blnk);
    printNode(headM);

    printf("\nYour blanks are following:\n");
    printNode(headB);

    freeNode(headM);    /* no need to pass the address of the pointer to free */
    freeNode(headB);    /* there won't be a list left when freeNode is done */

    return 0;
}

内容很多,慢慢看吧。

使用/输出示例

$ ./bin/llquestions
Enter the number of questions: 6
Enter your given answers: 1..223
Enter the solution: 123124
Your result:
        Mistakes: 2
        Blanks: 2
Your mistakes are following:
 4 6

Your blanks are following:
 2 3

注意:1..223123124中,5没有错,2在最后的位置是正确的)

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

【讨论】:

    【解决方案2】:

    我对这段代码做了一些修改,看看这个。

    #include <stdio.h>
    #include <stdlib.h>
    
    typedef struct Node node;
    struct Node
    {
      int data;
      struct Node * next;
    };
    
    void printNode(node *head)
    {
    node *local = head;
    while (local != NULL)
    {
        printf("%d ", local->data);
        local = local->next;
    }
    }
    
    void freeNode(node **head)
    {
    node *temp = (*head);
    
    while ((*head) != NULL)
    {
        (*head) = (*head)->next;
        free(temp);
        temp = (*head);
      }
      }
     int main()
     {
    int n, i, flagB = 0, flagM = 0, blnk = 0, mstk = 0;
    blnk = mstk = flagB = flagM = 0;
    
    printf("Enter the number of questions: ");
    scanf("%d", &n);
    
    char ques[n], ans[n];
    
    if (n == 0)
        return 0;
    
    node *headM = (node*) malloc(sizeof(node));
    headM->data = 0;
    node *nodeM = headM;
    
    node *headB = (node*) malloc(sizeof(node));
    headB->next = 0;
    node *nodeB = headB;
    
    printf("Enter your given answers: ");
    
    for (i = 0; i < n; ++i)
    {
        scanf("%s", &ques[i]);
    }
    
    ques[n] = '\0';
    fflush(stdin);
    
    printf("Enter the solution: ");
    
    for (i = 0; i < n; ++i)
    {
        scanf("%s", &ans[i]);
    }
    
    ans[n] = '\0';
    fflush(stdin);
    
    for (i = 0; i < n; ++i)
    {
        if (ques[i] == '.')
        { ++blnk;
            if (flagB == 0)
            {
                nodeB->data = i + 1;
                nodeB->next = NULL;
                flagB = 1;
                continue;
            }
    
            nodeB->next = (node*) malloc(sizeof(node));
            nodeB = nodeB->next;
            nodeB->data = i + 1;
            nodeB->next = NULL;
        }
        else if (ques[i] != ans[i])
        { ++mstk;
            if (flagM == 0)
            {
                nodeM->data = i + 1;
                nodeM->next = NULL;
                flagM = 1;
                continue;
            }
    
            nodeM->next = (node*) malloc(sizeof(node));
            nodeM = nodeM->next;
            nodeM->data = i + 1;
            nodeM->next = NULL;
            //flagM = 1;    //You made a mistake here
        }
    }
    
    nodeM = headM;
    nodeB = headB;
    
    printf("Your result:\n\tMistakes: %d\n\tBlanks: %d\n", mstk, blnk);
    
    printf("Your mistakes are following question numbers:\n");
    if (mstk != 0)
        printNode(headM);
    else
        printf("No Mistakes\n");
    
    printf("\nYour blanks are following question numbers:\n");
    if (blnk != 0)
        printNode(headB);
    else
        printf("No Blanks\n");
    
    freeNode(&headM);
    freeNode(&headM);
    
    return 0;
    }
    

    【讨论】:

    • fflush(stdin) in Undefined Behavior 在 C 标准中指定(只有 MS 提供允许使用的非标准实现)参见上面的 empty_stdin() 以获取标准- 兼容的方式来做到这一点。
    猜你喜欢
    • 2011-10-12
    • 2011-07-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-07-08
    • 1970-01-01
    • 2020-04-21
    相关资源
    最近更新 更多