code to convert any base input to any base output (base limited upto hexadecimal)

scanf() works , but gets() stops working from 2nd iteration

i apolozise beforehand for the length of this post, i hope there will be someone who'll bear with me :(

here is my code, the one with scanf(). this one has no problems and works just fine. :)

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


int entry();// gets any base input and converts it to decimal

int main(void){

int ip_num,ip_base,op_num,op_base,i=0,cont_flag,acc[20];

char base_digits[16] = {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'};

do{
ip_num=entry();

printf("choose the base to convert to:");
scanf("%d",& op_base);


puts("\nin main()\n\nexecuting for() loop . . .\n\n");
for(i=0;i<20 && ip_num!=0;i++) // converts to required o/p base
{

acc[i]=(ip_num % op_base);
ip_num=ip_num/op_base;
printf("index: %d \n",i);

if(ip_num==0)
break;

if(i==19) {
puts("array acc[] full");
break;
}

}//end of for()

printf("\nconverted number in base %d is:",op_base);

for(i;i>=0;i--)// prints the number 
{
printf("%c",base_digits[acc[i]]);
}

printf("\n\n convert another number? press\n 1(yes)\n 0(no)\n:");
scanf("%d",& cont_flag);
}while(cont_flag==1);
}


int entry()
{
 char sh[100],*stop;
 int ip_num,ip_base;
printf("Enter a number: ");
scanf("%s",&sh);

printf("enter radix: ");
scanf("%d",&ip_base); 

 ip_num = strtol(sh,&stop,ip_base);
 return(ip_num);
}

everything works fine. entry() has an strtol() function in its heart that converts any input to decimal, and then the main() takes that decimal and converts it to the desired base , as indicated by op_base.

THE ONLY PROBLEM :

its when i change entry() in the following manner:

int entry()
{
 char sh[100],*stop;
 int ip_num,ip_base;
printf("Enter a number: ");
gets(sh);

printf("enter radix: ");
scanf("%d",&ip_base); 

 ip_num = strtol(sh,&stop,ip_base);
 return(ip_num);
}

the gets() takes the staring as it supposed it would in the 1st iteration, but from the 2nd iteration onwards it just skips by the whole printf("Enter a number: "); and goes direct to printf("enter radix: "); without taking any input for the number.

n.b by iteration, i mean a do-while loop in the form

do{

// run the entire code

printf("\n\n convert another number? press\n 1(yes)\n 0(no)\n:");
scanf("%d",& cont_flag);
}while(cont_flag==1);

any help to understanding this problem would be great! :)
thanks
somjit.

Edited 4 Years Ago by somjit{}: typo..

I only glanced over your post, but I'm 99.9% sure that it's the classic stream garbage problem when mixing formatted and unformatted input.

You see scanf() is special in that most of the time it will discard leading whitespace from the stream and leave trailing whitespace. gets() on the other hand, doesn't discard leading whitespace and will stop reading when it encounters a newline. So what happens when you use scanf() followed by gets() when scanf() leaves a newline in the stream?

You guessed it: it appears that gets() is skipped because it terminates successfully on the first character, the newline. The following program exhibits the problem clearly:

#include <stdio.h>

int main(void)
{
    char s[BUFSIZ];
    int d;

    printf("Enter a number: ");
    fflush(stdout);

    while (scanf("%d", &d) == 1)
    {
        printf("You entered %d. Enter your name: ", d);
        fflush(stdout);

        if (fgets(s, sizeof s, stdin))
        {
            printf("You entered '%s'\n", s);
        }

        printf("Enter a number: ");
        fflush(stdout);
    }

    return 0;
}

There are two common fixes:

  1. Don't use scanf(). Always use some variant of fgets() to read lines from the stream and then parse them using sscanf(). This way the stream is always kept pristine for fgets().

    #include <stdio.h>
    
    int main(void)
    {
        char s[BUFSIZ];
        int d;
    
        printf("Enter a number: ");
        fflush(stdout);
    
        /* Note that s is being reused here as the temporary line for d */
        while (fgets(s, sizeof s, stdin) && sscanf(s, "%d", &d) == 1)
        {
            printf("You entered %d. Enter your name: ", d);
            fflush(stdout);
    
            if (fgets(s, sizeof s, stdin))
            {
                printf("You entered '%s'\n", s);
            }
    
            printf("Enter a number: ");
            fflush(stdout);
        }
    
        return 0;
    }
    
  2. Clean out garbage characters after calling scanf(). This is hackish and also hinders same line input for multiple calls, but it has the benefit of simplicity.

    #include <stdio.h>
    
    int main(void)
    {
        char s[BUFSIZ];
        int d;
    
        printf("Enter a number: ");
        fflush(stdout);
    
        while (scanf("%d", &d) == 1)
        {
            /* Clear the "garbage" from the stream */
            while (getchar() != '\n') { /* All work done in the condition */ }
    
            printf("You entered %d. Enter your name: ", d);
            fflush(stdout);
    
            if (fgets(s, sizeof s, stdin))
            {
                printf("You entered '%s'\n", s);
            }
    
            printf("Enter a number: ");
            fflush(stdout);
        }
    
        return 0;
    }
    

It's very situational, so which one you choose, or any variation, or even something completely different, depends on your needs at the time and for that application. But the above are the two most common solutions to the problem. You'll find that beginners tend to prefer #2 and more experience programmers prefer #1.

To start with scanf("%s",&sh); is incorrect it should be scanf("%s",sh); because sh is an array so using it without any [] already produces a pointer to it, you don't need to de-reference it.

Secondly, never, ever use gets, always use fgets, so not gets(sh); but fgets(sh, sizeof sh, stdin); it is safer since you can't overwrite the buffer, but note that gets does not return the '\n' at the end of the line and fgets does.

So the problem is mixing scanf and gets. gets reads up to the first '\n' character in the stream, scanf reads a data to fit the provided format string, skipping white space. So when you make this call scanf("%d",& cont_flag); it leaves a '\n' in the stream since you didn't tell it to read it in any way and the following call to gets reads the '\n' that is left in the stream and returns immediately. When you use scanf("%s",&sh); it is not looking for '\n' characters so it just skips it as whitespace and looks for actual data.

Mixing input methods nearly always leads to confusion.

Secondly, never, ever use gets, always use fgets, so not gets(sh); but fgets(sh, sizeof sh, stdin); it is safer since you can't overwrite the buffer,...

And the exact same thing goes for using scanf() to read strings. It's as bad as gets().

@deceptikon

thanks a lot for those example codes :) but as i was going through them.. u showed the use of fflush(stdout). a bit confusion here, coz i read [in this page](http://www.gidnetwork.com/b-57.html) that using fflush is a bad thing. although they only mentioned it for the case of fflush(stdin).

the page said

You've been programming for a couple years now and are in the habit of flushing stdin because you always use scanf() (another function you should avoid*). You get a new job that has a compiler without this particular enhancement. Your programs no longer work. You will spend days trying to figure out what's wrong because you 'know' fflush() can't be the problem -- you've used it for years.

does the same thing apply for flushing output stream as you had shown??

@ banfa

To start with scanf("%s",&sh); is incorrect it should be scanf("%s",sh); because sh is an array so using it without any [] already produces a pointer to it, you don't need to de-reference it.

Secondly, never, ever use gets, always use fgets, so not gets(sh); but fgets(sh, sizeof sh, stdin); it is safer since you can't overwrite the buffer, but note that gets does not return the '\n' at the end of the line and fgets does.

thanks for pointing them out :)
also thanks for the neat n trim analysis of my problem there :)

@WaltP

And the exact same thing goes for using scanf() to read strings. It's as bad as gets().

yeah i read that scanf() for reading strings was a bad thing to do.. that why started with gets(). im reading into fgets() now. :) thanks for the help :)

u showed the use of fflush(stdout). a bit confusion here, coz i read in this page that using fflush is a bad thing. although they only mentioned it for the case of fflush(stdin).

Kudos for critical thinking and an eye for detail. :) The difference between good and bad with fflush() is indeed in the argument. fflush() is defined only for output streams or update streams presently in an output orientation. It's not designed for input streams, and technically, passing an input stream or update stream in input orientation to fflush() invokes undefined behavior.

So fflush(stdout) is fine and dandy while fflush(stdin) is undefined behavior.

Note that the two are totally different in expectations as well. When someone uses fflush(stdin), they typically expect the functionality to be akin to this:

if (the stream contains characters)
{
    /* Extract and discard excess characters */
    int ch;

    while ((ch = getc(stdin)) != '\n' && ch != EOF)
        ;
}

The "if the stream contains characters" part is not possible without delving into non-portable options. But the concept of "flushing" an input stream is usually reading and throwing away characters until the stream is empty.

fflush(stdout) on the other hand is all about writing unwritten characters. If you send a prompt to the screen, flushing stdout will force that prompt to be displayed immediately. If you're writing to a file, flushing the file stream will force any buffered characters to be written to the file. This is what fflush() is defined to do, and why attempting to flush an input stream is nonsensical.

In my example code you'll notice a pattern: I always call fflush(stdout) after a printf() call where the last character is not a newline. This is because C defines basically three times when an output stream will be flushed:

  1. When the stream buffer is full.
  2. When a newline is send to the stream.
  3. When fflush() is called on the stream.

You don't have any control over #1, and #2 isn't ideal when displaying prompts where you want user input to be on the same line as the prompt. Thus I used fflush() to ensure that the prompt was displayed for the user.

Wait, why do all of this in the first place? If you remove the calls to fflush() on a Windows box, the behavior likely won't change. This is because the C runtime library often used will do the flush for you. But that's not required behavior; if you do the same test on a Linux or Unix box then the likelihood of your prompt not showing up before scanf() or fgets() block for input is pretty good (at least it was when I last checked).

So the fflush() calls are insurance that my code will always work the way I intended it to work. With a knowledge of the C standard, I've written slightly more complicated code with the goal of making it 100% portable.

Edited 4 Years Ago by deceptikon

@deceptikon

brother's wedding going on, that, coupled with him letting me play with his leica m8 while he's busy being the groom is turning out to a major distraction. :D its the reason for this late reply.

and regarding ur post, thank you so so much!! i tried to understand what that page was saying abt fflush being bad and all but wasnt very successful. u made it so much clearer!! thanks a lot! :) will be getting back to C within the end of the week . :) thanks for helping me out with such detailed answers and explanations :) really really helpful this was.

regards
somjit.

Edited 4 Years Ago by somjit{}

@deceptikon

brother's wedding going on, that, coupled with him letting me play with his leica m8 while he's busy being the groom is turning out to a major distraction. :D its the reason for this late reply.

There is no need to apologize for late replys. We are not timing anyone. What makes it late anyway? For all we care, 2 weeks later is not even a late reply.

i marked this thread as solved, just a question came up in my mind,
@deceptikon:
why use fflush() after every printf() ?

i read Here about fflush() which said if the last i/o operation was an output operation, any unwritten data in the output buffer is written to the stream pointed in fflush(file *stream) , but i fail to understand it in context of these statements that you used above to clarify problems of using gets() after scanf()..

   printf("Enter a number: ");
   fflush(stdout);

any help ?

thanks again
somjit.

Edited 4 Years Ago by somjit{}

It's because some compilers only do the actual output when either
1) the output buffer is full.
2) you fflush() the buffer.

Most of the compilers we run across on the forums must have the extension written to flush the buffer before returning from the printf(), but it's not something you can count on for all compilers.

This question has already been answered. Start a new discussion instead.