0

basically in codeblocks for windows before each printf I have "fflush(stdin);" which works. When I copied my code to Linux, it doesn't work, nor does any of the alternatives for "fflush(stdin);" that I've found. No matter which way I seem to do it, the input doesn't seem to be clearing in the buffer or something in my code is incorrect.

#include <stdio.h>
#include <math.h>
#include <limits.h>
#include <ctype.h>

int main()
{
   char pbuffer[10], qbuffer[10], kbuffer[10];
   int p=0, q=0, k=0;
   int r, i, Q, count, sum;
   char a[3];
   a[0]='y';
   while(a[0]=='y' || a[0]=='Y')
   {
      printf("Enter a p value: \n");
      fgets(pbuffer, sizeof(pbuffer), stdin);
      p = strtol(pbuffer, (char **)NULL, 10);

      printf("Enter a q value: \n");
      fgets(qbuffer, sizeof(qbuffer), stdin);
      q = strtol(qbuffer, (char **)NULL, 10);

      printf("Enter a k value: \n");
      fgets(kbuffer, sizeof(kbuffer), stdin);
      k = strtol(kbuffer, (char **)NULL, 10);

      while(p<q+1)
      {
         Q=p;
         sum=0;
         count=0;
         while(Q>0)
         {
            count++;
            r = Q%10;
            sum = sum + pow(r,k);
            Q = Q/10;
         }

         if ( p == sum && i>1 && count==k )
         {
            printf("%d\n",p);

         }
         p++;
         a[0]='z';
      }
      while((a[0]!='y') && (a[0]='Y') && (a[0]!='n') && (a[0]!='N'))
      {
         printf("Would you like to run again? (y/n) ");
         fgets(a, sizeof(a), stdin);
      }
   }
   return 0;
}
Andrew Ricci
  • 475
  • 5
  • 21
  • @PaulR , you are I belive mistaken. Conforming to These functions are nonstandard and not portable. The function fpurge() was introduced in 4.4BSD and is not available under Linux. The function __fpurge() was introduced in Solaris, and is present in glibc 2.1.95 and later. http://linux.die.net/man/3/fpurge – Satya Sep 23 '14 at 14:54
  • With `a` defined as a `char` your input is **WRONG** (`scanf(" %s", &a);`): it will try to write at least the `'\0'` character in the memory position after where `a` is located. That memory position does not belong to you. – pmg Sep 23 '14 at 15:23
  • @Satya: you may be right - I'll delete my comment. – Paul R Sep 23 '14 at 16:26
  • Please note that this behavior of `fflush` is not that widely supported and not really compatible with the usual meaning of "flush". There's really no standard, portable way to clear the input stream without reading from it. – John Bode Sep 23 '14 at 19:12
  • BTW: `pow(r,k);` is likely a problem. Lots of SO posts on that one. Use something like `unsigned powuu(unsigned x, unsigned y) { unsigned z = 1u; unsigned base = x; while (y) { if (y & 1u) { z *= base; } y >>= 1u; base *= base; } return z; }` – chux - Reinstate Monica Sep 23 '14 at 22:13

3 Answers3

2

Calling fflush(stdin) is not standard, so the behavior is undefined (see this answer for more information).

Rather than calling fflush on stdin, you could call scanf, passing a format string instructing the function to read everything up to and including the newline '\n' character, like this:

scanf("%*[^\n]%1*[\n]");

The asterisk tells scanf to ignore the result.

Another problem is calling scanf to read a character into variable a with the format specifier of " %s": when the user enters a non-empty string, null terminator creates buffer overrun, causing undefined behavior (char a is a buffer of one character; string "y" has two characters - {'y', '\0'}, with the second character written past the end of the buffer). You should change a to a buffer that has several characters, and pass that limit to scanf:

char a[2];
do {
    printf("Would you like to run again? (y/n) \n")
    scanf("%1s", a);
} while(a[0] !='y' && a[0] !='Y' && a[0]!='n' && a[0]!='N' );
}
Community
  • 1
  • 1
Sergey Kalinichenko
  • 714,442
  • 84
  • 1,110
  • 1,523
  • When I add that scanf function, it gets a bit more whacky. It seem that the more characters I enter, it becomes input for the future input(q and k value). – Andrew Ricci Sep 23 '14 at 15:04
  • @AndrewRicci The space in front of `%s` in `" %s"` looks highly suspicious, too. – Sergey Kalinichenko Sep 23 '14 at 15:13
  • Or he could keep `a` as a single `char` and use `" %c"`. – John Bode Sep 23 '14 at 15:28
  • `scanf("%*[^\n]\n");` will scan non-`'\n'`, and if finding one will scan for _any number_ of white-space and will _not_ return until a following non-white-space is entered. Better to use `scanf("%*[^\n]%1*[\n]");` – chux - Reinstate Monica Sep 23 '14 at 15:29
  • Hmmmm. `scanf(any_format, ...);` has the problem that if there is no input presently in the input buffer to flush, `scanf()` will not return until there is _something_. This might not meet OP's needs. – chux - Reinstate Monica Sep 23 '14 at 15:45
0

Drop the need for flushing the input buffer.

OP is on the right track using fgets() rather than scanf() for input, OP should continue that approach with:

char a;
while(a !='y' && a !='Y' && a!='n' && a!='N' ) {

  printf("Would you like to run again? (y/n) \n");
  if (fgets(kbuffer, sizeof(kbuffer), stdin) == NULL) 
    Handle_EOForIOerror();

  int cnt = sscanf(kbuffer, " %c", &a); // Use %c, not %s
  if (cnt == 0) 
    continue; // Only white-space entered
  }

Best to not use scanf() as it tries to handle user IO and parsing in one shot and does neither that well.


Certain present OP's woes stem from fgets() after scanf(" %s", &a); (which is UB as it should be scanf(" %c", &a);. Mixing scanf() with fgets() typically has the problem that the scanf(" %c", &a); leaves the Enter or '\n' in the input buffer obliging the code to want to flsuh the input buffer before the next fgets(). Else that fgets() gets the stale '\n' and not a new line of info.

By only using fgets() for user IO, there need for flushing is negated.


Sample fgets() wrapper

char *prompt_fgets(const char *prompt, char dest, long size) {
  fputs(prompt, stdout);
  char *retval = fgets(dest, size, stdin);
  if (retval != NULL) {
    size_t len = strlen(dest);
    if (len > 1 && dest[len-1] == '\n') { // Consume trailing \n
      dest[--len] = '\0';
    }
    else if (len + 1 == dest) {  // Consume extra char
      int ch;
      do {
        ch == fgetc(stdin);
      } while (ch != '\n' && ch != EOF);
    }
  return retval;
  }  
chux - Reinstate Monica
  • 143,097
  • 13
  • 135
  • 256
  • You're right, I don't know why I even though to mix it...anyhow, as I fixed that, my problem still persists where after the first run through and choosing to run again, I'm prompted with "Enter a p value" and "Enter a q value:" directly below it waiting for prompt. – Andrew Ricci Sep 23 '14 at 18:28
  • Suspect you are using something like `printf("Would you like to run again? (y/n) \n"); if (fgets(buffer, 2, stdin) ...` where `buffer` is too small. (Needs to be at least 3.) Otherwise I'd would have to see your corrected code. See if the buffer was too small, the `'\n'` for this prompt is still left in the input buffer and the next `fgets()` takes that in without waiting for another line of input - hence the 2 prompts of "Enter a p value" and "Enter a q value:", – chux - Reinstate Monica Sep 23 '14 at 18:37
  • I just updated my code. It is still acting weird. Generally when I input a larger set of characters it ether skips prompts and loops over again. I may just start over, I'm wondering if you could point me into a direction of secure ways of coding/handling input. – Andrew Ricci Sep 23 '14 at 19:28
  • 1) Not good to alter your post in such a ways that it makes answers look like they are addressing the wrong issues. Better to append your changes or even post a new question if this one is answered. 2) Suggest getting your user input via a helper function like in this edited answer. Likely more than you need. The idea is to encapsulate user prompt/input in one general purpose function. After each prompt, user input up to a `'\n'` should be consumed – chux - Reinstate Monica Sep 23 '14 at 20:03
0

I think what you are trying to do is more difficult than it seems.

My interpretation of what you are trying to do is disable type ahead so that if the user types some characters while your program is processing other stuff, they don't appear at the prompt. This is actually quite difficult to do because it is an OS level function.

You could do a non blocking read on the device before printing the prompt until you get EWOULDBLOCK in errno. Or the tcsetattr function family might help. It looks like there is a way to drain input for a file descriptor in there, but it might interact badly with fgets/fscanf

A better idea is not to worry about it at all. Unix users are used to having type ahead and what you want would be unexpected behaviour for them.

JeremyP
  • 84,577
  • 15
  • 123
  • 161