0

I currently am stuck on a small part of an assignment I need to do. One requirement of the assignment is

"Call a function that prompts the user for each of the values of the coefficients a, b, and c for the quadratic equation and returns the value entered, with error checking for a valid input (scanf returned a value)."

and I can't figure out how to do this. I can easily prompt the user for input and I can check if it is valid input, but I don't know how to turn this into a function. My current code is:

   {
   if (isalpha(a))
   {
      printf("INPUT ERROR!\n");
      printf("Enter a value for a: ");
      scanf("%d", &a);
   }
   } //this is how I would normally check the input

   int main(void) //start of main() function definition
   {
      int a, b, c, n, D; //declares integer variables a, b, c, n, and D
      float root1, root2; //declares float variables root1 and root2

      do //do while loop starts here
      {
         printf("Enter a value for a: "); //prompts user to input integer for variable 'a'
         scanf("%d", &a); //reads an integer from the keyboard and stores in the variable 'a' 
         printf("%d\n", a); //returns value of integer that was input for variable 'a'

         printf("Enter a value for b: "); //prompts user to input integer for variable 'b'
         scanf("%d", &b); //reads an integer from the keyboard and stores in the variable 'b'
         printf("%d\n", b); //returns value of integer that was input for variable 'b'

         printf("Enter a value for c: "); //prompts user to input integer for variable 'c'
         scanf("%d", &c); //reads an integer from the keyboard and stores in the variable 'c'
         printf("%d\n", c); //returns value of integer that was input for variable 'c'
         ...}

Sorry for any formatting mistakes, but that is basically the part of the program I am stuck with.

My question is, how can I combine the first function with everything in the do/while loop to make one big function that I can call three times?

I don't know how I'd be able to switch out all the instances of a for b and c using a function, as I've never really had to use a function like this before.

anothermh
  • 9,815
  • 3
  • 33
  • 52
rmgk
  • 41
  • 5
  • You could begin by checking what [`scanf`](https://en.cppreference.com/w/c/io/fscanf) *returns*. Or use [`fgets`](https://en.cppreference.com/w/c/io/fgets) together with [`strtol`](https://en.cppreference.com/w/c/string/byte/strtol). – Some programmer dude Oct 16 '18 at 07:32
  • You may find some help with [Why scanf(“%d”, &number) results 'a' as 29 in C programming?](https://stackoverflow.com/questions/47972360/why-scanfd-number-results-a-as-29-in-c-programming/47972568#47972568) and also [C - Trying to scanf hex/dec/oct values to check if they're equal to user input](https://stackoverflow.com/questions/47191895/c-trying-to-scanf-hex-dec-oct-values-to-check-if-theyre-equal-to-user-input/47193170#47193170) – David C. Rankin Oct 16 '18 at 08:42
  • "error checking for a valid input" can include issues like: non-numeric "xyz\n", empy "\n", extra "123 xyz\n", overflow "12345678901234567890\n", end-of-file, trailing whites space " 123 \n" and others. The code depends on how clean an input you want and what to do with the errant input. – chux - Reinstate Monica Oct 16 '18 at 14:17

5 Answers5

3

Function that prompts user for integer value and checks for valid input

If users only entered valid integer text on a line-by-line basis, then code is easy:

// Overly idealized case
fputs(prompt, stdout);
char buf[50];
fgets(buf, sizeof buf, stdin);
int i = atoi(buf);

But users are good, bad and ugly and **it happens. If code wants to read a line, parse it for an in-range int, and detect a host of problems, below is code that vets many of the typical issues of bogus and hostile input.

I especially interested in detecting overly long input as hostile and so invalid as a prudent design against hackers. As below, little reason to allow valid input for a 32-bit int with more than 20 characters. This rational deserve a deeper explanation.

  • End-of-file
  • Input stream error
  • Overflow
  • No leading numeric test
  • Trailing non-numeric text
  • Excessive long line

First a line of input is read with fgets() and then various int validation tests applied. If fgets() did not read the whole line, the rest is then read.

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

// Max number of `char` to print an `int` is about log10(int_bit_width)
// https://stackoverflow.com/a/44028031/2410359
#define LOG10_2_N 28
#define LOG10_2_D 93
#define INT_DEC_TEXT (1 /*sign*/ + (CHAR_BIT*sizeof(int)-1)*LOG10_2_N/LOG10_2_D + 1)

// Read a line and parse an integer
// Return:
//   1: Success
//   0: Failure
//   EOF: End-of-file or stream input error
int my_get_int(int *i) {
  // Make room for twice the expected need. This allows for some
  // leading/trailing spaces, leading zeros, etc.
  //           int        \n  \0
  char buf[(INT_DEC_TEXT + 1 + 1) * 2];
  if (fgets(buf, sizeof buf, stdin) == NULL) {
    *i = 0;
    return EOF; // Input is either at end-of-file or a rare input error.
  }
  int success = 1;

  char *endptr;
  errno = 0;
  long value = strtol(buf, &endptr, 10);


  // When `int` is narrower than `long`, add these tests
#if LONG_MIN < INT_MIN || LONG_MAX > INT_MAX
  if (value < INT_MIN) {
    value = INT_MIN;
    errno = ERANGE;
  } else if (value > INT_MAX) {
    value = INT_MAX;
    errno = ERANGE;
  }
#endif
  *i = (int) value;
  if (errno == ERANGE) {
    success = 0;  // Overflow
  }

  if (buf == endptr) {
    success = 0; // No conversion
  }

  // Tolerate trailing white-space
  // Proper use of `is...()` obliges a `char` get converted to `unsigned char`.
  while (isspace((unsigned char ) *endptr)) {
    endptr++;
  }

  // Check for trailing non-white-space
  if (*endptr) {
    success = 0; // Extra junk
    while (*endptr) {  // quietly get rest of buffer
      endptr++;
    }
  }

  // Was the entire line read?
  // Was the null character at the buffer end and the prior wasn't \n?
  const size_t last_index = sizeof buf / sizeof buf[0] - 1;
  if (endptr == &buf[last_index] && buf[last_index - 1] != '\n') {
    // Input is hostile as it is excessively long.
    success = 0;  // Line too long
    // Consume text up to end-of-line
    int ch;
    while ((ch = fgetc(stdin)) != '\n' && ch != EOF) {
      ;
    }
  }

  return success;
}

Sample usage

     puts("Enter a value for a: ", stdout);
     fflush(stdout);  // Insure output is seen before input.
     int a;
     if (my_get_int(&a) == 1) {
       printf("a:%d\n", a); 
     }
chux - Reinstate Monica
  • 143,097
  • 13
  • 135
  • 256
2

My question is, how can I combine the first function with everything in the do/while loop to make one big function that I can call three times?

Well, the function need not be big. The things to factor out are the prompt string and the variable to read - the latter can be left in the calling main() and assigned from a return value. Regarding how you would normally check the input, I recommend leaving this checking to scanf() and just test its return value.

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

int input(char *prompt)
{   // prompts user to input integer
    // reads an integer from standard input and returns it
    int a, s;   // leave it to scanf to check the input:
    while (printf("%s", prompt), fflush(stdout), s = scanf("%d", &a), !s)
    {
        printf("INPUT ERROR!\n");
        do s = getchar(); while (s != '\n' && s != EOF);    // consume bad input
    }
    if (s == EOF) puts(""), exit(0);    // no more input
    return a;
}

In main() you can then just do

     a = input("Enter a value for a: ");
     b = input("Enter a value for b: ");
     c = input("Enter a value for c: ");

(without a loop).

Armali
  • 18,255
  • 14
  • 57
  • 171
  • Fairly good `input(char *prompt)`. Although it does not handle a _line_ of input well, it nicely handles a _stream_ of input. – chux - Reinstate Monica Oct 16 '18 at 13:31
  • @chux - Would you care to elaborate on how _it does not handle a_ line _of input well_? – Armali Oct 17 '18 at 07:29
  • 1
    1) `scanf("%d", &a)` first consumes leading white-space including `'\n'`. When input is `"\n"`, that number-less line is not error prompted. 2) When input is "123 45x\n", `input()` returns 123 leaving the remaining _line_ of " 45x\n" in `stdin`. 3) Note out of range input like `"12345678901234567890"` invokes UB with `scanf("%d"...` – chux - Reinstate Monica Oct 17 '18 at 13:48
1

scanf() already processes the input for you according to the format specifier (%d) so you just need to understand how scanf works and use it to check and build your function :)

When you write scanf("%d", &a); the program expects you write an integer because of the %d specifier, and if an integer is read, the program writes it into variable a.

But the function scanf also has a return value, ie, you can do check = scanf("%d", &a); and check will have a value of 0 or 1 in this case. This is because the return value records how many values have been successfuly read. If you entered dsfugnodg there's no number so it would return 0. If you entered 659 32 it would read the 1st value successfully and return 1.

Your function would look something like:

#include <stdio.h>

int getAndPrint(char label)
{
    int n = 0, val = 0;
    do {
        printf("Enter a value for %c: ", label);
        n = scanf("%d", &val);
        if (n == 0) {
            printf("Error, invalid value entered.\n");
            /* Consume whatever character leads the wrong input 
             * to prevent an infinite loop. See: 
             * https://stackoverflow.com/questions/1669821/scanf-skips-every-other-while-loop-in-c */
            getchar();
        }
    } while (n == 0);
    printf("%c = %d\n", label, val);
    return val;
}

int main()
{
    int a, b, c;
    a = getAndPrint('a');
    b = getAndPrint('b');
    c = getAndPrint('c');
    printf("a=%d, b=%d, c=%d\n", a, b, c);
}

See also: Scanf skips every other while loop in C

chux - Reinstate Monica
  • 143,097
  • 13
  • 135
  • 256
div0man
  • 321
  • 1
  • 9
  • @chux fixed it. rmgk you're welcome and welcome to StackExchange. Hang around more and you'll notice that comments are for clarifications regarding the Q&A etc., and not for thank-yous :) – div0man Oct 17 '18 at 13:44
  • @chux how's it looking now? – div0man Oct 17 '18 at 14:16
  • @chux Wow, there's 2 things I didn't see there: 1. saving returned character into `c` is unnecessary, could have just done `getchar() != \n`; 2. that I don't have to care what I'm deleting because if any number followed the wrong input we wouldn't be at that place in the code anyway and it would have been read correctly. Thank you for the patience, I learned a lot. – div0man Oct 17 '18 at 15:14
0

I think the following code is you wanted:

#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>  // for isalpha

void InputAndCheck(int * pValue, const char * pName)
{
    do
    {
        printf("Enter a value for %s: ", pName);
        scanf("%d", pValue);
        if (isalpha(*pValue))
        {
            printf("INPUT ERROR!\n");
            continue;
        }
        else
        {
            break;
        }
    } while (1);

    // clear the input buffer
    fflush(stdin);
    return;
}

int main()
{
    int a, b, c;
    InputAndCheck(&a, "a");
    InputAndCheck(&b, "b");
    InputAndCheck(&c, "c");
    printf("a=%d;b=%d;c=%d;\r\n",a,b,c);
    return 0;
}
Jimmy
  • 223
  • 1
  • 11
-2

What you are looking for is an introduction to functions. Here is one : https://www.tutorialspoint.com/cprogramming/c_functions.htm

This is a very important building block in programming and you should definitely learn to master that concept.

functions will allow you to execute some code in different contexts over and over, just changing the context (the parameters).

It is declared like this

int add(int first, int second){
    //here we can use first and second
    printf("first: %d\n", first);
    printf("second: %d\n", second);

    //and eventually return a value
    return first+second;

}

Now when using we are reusing our previous code to excute a task which result will vary depending of the arguments we pass.

printf("1+2 = %d\n", add(1, 2));
-->3
printf("2+2 = %d\n", add(2, 2));
-->4

Example solution for your task:

//this will handle validation
int validateInput(int input){
    if(isalpha(input)){
        printf("INPUT ERROR!\n");
        return 0;
    }
    return 1;
}

//this will prompt the user and return input only if the input is valid
int askForCoefficient(unsigned char coefficientName){
    int valid = 0;
    int value = 0;
    while(!valid){
        printf("Enter a value for %c: ", coefficientName);
        value = scanf("%d", &value);
        valid = validateInput(value);

    }

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

    return value;

}
user2177591
  • 238
  • 2
  • 12
  • `value = scanf("%d", &value); valid = validateInput(value);` is strange code. That code passes 0, 1 or `EOF` to `validateInput(value)`, none of which are expected to be an _alpha_. – chux - Reinstate Monica Oct 17 '18 at 15:31