2

I've been reading sources for a while but they're always talking about overflows when doing operations but how do I really check for potential int overflow the moment it was entered by the user before it can be assigned to the int identifier?

I want to check the input the moment it was entered so when found that such value already exceeded the range of value for int type data, I can put a stop to it before it even proceeds to the next part of the code.

HaxCkzersssxz
  • 63
  • 1
  • 8

2 Answers2

6

You can read a string then use strtol then check the endptr and errno, when all is ok you can assign your int var

Verbose use of strtol

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

int main()
{
  char s[32]; /* 31 characters is surely large enough for an int */

  if (scanf("%31s", s) != 1)
    puts("nok");
  else {
    errno = 0;

    char * endptr;
    long int l = strtol(s, &endptr, 10);

    if (endptr == s)
      puts("no digit");
    else if ((*endptr != 0) && !isspace(*endptr))
      puts("invalid number");
    else if (errno != 0)
      puts("overflow on long");
    else if (((int) l) != l) /* in case long and int do not have the same size */
      puts("overflow on int");
    else
      puts("you enter a valid int");
  }

  return 0;
}

Compilation and execution :

pi@raspberrypi:/tmp $ gcc -pedantic -Wextra s.c
pi@raspberrypi:/tmp $ ./a.out
a 
no digit
pi@raspberrypi:/tmp $ ./a.out
12z
invalid number
pi@raspberrypi:/tmp $ ./a.out
123
you enter a valid int
pi@raspberrypi:/tmp $ ./a.out
12345678901
overflow on long

So to exactly answer to the question :

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

int readInt(int * v)
{
  char s[32]; /* 31 characters is surely large enough for an int */

  if (scanf("%31s", s) != 1)
    return 0;
  else {
    errno = 0;

    char * endptr;
    long int l = strtol(s, &endptr, 10);

    if ((endptr == s) ||       /* no digit */
        ((*endptr != 0) && !isspace(*endptr)) || /* e.g. 12a */
        (errno != 0) ||        /* overflow on long */
        (((int) l) != l))      /* overflow on int */
      return 0;

    *v = (int) l;
    return 1;
  }
}


int main()
{
  int v = 123;

  if (readInt(&v))
    printf("new valid in value : %d\n", v);
  else
    printf("unvalid input, still %d\n", v);

  return 0;
}

Compilation and execution :

pi@raspberrypi:/tmp $ gcc -pedantic -Wextra s.c
pi@raspberrypi:/tmp $ ./a.out
12
new valid in value : 12
pi@raspberrypi:/tmp $ ./a.out
9878787878787878
unvalid input, still 123
pi@raspberrypi:/tmp $ 
bruno
  • 32,421
  • 7
  • 25
  • 37
  • 1
    A comment sample worth a thousand words :) – SergeyA Feb 19 '19 at 16:51
  • @SergeyA what do you mean ? an example of code is missing ? – bruno Feb 19 '19 at 16:52
  • 1
    Yes, this is exactly what I mean – SergeyA Feb 19 '19 at 16:52
  • @SergeyA so we agree, I will ^^ – bruno Feb 19 '19 at 16:53
  • 1
    You need to set `errno` to zero before calling `strtol()` otherwise there's no guarantee it will be zero should `strtol()` succeed. – Andrew Henle Feb 19 '19 at 17:34
  • @AndrewHenle ah yes, very true, stupid I am, thank you – bruno Feb 19 '19 at 17:35
  • You could use `` and `INT_MAX` and `INT_MIN` and `LONG_MAX` and `LONG_MIN` to determine whether the range of `int` and `long` are the same, or use `sizeof(int) != sizeof(long)` in a condition (not in a preprocessor condition, but in an `if` statement). – Jonathan Leffler Feb 19 '19 at 18:21
  • @JonathanLeffler yes but I do not wanted to add #ifdef to know if hte size are not the same, and in case the size are the same I think the compiler by itself will remove `((int) l) != l)`, no ? – bruno Feb 19 '19 at 18:24
  • Yes, but you could also use `if (l > INT_MAX || l < INT_MIN)` which is not conditionally compiled, either. And you could prefix that with a test on `sizeof()` which would be resolved at compile time: `if (sizeof(int) != sizeof(long) && (l > INT_MAX || l < INT_MIN))`. An optimizing compiler would eliminate that code if `int` and `long` are the same size — maybe even when not optimizing. – Jonathan Leffler Feb 19 '19 at 18:32
  • @JonathanLeffler my point of view is `if (sizeof(int) != sizeof(long) && (l > INT_MAX || l < INT_MIN))` more complicated to read than the simple and short form `((int) l) != l)` and 'worst' to write for my lazy fingers, and all of that for the same result after compilation. But thank you for your remark – bruno Feb 19 '19 at 18:41
  • OK — opinions differ. Even without a comment, the expression checking against `INT_MIN` and `INT_MAX` is clearly testing the range, whereas it's not obvious what the comparison with cast is doing. (Note that the compiler has to treat it as `((long)(int)l != l)` with the extra conversion to `long` if the two types are of different sizes.) With a comment, it is 'OK' — it might even be more efficient because it doesn't require branching. I was merely pointing out that there are other ways to write the tests. I said 'could' in my first comment. I did not say 'should' (but I would use the limits). – Jonathan Leffler Feb 19 '19 at 18:50
1

If you must read directly into an int, you can't really check for overflow before assigning.

There are ways around that if reading directly into int is not a requirement. For example, you can read into a character buffer/string and then check if the input was numeric and if it will fit into an int. If so, you then convert character buffer to an integer using standard library functions and assign to your int.

Kon
  • 4,023
  • 4
  • 24
  • 38