Hey guys,
Im learning C online, and its hard somtimes to understand the concepts, w/o being in class asking questions. I was wondering hat would be the easiest and fstest way to understand numeric validating(no characters) and could anyone break it in easy steps? im new, but really excited about C and will learn fast. I found this program in one of the books, but it doesnt really explain whats going on under the hood, if there is an easier way of doing it please let me know.
I dont understand things, that happens in the loop.,
Cheers, and thanx a lot for your time!!! )

#include<stdio.h>

int get_int(void);

main()

{

printf ("Enter an integer:");

get_int();

printf ("Good!\n");

}

int get_int(void)

{

int input;
char ch;

    while (scanf("%d", &input) != 1)

    {

        while ((ch = getchar()) != '\n')

            putchar(ch);  // dispose of bad input

        printf(" is not an integer.\nPlease enter an ");

        printf("integer value, such as 25, -178, or 3: ");

    }

    return input;

}

Recommended Answers

All 5 Replies

#include<stdio.h> // include header

int get_int(void); // Function prototype is to be declared as the function is defined after main()

main() // Change to int main()

{

printf ("Enter an integer:");

get_int(); // Call function. No error as the function was declared previously

printf ("Good!\n");

}

int get_int(void)

{

int input;
char ch;

    while (scanf("%d", &input) != 1) // scanf returns the no of items successfully read. If user inputs a character then it won't be read into 'input' and scanf returns 0.

    {

        while ((ch = getchar()) != '\n') // Get all the characters given by the user (that is till '\n' was entered) then use putchar() to print it on the screen

            putchar(ch);  // dispose of bad input

        printf(" is not an integer.\nPlease enter an ");

        printf("integer value, such as 25, -178, or 3: "); // go back to the while loop where scanf will take the input from the user again 

    }

    return input;

}

it doesnt really explain whats going on under the hood

Ok, it's a simple but not a strong validation. For example, in most of C implementations scanf can't detect integer overflow (it's possible to type a huge number and to get some wrong number with scanf(...) == 1).
More strong (but not ideal) method is (an example only):

#include <limits.h>
#include <float.h>
...
int input;
double x;
...
if (scanf("%f",&x) == 1 && modf(x,&x) == 0.0 
    && x <= (double)INT_MAX && x >= (double)INT_MIN) {
    /* OK */
    input = x;
} else {
    /* not-a-number or integer overflow */
}

>More strong (but not ideal) method is (an example only)
Only an example because by the time you test for overflow, the damage is already done. Keep in mind that once you invoke undefined behavior, the rest of the program is undefined. You need to test before the damage is done either with a library function that's smarter (such as strtol) or by doing it all manually (which I don't recommend unless you know what you're doing):

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

const char *parse_int ( const char *s, int *value )
{
  /* Base of the final converted value */
  const unsigned base = 10;

  /* Largest possible value without the least significant digit */
  const unsigned limit = UINT_MAX / base;

  /* Least significant digit from the largest possible value */
  const unsigned top_digit = UINT_MAX % base;

  unsigned overflow = 0; /* True if integer overflow occurs */
  unsigned sign = 0;     /* Final sign of the converted value */

  unsigned temp = 0;     /* The intermediate converted value */
  unsigned n = 0;        /* Count of converted digits */

  /* Save and skip over the sign if present */
  if ( *s == '-' || *s == '+' )
    sign = *s++ == '-';

  /* Build the intermediate value */
  for ( ; isdigit ( *s ); s++, n++ ) {
    unsigned digit = *s - '0';
    
    /*
      This protects *only* the intermediate value
      from overflow. Overflow of the final value
      requires further checks
    */
    overflow =  temp > limit
      || ( temp == limit && digit > top_digit );

    if ( overflow )
      break;

    /* Shift-add by the base */
    temp = temp * base + digit;
  }

  if ( n > 0 ) {
    /*
      A conversion was made, so now we need to
      deal with overflow and set the final value
    */
    if ( overflow
      || ( sign && temp > -INT_MIN )
      || ( !sign && temp > INT_MAX ) )
    {
      /*
        The intermediate actually overflowed,
        or converting it to int would overflow.
        Either way it's an error to the caller
      */
      errno = ERANGE;
      temp = sign ? -INT_MIN : INT_MAX;
    }

    *value = sign ? -(int)temp : (int)temp;
  }
  else if ( sign ) {
    /*
      We found a sign and skipped over it. But
      because no conversion was made, we need
      to "unskip" the sign
    */
    --s;
  }

  return s;
}

#include <stdio.h>
#include <string.h>

int main ( void )
{
  char buffer[20];

  while ( fgets ( buffer, sizeof buffer, stdin ) != NULL ) {
    int x;
    const char *p;

    buffer[strcspn ( buffer, "\n" )] = '\0';
    
    errno = 0;
    p = parse_int ( buffer, &x );

    if ( errno != 0 )
      puts ( "Overflow!" );

    if ( *p == '\0' )
      puts ( "Successful conversion!" );
    else
      printf ( "Unconverted: \"%s\"\n", p );

    if ( p != buffer )
      printf ( "Value = %d\n", x );
    else
      puts ( "No value found" );
  }

  return 0;
}

Notice that both of these solutions rely on unformatted input using fgets and then a more precise validation of the string in memory. It's much easier to validate your input without having to worry about mucking with the stream at the same time.

>by the time you test for overflow, the damage is already done
No any damage (in 32-bit environment ;) ) at this moment because an example code tests possible integer (not double) overflow (compare int and IEEE double ranges). More robust test requires _finite call to detect possible double under/overflow:

#include <limits.h>
#include <float.h>
... // For 32-bit int type only?
int input;
double x;
... // must be %lf, sorry...
if (scanf("%lf",&x) == 1 && _finite(x) && modf(x,&x) == 0.0 
    && x <= (double)INT_MAX && x >= (double)INT_MIN) {
    /* OK */
    input = x;
} else {
    /* not-a-number or integer overflow */
}

Of course, strtol+errno check looks better (simpler) but strtol requires int representation buffering.

the program u wrote checks whether the entered input is an integer or character
if it is an inter it returns "good".
Basically you are using the
"int get_int(void)"
to call a function to perform your task.

while (scanf("%d", &input) != 1)

    {

        while ((ch = getchar()) != '\n')

            putchar(ch);  // dispose of bad input

        printf(" is not an integer.\nPlease enter an ");

        printf("integer value, such as 25, -178, or 3: ");

    }

    return input;

the above part of the program is as:

1.the program continues ubtil u enter 1
2.if u enter any character or string your last character is always "\n" that is automatically added in the last of the string u write.
so the program continues until the end if the string
3.when while loop continues it prints the character u entered in the string since putchar is used to print the char on the output screen.

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.