Forum: Compiler & IDEs Warum bekomme ich keinen Überlauf?!


von Dennis S. (eltio)


Lesenswert?

Hallo zusammen,

ich habe folgenden Code:
1
#include <stdio.h>
2
#include <stdint.h>
3
4
int main(void) {
5
6
int8_t zahl = 0;
7
int16_t n = 0;
8
9
    for (n=0; n<500; n++){
10
        printf("%3d\n", zahl++);
11
    }
12
13
return 0;
14
}

Das Programm zählt munter von 0 bis 499. Warum bekomme ich keine 
negativen Zahlen? Wenn ich uint8_t nehme zählt er doch auch nur bis 255 
und fängt dann wieder bei 0 an!

Gruß Dennis

von Rolf Magnus (Gast)


Lesenswert?

Das Überlaufverhalten von Integern ist in C undefiniert. Bei 
vorzeichenlosen Integern gibt es keine Überläufe, weil dort immer modulo 
größtmoglicher Wert gerechnet wird.

von Lutz H. (luhe)


Lesenswert?

Der Überlauf ist ein Flag .

Am besten Wert austesten in der Schleife etwa:

if (Zahl == 256 ) {Überlauf = true;}

Könne auch sein das der Compiler eine Einstellung hat, dann den 
Programmablauf zu unterbrechen.

von Dennis S. (eltio)


Lesenswert?

Hmm... ganz verstehe ich das nicht: Der größtmögliche Wert ist hier doch 
+127. Wenn ich irgendwas Modulo n nehme, kann es doch nicht größer als n 
werden!?

Fakt ist doch: int8_t hat 8 Bit.. wie bekomme ich in 8 Bit die Zahl 499 
rein?

Gruß Dennis

von Karl H. (kbuchegg)


Lesenswert?

Welcher compiler ist das?

Auch wenn das Überlaufverhalten von signed Werten nicht spzifiziert ist, 
ist es IMHO nicht korrekt wenn du da Zahlen von 0 bis 499 für einen 
int8_t siehst.

von Kaj (Gast)


Lesenswert?

Auf was für einer Platform probierst du das denn aus?
Auf meinem PC (Win7, visual Studio 2012) bekomme ich den Überlauf, also 
zahlen von -128 bis +127.

Ich denke es könnte an deinem int8_t liegen.
Es wird wohl ein Typedef auf char sein. Wenn es wirklich "char" und 
nicht auf "signed char" ist, dann könnte es sein, dass dein Compiler den 
"char" per default als "unsigned" behandelt. (Das ist zum Beispiel 
Standardeinstellung beim Atmel Studio.)
Das würde dann auch erklären warum du keinen "Überlauf" bekommst.

Grüße

von Dennis S. (eltio)


Lesenswert?

Der verwendete Compiler ist
1
gcc (Ubuntu/Linaro 4.7.2-2ubuntu1) 4.7.2
, leider bin ich gerade zu blöd die Header-Datei auf der Festplatte zu 
finden... :-/ Aber danke schonmal, dann habe ich etwas wo ich weiter 
forschen kann!

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Ändert sich das Verhalten mit -fwrapv ?
1
-fwrapv
2
  This option instructs the compiler to assume that signed arithmetic
3
  overflow of addition, subtraction and multiplication wraps around
4
  using twos-complement representation. This flag enables some
5
  optimizations and disables others.

von (prx) A. K. (prx)


Lesenswert?

Kaj schrieb:
> Ich denke es könnte an deinem int8_t liegen.
> Es wird wohl ein Typedef auf char sein. Wenn es wirklich "char" und
> nicht auf "signed char" ist, dann könnte es sein, dass dein Compiler den
> "char" per default als "unsigned" behandelt.

Das setzt freilich voraus, dass int8_t nicht durch ein zum Compiler 
passendes Include-File definiert wurde.

von (prx) A. K. (prx)


Lesenswert?

Das Verhalten ist mit GCC/Linux 4.7.3 reproduzierbar und tritt nur bei 
eingeschalteter Optimierung auf, nicht aber bei fwrapv.

Das Verhalten des Compilers ist völlig in Ordnung, da - wie schon 
erwähnt wurde - das Überlaufverhalten undefiniert ist. Folglich ist bei 
Code, der Überlauf erzeugt, jedes beliebige Ergebnis zulässig.

Hintergrund: Code ist mit Wortbreite oft effizienter als mit Bytes.

von Rolf Magnus (Gast)


Lesenswert?

Karl Heinz Buchegger schrieb:
> Welcher compiler ist das?
>
> Auch wenn das Überlaufverhalten von signed Werten nicht spzifiziert ist,
> ist es IMHO nicht korrekt wenn du da Zahlen von 0 bis 499 für einen
> int8_t siehst.

Es wäre ungewöhnlich, aber korrekt ist es schon. "undefined behiavior" 
halt.

von Dennis S. (eltio)


Lesenswert?

A. K. schrieb:
> Das Verhalten ist mit GCC/Linux 4.7.3 reproduzierbar und tritt nur bei
> eingeschalteter Optimierung auf, nicht aber bei fwrapv.
>
> Das Verhalten des Compilers ist völlig in Ordnung, da - wie schon
> erwähnt wurde - das Überlaufverhalten undefiniert ist. Folglich ist bei
> Code, der Überlauf erzeugt, jedes beliebige Ergebnis zulässig.
>
> Hintergrund: Code ist mit Wortbreite oft effizienter als mit Bytes.

Tatsache, ich habe das Programm jetzt einmal mit und einmal ohne -O2 
kompiliert und es ist wie du es beschreibst.

Aber dann stellt sich mir doch die Frage: wenn ich mich nicht drauf 
verlassen kann, dass int8_t 8 Bit groß ist, warum soll ich es dann 
benutzen!?

Gruß Dennis

von (prx) A. K. (prx)


Lesenswert?

Dennis S. schrieb:
> Aber dann stellt sich mir doch die Frage: wenn ich mich nicht drauf
> verlassen kann, dass int8_t 8 Bit groß ist, warum soll ich es dann
> benutzen!?

Im Speicher ist es 8 Bit gross. Nur ist es hier bei Optimierung nicht im 
Speicher, sondern in einem Prozessorregister. Ein Prozessorregister nur 
teilweise zu verwenden ist oft nachteilig, sofern überhaupt möglich.

von Wolfgang (Gast)


Lesenswert?

Dennis S. schrieb:
> Das Programm zählt munter von 0 bis 499. Warum bekomme ich keine
> negativen Zahlen?

Wenn deine Variable 'zahl' ohne Murren den Zählerstand 499 abbilden 
kann, sprich alles dafür, dass int8_t kein 8-Bit Datentyp ist.

Unabhängig von irgendwelcher Vorzeicheninterpretation.

von Lutz H. (luhe)


Lesenswert?

So ist das halt, manchmal ist denkt sich der Entwickler des Compilers 
etwas anderes, als der Entwickler des Programms.
Wenn es dem Entwickler des Programmes es ganz egal ist, was bei einem 
Überlauf passiert, warum soll der Compilereentwickler irgenderwas 
vorschreiben?

von Mike (Gast)


Lesenswert?

Wolfgang schrieb:
> Dennis S. schrieb:
>> Das Programm zählt munter von 0 bis 499. Warum bekomme ich keine
>> negativen Zahlen?
>
> Wenn deine Variable 'zahl' ohne Murren den Zählerstand 499 abbilden
> kann, sprich alles dafür, dass int8_t kein 8-Bit Datentyp ist.
>
> Unabhängig von irgendwelcher Vorzeicheninterpretation.

Der Compiler macht (mit -O2) das hier aus der Schleife:
1
L2:
2
  movl  %ebx, 4(%esp)
3
  movl  $LC0, (%esp)
4
  call  _printf
5
  incl  %ebx
6
  cmpl  $500, %ebx
7
  jne  L2

So wie es aussieht fasst er die beiden Variablen zusammen und hält sie 
in einem 32 Bit Register.

Interessanterweise gibt das Programm -12 aus wenn man "zahl" nach der 
Schleife ausgibt (auch mit Optimierung). Wenn man sich den Code 
anschaut, dann sieht man das der Compiler den Wert vorher ausgerechnet 
hat und einfach als Konstante ausgibt.

von (prx) A. K. (prx)


Lesenswert?

Wolfgang schrieb:
> Wenn deine Variable 'zahl' ohne Murren den Zählerstand 499 abbilden
> kann, sprich alles dafür, dass int8_t kein 8-Bit Datentyp ist.

Doch.
1
#include <stdio.h>
2
#include <stdint.h>
3
4
int main(void) {
5
6
int8_t zahl = 0;
7
int16_t n = 0;
8
    for (n=0; n<500; n++){
9
        printf("%3d\n", zahl++);
10
    }
11
    printf("Size: %d\n", sizeof zahl);
12
13
return 0;
14
}
gibt:

496
497
498
499
Size: 1

von (prx) A. K. (prx)


Lesenswert?

Und für jene, die es immer noch nicht kapiert haben: Wenn "zahl" zu 
uint8_t wird, dann gibts am Schluss statt 499:
237
238
239
240
241
242
243
Ohne -fwrapv gibts eben wirklich nur bei vorzeichenlosen Typen das 
intuitiv erwartete Überlaufverhalten. Sonst gilt Schrott rein Schrott 
raus.

von (prx) A. K. (prx)


Lesenswert?

Mike schrieb:
> So wie es aussieht fasst er die beiden Variablen zusammen und hält sie
> in einem 32 Bit Register.

Dieser Schritt lässt sich auch abschalten:
   gcc -O2 -fno-ivopts ...

"Perform induction variable optimizations (strength reduction, induction 
variable merging and induction variable elimination) on trees."

Sinn: http://www.compileroptimizations.com/category/ive.htm

von Lutz H. (luhe)


Lesenswert?

Dennis S. schrieb:
> int main(void) {
>
> int8_t zahl = 0;
> int16_t n = 0;
>
>     for (n=0; n<500; n++){
>         printf("%3d\n", zahl++);
>     }
>
> return 0;
> }
Ich würde das einfach so lösen:
 int8_t zahl = -128;
 int16_t n = 0;

     for (n=0; n<500; n++){
         printf("%3d\n", zahl++);
if (Zahl>127) Zahl=-128; // der Porgrammierer weiß was er will bei einem 
Überlauf könnte allesmögliche sein,hängt von der Anwendung ab
     }

 return 0;
 }

von (prx) A. K. (prx)


Lesenswert?

lutz h. schrieb:
> if (Zahl>127) Zahl=-128;

;-)

gcc: "Warnung: Vergleich ist durch beschränkten Wertebereich des 
Datentyps stets »unwahr« [-Wtype-limits]"

von Lutz H. (luhe)


Lesenswert?

lutz h. schrieb:
> printf("%3d\n", zahl++);
> if (Zahl>127) Zahl=-128;

dann :
if (zahl==127)
{ printf("%3d\n", zahl);
zahl=-128;}
else
{ printf("%3d\n", zahl);
zahl=zahl+1;}
;-)

von Wolfgang (Gast)


Lesenswert?

A. K. schrieb:
> Doch.

Nein. Ein 8-Bit Datentyp kann wahlweise den Zahlenbereich [0..255] oder, 
bei 2er-Komplementdarstellung [-128..+127], aufnehmen. Mehr nicht.

Welche Bitkombination würdest du denn vorschlagen, um dein "Doch" zu 
untermauern? Wie sähe nach deiner Meinung z.B. die Zahl 496 im 
Dualsystem, in 8 Bit untergebracht, aus?

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Warum sollte 496 in dem hier diskutierten Fall in einem int8_t 
darstellbar sein?  Der Wert 496 entsteht nach einem Signed Overflow, und 
der ist in C "Undefined Behaviour" und wird auch von der Implementation 
(hier: avr-gcc) nicht näher definiert -- zumindest nicht ohne Schalter 
wie z.B. -fwrapv.

von (prx) A. K. (prx)


Lesenswert?

Wolfgang schrieb:
> Welche Bitkombination würdest du denn vorschlagen, um dein "Doch" zu
> untermauern?

Ganz einfach. Ein int8_t ist 8 Bit breit. Ein Compiler, der keine 8 Bit 
Typen kennt, kennt auch nicht den Typ int8_t, per Definition.

Aber: Der Compiler verwendet für diese Variable effektiv nicht den Typ 
int8_t, obwohl es im Quelltext so drinsteht. Solange im Rahmen der C 
Spezifikation das gleiche Ergebnis rauskommt darf er das nämlich. Da 
dieser Code aber die C Spezifikation verletzt, hat der Compiler einige 
Freiheiten. Und die nimmt er sich.

von Lutz H. (luhe)


Lesenswert?

eine Zahl kann auch  in der Form a*x + b dargestellt werden.

mit b = 495 definiert als Konstante und  und x = 1  definiert als 
Konstante  wäre die  binäre Darstellung von 496  :   a= 0000 0001.

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.