2

writing a program that will be finding min, max, avg of values entered by user. Having trouble writing something that will check to make sure there are only postive integers entered and produce an error message. heres my for statement that is reading the input so far:

  for (int value = 0; value <= numofvals; ++value) {
      printf("Value %d: %f\n", value, val_input);
      scanf("%f", &val_input);
  }

mind you I've been learning code for about 3 weeks and was just introduced to loops this week so my understanding is rudimentary at best!

8
  • How do you print a val_input that is read from stdin the line *after* your printf()? Commented Oct 11, 2018 at 23:32
  • 1
    You need to use an if-then statement after you read in the value to validate that the input conforms to whatever you (and more importantly, your program) expects. This is something that you'll end up spending lots of work doing in programming, since invalid input can be the cause of severe security issues and other bugs. Basic rule is to NEVER trust user input and always validate everything. Commented Oct 11, 2018 at 23:33
  • 1
    You can also check if ((val_input >> (sizeof val_input * CHAR_BIT - 1)) & 1) to check whether the sign-bit is set (bit 31 for single precision floating point and int or bit 63 for double precision floating point and 8-byte integer values) CHAR_BIT is the number of bits-per-character (generally 8) and is defined in limits.h. Commented Oct 12, 2018 at 1:53
  • What should happen if more than 1 number enter on a line? Commented Oct 12, 2018 at 13:49
  • What should happen with numeric input like 1.234 that is not an integer? Commented Oct 12, 2018 at 13:51

3 Answers 3

3

First, don't use scanf. If stdin doesn't match what it expects it will leave it in the buffer and just keep rereading the same wrong input. It's very frustrating to debug.

const int max_values = 10;

for (int i = 0; i <= max_values; i++) {
    int value;
    if( scanf("%d", &value) == 1 ) {
        printf("Got %d\n", value);
    }
    else {
        fprintf(stderr, "I don't recognize that as a number.\n");
    }
}

Watch what happens when you feed it something that isn't a number. It just keeps trying to read the bad line over and over again.

$ ./test
1
Got 1
2
Got 2
3
Got 3
foo
I don't recognize that as a number.
I don't recognize that as a number.
I don't recognize that as a number.
I don't recognize that as a number.
I don't recognize that as a number.
I don't recognize that as a number.
I don't recognize that as a number.
I don't recognize that as a number.

Instead, use fgets to reliably read the whole line and sscanf to parse it. %f is for floats, decimal numbers. Use %d to recognize only integers. Then check if it's positive.

#include <stdio.h>

int main() {
    const size_t max_values = 10;
    int values[max_values];
    char buf[1024];

    size_t i = 0;
    while(
        // Keep reading until we have enough values.
        (i < max_values) &&
        // Read the line, but stop if there's no more input.
        (fgets(buf, sizeof(buf), stdin) != NULL)
    ) {
        int value;

        // Parse the line as an integer.
        // If it doesn't parse, tell the user and skip to the next line.
        if( sscanf(buf, "%d", &value) != 1 ) {
            fprintf(stderr, "I don't recognize that as a number.\n");
            continue;
        }

        // Check if it's a positive integer.
        // If it isn't, tell the user and skip to the next line.
        if( value < 0 ) {
            fprintf(stderr, "Only positive integers, please.\n");
            continue;
        }

        // We got this far, it must be a positive integer!
        // Assign it and increment our position in the array.
        values[i] = value;
        i++;
    }

    // Print the array.
    for( i = 0; i < max_values; i++ ) {
        printf("%d\n", values[i]);
    }
}

Note that because the user might input bad values we can't use a simple for loop. Instead we loop until either we've read enough valid values, or there's no more input.

Sign up to request clarification or add additional context in comments.

21 Comments

@Acorn: If you aren't dealing with interactive input, you don't need prompts.
@Acorn We get so many questions about scanf that boil down to "scanf left stuff on the buffer" and "check the return value". So many. So very many.
@Acorn It's an indication of how often people get caught by scanf's gotchas.
@Acorn -- "don't use scanf()" is very good advice, especially for learners. This function is difficult to use correctly, and has so many dark corners that even those who know what they are doing can get caught by its quirks. There is almost always a better solution than scanf().
@Acorn -- it is easier, and more flexible. Using fgets() followed by sscanf() means that lines of input are fetched and stored in a generously sized buffer which can be scanned again when input does not match initial expectations. The ability to rescan input makes it more flexible. And unless the input was too large, stdin will be empty after the call to fgets() (hence the generously sized buffer), meaning that there is no need for the code that cleans the input stream.
|
1

Something easy like this may work for you:

int n;
int ret;

for (;;) {
    ret = scanf("%d", &n);

    if (ret == EOF)
        break;

    if (ret != 1) {
        puts("Not an integer");
        for (;;)
            if (getchar() == '\n')
                break;
        continue;
    }

    if (n < 0) {
        puts("Not a positive integer");
        continue;
    }

    printf("Correct value %d\n", n);

    /* Do your min/max/avg calculation */
}

/* Print your results here */

This is just an example and assumes you do not need to read floating point numbers and then check if they are integers, as well as a few other things. But for starters, it is simple and you can work on top of it.

To break out of the loop, you need to pass EOF (typically Ctrl+D in Linux/macOS terminals, Ctrl+Z in Windows ones).

Comments

-2

An easy and portable solution

#include <limits.h>
#include <stdio.h>
int get_positive_number() {
  char buff[1024];
  int value, ch;
  while (1) {
    printf("Enter positive number: ");
    if (fgets(buff, 1023, stdin) == NULL) {
      printf("Incorrect Input\n");
      // Portable way to empty input buffer
      while ((ch = getchar()) != '\n' && ch != EOF)
        ;
      continue;
    }
    if (sscanf(buff, "%d", &value) != 1 || value < 0) {
      printf("Please enter a valid input\n");
    } else {
      break;
    }
  }
  return value;
}
void solution() {
  // Handling malformed input
  // Memory Efficient (without using array to store values)
  int n;
  int min = INT_MAX;
  int max = INT_MIN;
  double avg = 0;
  printf("Enter number of elements: ");
  scanf("%d", &n);
  getc(stdin);
  int value;
  for (int i = 0; i < n; i++) {
    value = get_positive_number();
    if (value > 0) {
      if (min > value) {
        min = value;
      }
      if (max < value) {
        max = value;
      }
      avg += value;
    }
  }
  avg = avg / n;
  printf("Min = %d\nMax = %d\nAverage = %lf\n", min, max, avg);
}
int main() {
  solution();
  return 0;
}
  • Output:


Enter number of elements: 3
Enter positive number: 1
Enter positive number: 2
Enter positive number: a
Please enter a valid input
Enter positive number: -1
Please enter a valid input
Enter positive number: 1
Min = 1
Max = 2
Average = 1.333333

12 Comments

Not my downvote, but…the fseek(stdin, 0, SEEK_END); line is highly debatable. Many times, stdin is not a seekable device. Skipping the entire input file because of an error in one line may be too draconian. (See also Using fflush(stdin) for more spin on this topic.) I'd also prefer to see printf("%d\n", n); with the newline at the end of the format string.
This does not print any error message nor reads the values for processing, simply searches for the first correct one.
You're really better off using fgets and sscanf then trying to patch up scanf.
Note that the new code ("an easy way") runs into problems if the user only indicates EOF (n is used uninitialized), or if the user types a letter (or other non-numeric characters) instead of a number. You must check the return value from scanf(). If scanf() returns EOF, you should probably abandon the loop; if it returns 0, you should probably think about reading characters up to the next newline, or maybe until the next character that could be numeric — a +, - or a digit when reading integers. Robust input is depressingly hard.
This code has undefined behavior when the input is malformed, e.g., if the user enters letters instead of numbers.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.