Garbage value reading due to buffering problem

Collapse
X
 
  • Time
  • Show
Clear All
new posts
  • Aftabpasha
    New Member
    • Oct 2008
    • 32

    Garbage value reading due to buffering problem

    I have seen a simple program for handling Link List operations. The following is the part of the code that contains the problem. The code is modified but contains all necessary information regarding the problem.
    Code:
    #include<stdio.h>
    #include<stdlib.h>
    #include<conio.h>
    
    struct llist
    {
    	int val;
    	struct llist *next;
    };
    typedef llist item;
    
    void llist(item **head);
    
    void main()
    {
    	item *head;
    	llist(&head);
    }
    
    void llist(item **head)
    {
    	int i=1, no;
    	char ch='y';
    	item *curr, *temp;
    
    	while(ch=='y')
    	{
    		printf("Enter no: ");
    		scanf("%d",&no);
    
    		if(i==1)
    		{
    			(*head)=(item*)malloc(sizeof(item));
    			(*head)->val=no;
    			(*head)->next=NULL;
    			temp=(*head);
    			i++;
    
    			printf("After First node allocation : *head = %u  temp = %u\n\n",*head, temp);
    		}
    		else
    		{
    			printf("Before next node allocation : temp = %u\n", temp);		
    
    			curr=(item*)malloc(sizeof(item));
    			curr->val=no;
    			curr->next=NULL;
    			temp->next=curr;
    			temp=curr;
    
    			printf("After compliting next node allocation : curr=%u  temp=%u\n\n",curr,temp);		
    		}
    
    		printf("Do you want to add node?(y/n) : ");
    		scanf("%s",&ch); /**** Use %c here ****/
    	}
    }
    In this, the link list is created and initialized. There are certain printf() statements I've inserted for better understanding of the problem. The llist() function prompts the user to enter a number, creates a node, initializes it and attaches it to LL, and then asks user if he has any node to be inserted. The choice is read in a "char" variable "ch" which is initialized to 'y'.
    1. If we use "scanf("%c",&ch );", it never accepted the choice "ch" and directly returned to main().
    2. If we use string format specifier %s in place of %c (as used in above code snippet), it accepts choice "ch", but manipulates the addresses of LL. The following is a sample output. It will give an Idea what is happening.
    Code:
    Enter no: 123
    After First node allocation : *head = 3289416  temp = 3289416
    
    Do you want to add node?(y/n) : y
    
    Enter no: 234
    Before next node allocation : temp = 3289344
    After completing next node allocation : curr=3289432  temp=3289432
    
    Do you want to add node?(y/n) : y
    
    Enter no: 345
    Before next node allocation : temp = 3289344
    After completing next node allocation : curr=3289448  temp=3289448
    
    Do you want to add node?(y/n) : y
    
    Enter no: 456
    Before next node allocation : temp = 3289344
    After completing next node allocation : curr=3289464  temp=3289464
    
    Do you want to add node?(y/n) : n
    3. When we use "int ch" in place of "char ch", and give option as "Enter 0 for exit" and do necessary modifications in code, the whole thing works perfectly and we get desired output.

    So, I don't understand why it is not accepting "char" (%c) but accepts "string" (%s) but gives problem and why no problem with "int"(%d)? How is it related to keyboard buffer? Please explain this.
    Thank you.
  • gpraghuram
    Recognized Expert Top Contributor
    • Mar 2007
    • 1275

    #2
    Hi,
    The problem is becos STDIN has some characters which the scanf if reading and not waiting for your prompt.
    You can use fflush(STDIN) befor using scanf.
    but fflush(STDIN) is not the right way to do it.

    Raghu

    Comment

    • Aftabpasha
      New Member
      • Oct 2008
      • 32

      #3
      @Raghu
      Thanks for your reply.
      The reason given by you can satisfy the problem when we accept "ch" as "char" i.e. using %c as format specifier.

      But when we are using "%s", it is accepting 'y' properly. If there are some characters left in buffer then it should read some garbage value.in place of 'y' also.
      Here, it is accepting 'y' properly but causing the value of "temp" to change.

      And when we use "int ch"(%d) in place of "char ch"(%c or %s) and end the loop when ch=0, then it works perfectly. Here why the garbage values are not being read?

      I just want to know, if there are garbage values present in buffer, why they are affecting differently? Is it due to different alignment of different datatypes & there respective sizes in memory? I think alignment and size of different datatypes are the most probable reasons.
      Please explain these with example if possible.

      Comment

      • Banfa
        Recognized Expert Expert
        • Feb 2006
        • 9067

        #4
        You can not use ch, a char, to accept input from %s. The minimum number of bytes written by %s is 2, so you will write to memory outside the limits of the variable ch corrupting other variables and producing undefined behaviour.

        %c doesn't work properly when used just after %d because there are extra characters (\n) in the input stream that %d has not read but that will be read by %c.

        You need to empty the input stream before calling scanf with %c. However you should not that calling fflush on stdin or any input stream is undefined behaviour according to the standard. The only real way to clear the input stream is to read characters until it is empty.

        Things to Avoid in C/C++ -- fflush(stdin), Part 2 - GIDNetwork

        Comment

        • Aftabpasha
          New Member
          • Oct 2008
          • 32

          #5
          @ Banfa
          Thanks for your reply.
          Now, I got this.
          1. %s is accepting character ('y') correctly, but violating memory. Hence, the LL is getting corrupted.
          2. When read as "char"(%c), due to scanf() problem, it is reading some accidentally left characters (probably '\n') as a wrong answer.

          So, the whole problem is due to scanf() leaving some data unread in buffer.
          But this accidentally left values should also affect integers. Here when I change "char ch" to "int ch" and continues accepting values (to be inserted in LL) from user while(ch != 0), it works perfectly. When I input 0 as my choice, it accepts 0 exactly, and not a single bit of it as garbage. If there are some unread bits left in buffer, they should be read in the "int" too, giving a non-zero value even if we enter a 0.
          Please explain why this happening?
          Thank you.

          Comment

          • Banfa
            Recognized Expert Expert
            • Feb 2006
            • 9067

            #6
            That is easy to explain, when you ask for an integer \n is not a valid character however it is white space. scanf will skip white space looking for the start of a numeric type (integer %d or float %f) because that is how you would want a user interface to work. You would not want your user interface to fail to convert a number just because the user had typed a few spaces first. So although a \n is left in the input stream when you ask for a int %d scanf automatically discards it before reading the provided digit 0.

            However when you ask scanf for a character %c, \n is a perfectly valid character so that is what scanf returns.

            Try this program

            [code=c]
            #include <stdio.h>

            int main(int argc, char *argp[])
            {
            int n[3];
            char ans[3];

            printf("Input an integer: ");
            scanf ("%d",&n[0]);
            printf("\nInput another integer: ");
            scanf ("%d",&n[1]);
            printf("\nInput a final integer: ");
            scanf ("%d",&n[2]);
            printf("\nNow give me an answer yes or no (y/n):");
            scanf ("%c",&ans[0]);
            scanf ("%c",&ans[1]);
            scanf ("%c",&ans[2]);

            printf("\n\nI read the integers: %d %d %d\n", n[0], n[1], n[2]);
            printf("\n\nI read the character values: %d %d %d\n", ans[0], ans[1], ans[2]);

            return 0;
            }
            [/code]
            Note at the end although it asks for a single character the program then reads 3. The \n from the previous input and the \n from the current input with the actual input between them.

            Comment

            • Aftabpasha
              New Member
              • Oct 2008
              • 32

              #7
              @Banfa
              Thank you for your clear & informative replies.
              All of my confusions about those garbage values are removed now.
              The code you've provided was certainly helpful to me.
              Just a single question. It's about the code from you. Why have you used
              Code:
              int main(int argc, char *argp[])
              I mean you could have simply used
              Code:
              int main()
              in place of it.
              Is it a better practice to use such syntax?(Command Line Interface to your program).
              Thanks again.

              Comment

              • Banfa
                Recognized Expert Expert
                • Feb 2006
                • 9067

                #8
                I used it because of habit mainly, it is not better (or best) practice. I have been writing main like that for 20 years now so that is how I write it even if there is no command line parameters.

                Of course the fact that I never typed the main line of that program but copied it from another program could also have something to do with it :D

                Comment

                • Aftabpasha
                  New Member
                  • Oct 2008
                  • 32

                  #9
                  Well, I m just a beginner...and hope I'll also have some habit after 20 years (of course a good one).
                  Once again thank you all for your replies.

                  Comment

                  Working...