2

I am trying to handle integer overflow. My code is :

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<errno.h>
#include<limits.h>

int isInt (char *s)
{
 char *ep = NULL;
 long i = strtol (s, &ep, 10);
 if ((*ep == 0) || (!strcmp(ep,"\n")))
    return 1;  // it's an int

 return 0;  
} 

int main()
{
char *buffer = NULL;
size_t count = 0;
ssize_t ret;
//AMINO *a_acid;
int num;

for(;;)
{   
printf("Please enter an integer:");
if((ret = getline(&buffer, &count, stdin)) < 0)
{
    perror("getline: error\n");
    free(buffer);
    exit(EXIT_FAILURE);
}

if(!isInt(buffer))
{
    perror("you are not entering int , Try again:");
    continue;
}
sscanf(buffer, "%d",&num);
printf("%d\n", num);
if ((num > INT_MAX)|| (num < 0))
{
    perror("you overflowed int variable , Try again:\n ");
    continue;
}
break;
}

}

Now I was checking how this code is responding. And I saw something weird.When I am entering so big number, then it is detected. But sometimes is not getting detected. Here is my terminal view:

> nazmul@nazmul-Lenovo-G50-80:~/2nd_sem/biophysics$ gcc torson.c 
> nazmul@nazmul-Lenovo-G50-80:~/2nd_sem/biophysics$ ./a.out
> Please enter an integer:ksdjfjklh 
> you are not entering int , Try again:: Success
> Please enter an integer:338479759475637465765
> -1 
> you overflowed int variable , Try again:  : Numerical result out of  
> range 
> Please enter an integer:58678946895785 
> 1103697833
> nazmul@nazmul-Lenovo-G50-80:~/2nd_sem/biophysics$

*Why it is working for this number 338479759475637465765. But it is not working for 58678946895785. logic , I used in my program, is when it is out of bound, then int variable gives some -1 or negative value. I read many article, still it is not quite clear.

5
  • ignore that line. I edited some code from isfloat() to isInt() Commented Jan 19, 2019 at 14:34
  • The second and third use of perror() does not make sense. I Commented Jan 19, 2019 at 14:37
  • Also please take a bit of time and indent the code properly. Commented Jan 19, 2019 at 14:39
  • And an minimal reproducible example would be better Commented Jan 19, 2019 at 14:40
  • 2
    num > INT_MAX is never true. Since num is an int, its value is never bigger than INT_MAX. What likely happened is that, when you entered a large number, overflow occurred inside scanf (which likely has behavior not defined by the C standard; I have not checked this specific point), and the result was that a negative value was placed in int. Then this triggered your test num < 0. Commented Jan 19, 2019 at 14:41

4 Answers 4

2

strtol converts the value to a long int, whose range might be distinct from int. Furthermore, it returns LONG_MAX or LONG_MIN if the value could be converted but is outside the range for long int. In that case, errno will be set to ERANGE (but not otherwise!) Also, in the case of matching failure the value returned is 0, but errno is not set; but the ep points to the beginning of the string.

int isInt (char *s)
{
   char *ep = NULL;

   // zero errno first!
   errno = 0;
   long i = strtol (s, &ep, 10);
   if (errno) {
       return 0;
   }

   // matching failure.
   if (ep == s) {
       return 0;
   }

   // garbage follows
   if (! ((*ep == 0) || (!strcmp(ep,"\n")))) {
      return 0;
   }

   // it is outside the range of `int`
   if (i < INT_MIN || i > INT_MAX) {
      return 0;
   }

   return 1; 
} 

What dbush says about the use of perror is correct, though. strtol sets an error only in case of long overflow, which is not the only possible failing case in your function, so perror could print anything like Is a directory or Multihop attempted.

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

1 Comment

very helpful.But If I check over flow for long long int, I can not do this way, right? what you are doing is you are converting input as long in for checking overflow for int then compare it with INT_MIN or INT_MAX .like wise for checking overflow in long in, I can use strtoll and the compare it with LLONG_MIN or LLONG_MAX.But, how do i check overflow for long long int?
2

sscanf(buffer, any_format_without_width, &anytype); is not sufficient to detect overflow.

if the result of the conversion cannot be represented in the object, the behavior is undefined. C11dr §7.21.6.2 10

Do not use *scanf() family to detect overflow. It may work in select cases, but not in general.


Instead use strto**() functions. Yet even OP's isInt() is mis-coded as it incorrectly assess isInt("\n"), isInt(""), isInt("999..various large values ...999") as good ints.

Alternative:

bool isint_alt(const char *s) {
  char *endptr;
  errno = 0;
  long y = strtol(s, &endptr, 10);

  if (s == endptr) {
    return false; // No conversion
  }

  if (errno == ERANGE) {
    return false; // Outside long range
  }

  if (y < INT_MIN || y > INT_MAX) {
    return false; // Outside int range
  }

  // Ignore trailing white space
  while (isspace((unsigned char)*endptr)) {
    endptr++;
  }

  if (*endptr) {
    return false; // Trailing junk
  }

  return true;
}

2 Comments

If only the *scanf functions would say something like "the value is the same as the return value of strtoimax, converted to the appropriate type". :/
@AnttiHaapala Yes, the "the behavior is undefined." is the killer for typical use of *scanf().
1

You're getting your types mixed up.

In the isInt function you use strtol, which return a long to check the value. Then in your main function you use sscanf with %d, which reads into an int.

On your system, it seems that a long is 64 bits while an int is 32 bits. So strtol fails to fully convert 338479759475637465765 because it is larger than a 64 bit variable can hold. Then you try to convert 58678946895785 which will fit in a 64 bit variable but not a 32 bit variable.

You should instead have sscanf read into a long. Then you can compare the value against INT_MAX:

long num;
...
sscanf(buffer, "%ld", &num);
printf("%ld\n", num);
if ((num > INT_MAX)|| (num < INT_MIN))
{
    printf("you overflowed int variable , Try again:\n ");
    continue;
}

Also note that it doesn't make sense to call perror here. You only use it right after calling a function which sets errno.

3 Comments

I understand. But still I have one query. After taking input in long int , I have to compare with INT_MAX to check overfow in respect of int .Then when I would take long int input, then I have check against long long int. If i take input in long long int , then how I check overflow. I mean, Here I wanted int input, so I take it in long int variable and check it with INT_MAX. But, when I want to take long long int, then how can I check overflow?
I disagree. sscanf is not a sound advice, because with sscanf you cannot tell if the value overflowed or not. It works only iff range of long int is distinct from int. Furthermore OPs code contained a check for newline or 0 i.e. that the line was terminated just after the number- yours will discard trailing characters.
The proper course of action is to use the strtol code, check ERANGE, then compare against INT_{MAX|MIN}
1

If one must use sscanf() to detect int overflow rather than the robust strtol(), there is a cumbersome way.

Use a wider type and a width limit to prevent overflow when scanning.

bool isint_via_sscanf(const char *s) {
  long long y;
  int n = 0;
  if (sscanf(s, "18%lld %n", &y, &n) != 1) {  // Overflow not possible
    return false;  // Conversion failed 
  }

  if (y < INT_MIN || y > INT_MAX) {
    return false; // Outside int range
  }

  if (s[n]) {
    return false;  // Trailing junk
  }

  return true;  
}

It is insufficient on rare platforms where INT_MAX > 1e18.

It also incorrectly returns input like "lots of leading space and/or lot of leading zeros 000123" as invalid.

More complex code using sscanf() can address these short-comings, yet the best approach is strto*().

Comments

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.