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

int getInt() {
    int n, check;
    char ch;
    do {
        fflush(stdin);
        printf("Enter positive integer n = ");
        check = scanf("%d%c", &n, &ch);
        if ((check == 2)&&(ch == '\n')) {
            if (n > INT_MAX) {
                printf("Invalid number\n");
            } else if (n < 0) {
                printf("n must > 0");
            } else {
                break;
            }
        } else printf("Invalid number\n");
    } while (1);
    return n;
}

int main(int argc, char** argv) {
    int n;
    n = getInt();
}

My code accepts user input number in range 0 to INT_MAX.

When I input -1, program displays "n must > 0".

But when I input '77777777777777777777777777777777' (> INT_MAX) program still displays "n must > 0", and not 'Invalid number'.

Roberto Caboni
  • 7,252
  • 10
  • 25
  • 39
Đức Long
  • 117
  • 1
  • 9
  • 4
    `n` is an `int`. An `int` can't represent values greater than `INT_MAX`, so `n > INT_MAX` is always false. I'd expect that the compilers optimizes out that option completely. – Thomas Jager Jul 11 '20 at 13:06
  • do you know anyway to limits user input number in range INT, if user too long, program will failed – Đức Long Jul 11 '20 at 13:12
  • 4
    You could read the number as a string and count the number of digits by using `strlen` and/or `isdigit`. But using [`strtol`](https://en.cppreference.com/w/c/string/byte/strtol) is probably easiest, as it explicitly tells you if the number is out of range, by setting `errno` to `ERANGE`. – Andreas Wenzel Jul 11 '20 at 13:21
  • 1
    `scanf` a long or long long and check against INT_MAX. As the max value of int is MAX_INT, you cannot check against that. – Paul Ogilvie Jul 11 '20 at 13:39
  • 1
    One way to do what you want is to use `strtol` to convert it to a `long`. Then check 2 things: (1) If the entire string was consumed, and (2) If the `long` is in the range of an `int`. If so, then you can cast it to an `int` and you have your value. – Tom Karzes Jul 11 '20 at 14:18
  • 1
    @TomKarzes `strtoll()` (`long long`) would be better than `strtol()`. A `long` is the same size as an `int` on almost all 32-bit platforms - **and on 64-bit Windows**. – Andrew Henle Jul 11 '20 at 14:22
  • @AndreasWenzel your comment, taking into account also AndrewHenle 's suggestion, could be easily transformed into an answer. – Roberto Caboni Jul 11 '20 at 14:37

4 Answers4

2

With out of range input in scanf("%d%c", &n, &ch);, the behavior is undefined.

Instead read a line of input with fgets(), then convert using strtol()

for (;;) {
  char buf[100];
  if (fgets(buf, sizeof buf, stdin) == NULL) {
    printf("No more input\n");
    return -1;
  }

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

  // No numeric conversion done at all?
  // Numeric input outside long range?
  // Junk after the numeric text?
  if (buf == endptr || errno == ERANGE || *endptr != '\n') {
    printf("Invalid number\n");
    continue;
  }
  // Outside int range?
  if (val < INT_MIN || val > INT_MAX) {
    printf("Invalid number\n");
    continue;
  }
  // OP's additional requirement for non-negative numbers.
  if (val < 0) {
    printf("n must > 0");
    continue;
  }

  n = (int) val;
}

I'd recommend a re-usable int get_int(int *val) helper function.

chux - Reinstate Monica
  • 143,097
  • 13
  • 135
  • 256
0

First you need to understand how variables stores data.

In 64 bits architeture, the int type have 4 bytes (either the C long type, regardless architeture), so can store the following values:

00000000 00000000 00000000 00000000 = 0 (decimal value)

01111111 11111111 11111111 11111111 = 2,147,483,647 (decimal value)

11111111 11111111 11111111 11111111 = 4,294,967,294 (unsigned decimal value)

11111111 11111111 11111111 11111111 = -1 (signed decimal value)

Note that the integer types can use the Most Significant Bit (MSB) to represent signal (0 to positive, 1 to negative) using a modular aritmetic.

For more details about integer signal: https://en.wikipedia.org/wiki/Two%27s_complement

So, to store decimal data higher than INT_MAX, you need more bytes than you have using int type. A good way, compatible with 64 bits architeture, is using long long type.

Long long type uses 8 bytes, so can stores value higher than INT_MAX.

You will have to declare: long long n;

And use scanf() like this: scanf("%lld%c", &n, &ch);

Your fflush(stdin) have to be after the scanf(), because if your aplication break the loop after the scanf() and before have reached the fflush() instruction you may have problem in further input handling. Like this:

check = scanf("%lld%c", &n, &ch);
fflush(stdin);

However, some developers disapprove use fflush() in stdin, so this is an alternative (a bit more complex) using getch() accepting only numbers and converting char* to long long using strtoll():

        char c = 0;
        char* input_number = malloc(32);
        int accepted_chars = 0;
        memset(input_number, 0, 32);
        while(c != '\r'){ //loop until user press ENTER
            c = getch();
            //receive numbers keys pressed
            if(c >= '0' && c <= '9'){
                *(input_number + accepted_chars) = c;
                accepted_chars ++;
                
                printf("%c", c);
            }
            
            //receive backspace key pressed
            if(c == 8){
                if(accepted_chars > 0){ //don't do nothing if there is nothing to clear
                    accepted_chars --;
                    *(input_number + accepted_chars) = 0;
                    printf("\b");
                    printf(" ");
                    printf("\b");
                }
            }
        }
        printf("\n");
        char* endptr;
        n = strtoll(input_number, &endptr, 10); //convert string in base 10 (decimal) long long type
Raphael
  • 114
  • 6
  • 2
    Flushing input streams is just wrong, despite working on some computers. See https://stackoverflow.com/a/2979217/2472827. – Neil Jul 11 '20 at 18:29
0

As pointed out in the comments section, there is no point in testing if n > INT_MAX, as n is of type int and therefore by definition unable to represent any value larger than INT_MAX.

A simple, flexible way of checking the size of a number provided as user input would be to read it in as a string and count the number of digits by using the function strlen and possibly also isdigit to count the actual digits.

However, when dealing with potentially large numerical values as user input, it is usually better to first read this input into a data type that is larger than int and do your range checks with this larger data type. After you are satisfied that the number is in the desired range, you can convert it to the smaller int data type. Although the ISO C standard does not guarantee that a long long is larger than an int, this is a reasonable assumption and it is the case on all compilers that I am aware of. However, on some platforms (including 64-bit Windows), the data type long is the same size as int, so a long cannot be reliably used for this on all platforms.

Probably the easiest solution to your problem would be to use the function strtol or strtoll. Using these functions has the advantage that you don't need a larger data type and it explicitly tells you if a number is out of range, by setting errno to ERANGE. However, these functions only support the data types long and long long, but not int. Therefore, if you want to use int, you will have to do your range checks manually before you convert it.

Andreas Wenzel
  • 22,760
  • 4
  • 24
  • 39
0

Just checking

int x;
...
if (x > INT_MAX) { ...

is something that will never work. If an int value cannot have values above INT_MAX then it is impossible that you can ever get x stored a value higher than that. So that if will become always false and the compiler will probably eliminate all the code you put inside that if's then block.

Normal reading routines like scanf() actually limit the input to a value in the range of allowable values.

But if you want to read and build the number yourself, you need to ancitipate the possibility, and use better triggers to stop.

For example, checking that the number is above (INT_MAX - 9) / 10 will tell you that if you try to add another digit to it, you'll run the risk of overflowing the integer, when you add the digit. You can simply stop there and don't continue reading, but if you want to read one more digit (who knows, it could be a 0 and that doesn't hurt, you'll have to check for something like

int digit;
int the_integer = 0;
while (  (digit = fgetchar(stdin)) != EOF 
       && isdigit(digit) 
       && the_integer <= (INT_MAX - (digit - '0'))) 
{
    the_integer *= 10;
    the_integer += digit;
} /* while */
if (digit != EOF && isdigit(digit)) {
    /* x > (INT_MAX - (digit - '0')) */
    /* this is necessary, as if you don't do it, you'll lose
     * the next character to read. */
    unput(digit, stdin);
}

This way you'll check the number x before multiplying it by 10 and adding the digit - '0' value.

Luis Colorado
  • 10,974
  • 1
  • 16
  • 31