0

I have created a program to generate the result of a multiple choice exam. The program was supposed to show the total number of mistakes, blank answers and the number of the question which were answered incorrectly. For the following input: 6

1..223 (Here . means blank answer)

123124

The output was supposed to be:

Your result:

Mistakes: 3

Blanks: 2

Your mistakes are following:

4 5 6

Your blanks are following:

2 3

But the code shows undefined behavior. It seems to go through infinite loop. Expecting solution to my problem shortly. Thanks in advance.

#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;
}
  • 1
    `if(i == 3) .. i = 0;` -- that seems like it may go on a very long time `:)` If you are only using three of your struct instances -- why not just make an array of 3 stuct? Also, why does it look like you are trying to *nul-terminate* an array of `int` with `ques[n] = '\0';`? – David C. Rankin Aug 22 '20 at 06:03
  • I actually wanted to print the result in three columns and I wanted to give inputs without pressing the space bar. So I thought it might be a good idea to put integers in a string. – Pranjal Basak Aug 22 '20 at 06:29
  • And the loop is supposed to terminate on null value of `node` type variable `local` – Pranjal Basak Aug 22 '20 at 06:31
  • 1
    Why not just read everything as a string and then parse the integers from the string with `strtol()` or using `sscanf()` with an offset? – David C. Rankin Aug 22 '20 at 06:32
  • Thanks for suggesting. I will give it a try. But can you point out the reason behind infinite while loop? The loop should terminate as soon as it gets null value. – Pranjal Basak Aug 22 '20 at 06:37
  • 1
    For starters, you need to fix the conversion specifier and pointer mismatch in `scanf("%d", &ques[i]);` and in `scanf("%d", &ans[i]);` which invokes *Undefined Behavior* in your code. Give me a bit to look at the loop issue -- your logic is -- how shall we say this -- less than clear... – David C. Rankin Aug 22 '20 at 06:49
  • 1
    Correcting the `"%d"` to `"%c"` I don't get any infinite loop.. with `6`, `1..223` and `123124` the results are `Mistakes: 4`, `Blanks: 2` with `"Your mistakes... 5"` and `"Your blanks... 3"`. Where are you encountering the infinite loop? – David C. Rankin Aug 22 '20 at 06:55
  • I have edited the code. Can you have another look at it? – Pranjal Basak Aug 22 '20 at 06:55
  • Ok, I don't get the loop anymore..but the code doesn't seem to print the numbers of the mistaken or blanked our questions like this: `Your mistakes are following:` `4 5 6` `Your blanks are following:` `2 3` – Pranjal Basak Aug 22 '20 at 06:58
  • 1
    You have more undefined behavior with `char ques[n], ans[n];` -- both are too-short-by-one to hold a string of `n` characters. That should be `char ques[n+1], ans[n+1];` to provide room for `'\0'`. (I would use, e.g. `char ques[2*n]` to allow you to read the entire string of input with `fgets()` and then just trim the `'\n'` with `ques[strcspn (ques, "\r\n")] = 0;` (`#include `) which would eliminate your loops with `scanf()` (bad practice) – David C. Rankin Aug 22 '20 at 07:04
  • But still I don't get the desired output printing the index of the mistaken and blanked out questions. – Pranjal Basak Aug 22 '20 at 07:08
  • 1
    That's a logic-error and will require a 8.5x11 sheet of paper and pencil to step through your code and go though each iteration and look at what the values should be. Two others, see: [Do I cast the result of malloc?](http://stackoverflow.com/q/605845/995714) and then [How to debug small programs](https://ericlippert.com/2014/03/05/how-to-debug-small-programs/). If I locate the cause, I'll drop a comment in a bit. There is no `space` on either side of `->` when referring to a struct member either -- people get shot for that `:)` – David C. Rankin Aug 22 '20 at 07:11
  • You have helped me a lot, sir. Can please do me another favor? I have been coding in c for quite a while(more than a year). But still I am not able to grasp the basic of this language. Can you suggest me some resources to get a good grip on the contents of c? – Pranjal Basak Aug 22 '20 at 07:16
  • 1
    This Site -- and specifically [The Definitive C Book Guide and List](https://stackoverflow.com/questions/562303/the-definitive-c-book-guide-and-list) – David C. Rankin Aug 22 '20 at 07:19
  • Thanks a lot for spending your time in helping a complete beginner like me. I highly appreciate it. – Pranjal Basak Aug 22 '20 at 07:23

2 Answers2

1

I made some changes to this code, check this out.

#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;
}
Prithvish111
  • 106
  • 7
  • 2
    `fflush(stdin)` in *Undefined Behavior* as specified in the C-standard (only MS provides a NON-Standard implementation that allows that use) See `empty_stdin()` above for a standard-compliant way to do that. – David C. Rankin Aug 22 '20 at 08:13
1

Here are some additional thoughts. What makes your code very convoluted and hard to debug and keep the logic straight is you are mixing your linked-list Add function within the logic of your blanks and mistakes and using special conditions to handle adding the first node and subsequent nodes. This make things difficult to test and debug. If you need to add nodes to a linked-list, then write an add() function that you can thoroughly test and debug before putting it to use in your code.

Your VLAs ques and ans are too short to hold a string of n characters, at minimum they must be n + 1 characters long to provide storage for the nul-termining character that marks the end of the string. Ideally, you will make them at least 2-character longer to also hold the '\n' which will allow you to take input with fgets() rather than looping scanf() a character at a time -- which is just nuts.

You do not need to pass the address of the pointer to freeNode() simply pass a pointer. Sure freeNode() will receive a copy of the pointer -- but it will contain the original address -- and since you don't have to make any changes to that pointer available back to the caller, there is no need to pass the address of the pointer (there won't be any list left to worry about when you are done...)

So putting those pieces together, adding an add() function to add to your linked lists (See Linus on Understanding Pointers for why a pointer-to-pointer is used to iterate to the end), and adding a simple empty_stdin() function to remove the '\n' left in stdin from reading n with scanf() before making calls to fgets() later for ques and ans, you could do:

#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;
}

There is a lot there, so go through it slowly.

Example Use/Output

$ ./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

(note: in 1..223 and 123124, 5 is not a mistake, the 2 is in the correct position at the end)

Look things over and let me know if you have further questions.

David C. Rankin
  • 81,885
  • 6
  • 58
  • 85