Forum: Compiler & IDEs String zu Integer (Wer findet den Fehler?)


von Gast (Gast)


Lesenswert?

Hallo!

Ich bekomme über eine User-schnittstelle einen String. In erster Instanz 
verifiziere ich die Zeichen, sodass nur noch max. 5 Zahlen und evtl. ein 
Minuszeichen am Anfang enthalten sind.

z.B. "-12345" oder "12345"

Der Wertebereich soll den int-Datentyp entsprechen. D.h. Zahlen zw. 
-32768 und 32767 sollen gültig sein.

D.h. die Zahlen "-55555" oder "66666" sollen einen Fehler zurückgeben. 
(=Funktions-Return -1)

Die Funktion lautet folgendermassen:
1
char dezstr_2_int( unsigned char *string, int *value )
2
{
3
  unsigned char is_neg = 0, i;
4
  long int ret_value = 0;
5
6
  for( i = 0; i < 7; i++)
7
  {
8
    if(*(string + i) == 0x00) { break; }
9
  }
10
11
  for(; *string; string++ ) // string-to-integer - Umwandlung
12
  {
13
    if (*string == '-') { i--; is_neg = 1; continue; } // Vorzeichen auf negativ
14
    
15
    //Dezimalzahl umwandeln
16
    switch (i)
17
    {
18
      case 1: ret_value += (*string - 0x30); break;
19
      case 2: ret_value += ((*string - 0x30) * 10); break;
20
      case 3: ret_value += ((*string - 0x30) * 100); break;
21
      case 4: ret_value += ((*string - 0x30) * 1000); break;
22
      case 5: ret_value += ((*string - 0x30) * 10000); break;
23
    }
24
    i--;
25
  }
26
27
  if (is_neg) // in negative Zahl umwandeln
28
  {
29
    ret_value *= (-1);
30
  }
31
32
  //Bereich von Dezimalzahl prüfen und zurückgeben
33
  if (ret_value > -32769 && ret_value < 32768)
34
  {
35
    *value = (int)ret_value; // Zahl zurückgeben
36
    return 0;
37
  }
38
39
  return -1;
40
}

Solange der Wert zw. -39999 und 39999 schwankt, macht die Funktion, was 
sie soll. Liegt der Wert ausserhalb dieser Grenze kommt nur Müll raus 
(anstatt eines Fehlers '-1').

Irgendwie hab ich Tomaten auf den Augen, weil ich den Bug nicht finde.. 
:(

Bitte um Hilfe. - Danke!

von Kai G. (runtimeterror)


Lesenswert?

In meinen Augen liefert "((*string - 0x30) * 10000)" einen Überlauf ab 
40000

von Timmo H. (masterfx)


Lesenswert?

Geht bei mir wunderbar. Du musst natürlich auch auf den Fehler prüfen. 
In Value steht im Fehlerfall eine zufällige Zahl:
1
int main(int argc, char* argv[])
2
{
3
  int test;
4
  char test1[] = "40450";
5
  if(dezstr_2_int(test1,&test) == -1)
6
    printf("Fehler");  
7
  else
8
    printf("\n%d", test);
9
10
return 0;
11
}

von Karl H. (kbuchegg)


Lesenswert?

Denk mal über folgendes nach:
1
   ...
2
   ... Prüfung auf Gültigkeit
3
 
4
   if( *string == '-' )
5
     is_neg = 1;
6
7
   ret_val = 0;
8
   while( *string )
9
     ret_val = 10 * ret_val + ( *string++ + '0' )
10
11
   if( is_neg )
12
     ret_val = -ret_val;
13
14
   ... Prüfung auf Wertebereich

von Karl H. (kbuchegg)


Lesenswert?

Timmo H. wrote:
> Geht bei mir wunderbar.

Hast du auf einem PC oder auf einem AVR getstet?

Kai hat das Problem schon richtig erkannt.
Auf einem AVR ist sizeof(int) == 2 und daher wird die
Multiplikation mit 10000 für viele Zahlen überlaufen.

Um das zu  umgehen, muss die Multiplikation als long gemacht werden.
1
    ((*string - 0x30) * 10000L);

Auf einem PC wirst du das Phänomen whrscheinlich so nicht sehen, da dort 
sizeof(int) heutzutage meistens 4 ist.

von Stefan E. (sternst)


Lesenswert?

Karl heinz Buchegger wrote:
1
   if( *string == '-' )
2
     is_neg = 1;

->

1
   if( *string == '-' ) {
2
     is_neg = 1;
3
     string++;
4
   }

von Karl H. (kbuchegg)


Lesenswert?

Stefan Ernst wrote:
> Karl heinz Buchegger wrote:
>
1
>    if( *string == '-' )
2
>      is_neg = 1;
3
>
>
> ->
>
>
>
1
>    if( *string == '-' ) {
2
>      is_neg = 1;
3
>      string++;
4
>    }
5
>

und wieder mal die alte Weisheit:
Sei der Code auch noch so einfach - vor dem Posten testen!

Danke für den Catch.

und noch einer:
1
     ret_val = 10 * ret_val + ( *string++ + '0' )

--->
1
     ret_val = 10 * ret_val + ( *string++ - '0' )

von Ggg (Gast)


Lesenswert?

hmm

Aber ret_value hat er doch mit long int definiert - D.h. 4 Byte 
lang..?!?

von Gast (Gast)


Lesenswert?

@Ggg: Ja, das hab ich, ABER

karl heinz hat meinen 'Bug' (glaub ich jedenfalls, kann es momentan 
nicht testen) entdeckt -> Multiplikation ist mit einem Int-Wert, anstatt 
mit einem long int..! ;-)

Ich meld mich wieder, ob es das war..

Danke!

von Johannes M. (johnny-m)


Lesenswert?

Ggg wrote:
> Aber ret_value hat er doch mit long int definiert - D.h. 4 Byte
> lang..?!?
Ja und? Die Zuweisung wird als letztes ausgeführt (nach der Berechnung). 
Zum Zeitpunkt der Berechnung ist es dem Compiler piepegal, was links von 
dem "=" steht. Wenn nichts anderes angegeben ist, wird die Berechnung 
also in int durchgeführt, solange alle beteiligten Operanden in int 
darstellbar sind. Und in diesem Falle sind alle beteiligten Operanden in 
int darstellbar, weshalb die Berechnung auch in int durchgeführt 
wird.

von Karl H. (kbuchegg)


Lesenswert?

Ggg wrote:
> *hmm*
>
> Aber ret_value hat er doch mit long int definiert - D.h. 4 Byte
> lang..?!?

Und, wen kümmerts?

Das ist ein oft gemachter Irrglaube.
Der Datentyp der Ergebnissvariablen ist sowas von irrelevant! Wenn der 
Compiler entscheiden muss, wie eine bestimmte Operation durchzuführen 
ist, dann sieht er sich die beiden Operanden an! Einzig und alleine 
deren Datentyp entscheidet.

    3 / 5

3 ist ein int. 5 ist ein int. Also wird die Division als int-Division 
ausgeführt. Das du das Ergebnis

   double i = 3 / 5;

in einem double ablegst, ist für die Entscheidung wie die Division 
durchzuführen ist, nicht von Belang.

von SoLaLa (Gast)


Lesenswert?

> double i = 3 / 5;

wäre also danach i=0 ?

von Sven P. (Gast)


Lesenswert?

Ja.

Und: long int --> uintXX_t, kommt oft sicherer. Spricht sonst noch was 
gegen atoi und Konsorten?

von Timmo H. (masterfx)


Lesenswert?

@Karl heinz Buchegger
>Timmo H. wrote:
>> Geht bei mir wunderbar.
>Hast du auf einem PC oder auf einem AVR getstet?

Auf einem PC, hier steht ja nicht dass es sich um ein AVR handelt. Es 
ist ja allgemein GCC-Forum und nicht AVR GCC. Im Ausgangsposting steht 
nirgends AVR.

von Gast (Gast)


Lesenswert?

=> Funktioniert  :-)

Danke nochmals..!

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Wenn du sowieso zwischendrin einen long int brauchst, warum nimmst
du dann nicht gleich strtol()?  Dann hätteste dir den ganzen Stress
gespart... (OK, so hast du dafür was fürs Leben gelent. :)

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.