Ich versuche, mit einem Ganzzahlüberlauf umzugehen. Mein Code lautet:

#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;
}

}

Jetzt habe ich überprüft, wie dieser Code reagiert. Und ich habe etwas Seltsames gesehen. Wenn ich eine so große Zahl eingebe, wird es erkannt. Aber manchmal wird nicht erkannt. Hier ist meine Terminalansicht:

> 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$

* Warum es für diese Nummer funktioniert 338479759475637465765. Aber es funktioniert nicht für 58678946895785. Die Logik, die ich in meinem Programm verwendet habe, ist, wenn es nicht gebunden ist, dann gibt die Variable int einen -1- oder negativen Wert. Ich habe viele Artikel gelesen, trotzdem ist es nicht ganz klar.

2
NAZMUL HUSSAIN 19 Jän. 2019 im 17:29

4 Antworten

Beste Antwort

strtol konvertiert den Wert in ein long int, dessen Bereich sich möglicherweise von int unterscheidet. Außerdem wird LONG_MAX oder LONG_MIN zurückgegeben, wenn der Wert konvertiert werden konnte, aber außerhalb des Bereichs für long int liegt. In diesem Fall wird errno auf ERANGE gesetzt (aber nicht anders!). Im Falle eines Übereinstimmungsfehlers ist der zurückgegebene Wert 0, aber errno ist nicht gesetzt. Das ep zeigt jedoch auf den Anfang der Zeichenfolge.

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; 
} 

Was dbush über die Verwendung von perror sagt, ist jedoch richtig. strtol setzt einen Fehler nur im Falle eines long Überlaufs, was nicht der einzige mögliche Fehlerfall in Ihrer Funktion ist, sodass perror so etwas wie Is a directory oder {{X5 drucken kann }}.

2
Antti Haapala 19 Jän. 2019 im 15:25

sscanf(buffer, any_format_without_width, &anytype); reicht nicht aus, um einen Überlauf zu erkennen.

Wenn das Ergebnis der Konvertierung nicht im Objekt dargestellt werden kann, ist das Verhalten undefiniert. C11dr §7.21.6.2 10

Verwenden Sie die Familie *scanf() nicht, um einen Überlauf zu erkennen. Es kann in ausgewählten Fällen funktionieren, aber nicht im Allgemeinen.


Verwenden Sie stattdessen strto**() Funktionen. Doch selbst die isInt() von OP sind falsch codiert, da sie isInt("\n"), isInt(""), isInt("999..various large values ...999") fälschlicherweise als gute int bewerten.

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
chux - Reinstate Monica 19 Jän. 2019 im 15:18

Sie bringen Ihre Typen durcheinander.

In der Funktion isInt verwenden Sie strtol, die ein long zurückgeben, um den Wert zu überprüfen. Dann verwenden Sie in Ihrer main -Funktion sscanf mit %d, das in ein int einliest.

Auf Ihrem System scheint ein long 64 Bit zu sein, während ein int 32 Bit ist. Daher kann strtol 338479759475637465765 nicht vollständig konvertieren, da es größer ist als eine 64-Bit-Variable. Dann versuchen Sie, 58678946895785 zu konvertieren, das in eine 64-Bit-Variable passt, aber keine 32-Bit-Variable.

Sie sollten stattdessen sscanf in ein long einlesen lassen. Dann können Sie den Wert mit INT_MAX vergleichen:

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;
}

Beachten Sie auch, dass es keinen Sinn macht, hier perror aufzurufen. Sie verwenden es erst direkt nach dem Aufruf einer Funktion, die errno setzt.

1
dbush 19 Jän. 2019 im 14:43

Wenn man sscanf() verwenden muss, um int Überlauf zu erkennen, anstatt den robusten strtol(), gibt es einen umständlichen Weg.

Verwenden Sie einen breiteren Typ und eine Breitenbeschränkung, um ein Überlaufen beim Scannen zu verhindern.

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;  
}

Auf seltenen Plattformen mit INT_MAX > 1e18 ist dies nicht ausreichend.

Eingaben wie "lots of leading space and/or lot of leading zeros 000123" werden fälschlicherweise als ungültig zurückgegeben.

Komplexerer Code mit sscanf() kann diese Mängel beheben, der beste Ansatz ist jedoch strto*().

1
chux - Reinstate Monica 19 Jän. 2019 im 15:43