Warum bekomme ich keinen Überlauf?!

#3365674
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
Gast #3365705
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
#3365833
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.
#3365837
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.
#3365856
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
#3365858
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.
Gast #3366021
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.
#3366024
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?
Gast #3366033
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.
#3366046
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
#3366048
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.
#3366052
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
#3366072
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;
 }
Gast #3366733
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?
#3366804
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.

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren