Das habe ich nicht gewusst!Zitat von sternst
Vielen Dank!
M.
Mir ist nicht ganz klar, worum es dir hier überhaupt geht. Der Überlauf von signed Integers ist dem C-Standard nach undefiniert. Selbst wenn die Ausgabe so aussehen würde
wäre das vollkommen in Ordnung.Code:... 126 127 666 -999 ...
Nur der Überlauf von unsigned Integers ist definiert.
MfG
Stefan
Das habe ich nicht gewusst!Zitat von sternst
Vielen Dank!
M.
Auch wenn der Überlauf nicht definiert ist, sollte die Variabel a dennoch nicht auf 16 Bit erweitert werden.
Wenn man sich daran stört, sollte es vermutlich reichen die Variabel a als volatile zu deklarieren. Dann kann der Compiler nicht mehr so viel Optimieren, und im RAM wird wohl kaum ein 2 tes Byte reserviert.
Das ist zwar nicht der eigentliche Zweck, aber Volative hilft machmal um den Optimierer zu bremsen.
Nicht die Variable an sich wird auf 16 Bit erweitert (und daher auch kein 2tes Byte im Speicher reserviert), sondern nur die Schleifen interne Registerkopie der Variable. Und das ist völlig legitim. Der Standard schreibt sogar vor, dass die eigentliche Operation (a++) mindestens in int (also 16 Bit) gemacht werden muss. Und da die dann mit dem Wert aufgerufene Funktion als Parameter ebenfalls ein int erwartet, entscheidet sich der Compiler halt, die lokale Registerkopie gleich ganz als 16-Bit-Wert zu führen. Und weil er sich bei einem signed Wert keine Gedanken um einen Überlauf zu machen braucht, spart er sich halt das Ausnullen des High-Bytes zwischen den beiden Operationen.Zitat von Besserwessi
Ein volatile wird die Situation tatsächlich verändern, denn dann wird ja die lokale Kopie (16-Bit) zwischendurch in die eigentliche Variable (8-Bit) geschrieben und wieder gelesen, was das High-Byte der Kopie nullt. Aber wozu sollte man den gewünschten Überlauf "hintricksen"? Code zu schreiben, der sich bei einem signed Wert auf einen bestimmten Überlauf verlässt, ist böse, denn wie gesagt, ein solcher Überlauf ist undefiniert.
MfG
Stefan
Hallo zusammen.
Na M1.R, da hast du ja was grauenvoll teuflisches entdeckt.
Ich habe mal alle Varianten zu folgendem ausprobiert mit Optimizermode "-Os" (Größe): (Versionen: avr-gcc (GCC) 4.1.2 (WinAVR 20070525))
-- Als Variable
int8_t a=0;
char a=0;
-- Als Funktion
usart_put_int (a);
usart_put_int ((int8_t) a);
usart_put_int8_t (a);
-- Als Rechnung
a=a+1;
a+=1;
a++;
Es gibt nur genau eine Funktion, die zu jeder Variablen/Rechnungs-Kombination das richtige Ergebnis liefert.
==> Das ist die Funktion usart_put_int8_t() mit dem Parametertyp int8_t
- Das casten hatte nichts gebracht und lieferte beim a++ genau das gleiche falsche Ergebnis.
- Zu jeder anderen Variablen/Funktions-Kombination, also eigentlich mit falschem Parametertyp, hatte es keinerlei Warning vom Compiler gegeben. Auch dann nicht, wenn alle Funktionen als Prototypen angegeben waren.
- In allen Fällen wurde die Variable a als 16-Bit-Zahl immer in 2 Registern gehalten.
Hier der kommentierte Output vom Compiler im Fehlerfall mit a++:
Hier der kommentierte Output vom Compiler im zufällig funktionierenden Fall mit a=a+1:Code:Variante 03: int8_t a++; int8_t a=0; 158: c0 e0 ldi r28, 0x00 ; 0 <<== Byte 1 für a 15a: d0 e0 ldi r29, 0x00 ; 0 <<== Byte 2 für a usart_put_int (a); 15c: ce 01 movw r24, r28 <<== Ein WORT wird bewegt 15e: c6 df rcall .-116 ; 0xec <usart_put_int> a++; 166: 21 96 adiw r28, 0x01 ; 1 <<== Ein WORT wird in a addiert
Code:Variante 01: int8_t a=a+1 int8_t a=0; 158: c0 e0 ldi r28, 0x00 ; 0 <<== Byte 1 für a 15a: d0 e0 ldi r29, 0x00 ; 0 <<== Byte 2 für a usart_put_int (a); 15c: 8c 2f mov r24, r28 <<== EIN Byte von a für Parameter 15e: 99 27 eor r25, r25 <<== Vorbelegung Byte 2 für Parameter 160: 87 fd sbrc r24, 7 <<== Vorzeichen Low-Byte a prüfen 162: 90 95 com r25 <<== Vorzeichen vom Parameter auf - setzen 164: c3 df rcall .-122 ; 0xec <usart_put_int> a=a+1; 16c: 21 96 adiw r28, 0x01 ; 1 <<== Ein WORT wird in a addiert
Weiterer Versuch mit:
volatile int8_t a=0;
Nur jetzt wird die Variable als ein Byte im Speicher gehalten. Das 2.te Byte zum Aufrufen der INT-Parameter-Funktion wird immer mit korrektem Vorzeichen erzeugt.
- Soll heissen: Es kommt immer das richtige Ergebnis zustande.
Es bleibt: Parameter müssen passen.
Es verwundert: Kein Warning vom Compiler.
Es staunt, grüßt und trinkt nun ein Bier:
Sternthaler
P.S.: Ein Versuch, so wie von Radbruch, dass alle Varianten in einem Programm getestet werden, hat vollkommen andere Verhältnisse gebracht.
Selbst mehrere Variablen mit unterschiedlichen Startwerten konnte den Optimierer meistens nicht dazu bewegen nicht doch nur immer eine Variable zu erzeugen.
Bei Radbruch wird die "falsche" Variable b wegoptimiert.
Und dann wäre da noch die "fähige" Funktion:
void usart_put_int8_t (int8_t wert)
{
usart_put_int ((int)wert);
}
Lieber Asuro programieren als arbeiten gehen.
Nur weil das Ergebnis und die Vorgehensweise des Compilers nicht deinen Erwartungen entsprechen, ist das nicht automatisch fehlerhaft. In diesem Fall liegt der Fehler einzig in deinen Vorstellungen, der Compiler verhält sich völlig korrekt.Zitat von Sternthaler
Ich wiederhole es gerne nochmal: der Überlauf eines vorzeichenbehafteten Integers wird vom C-Standard ausdrücklich als "undefined" festgelegt. Das einzige, was hier also fehlerhaft ist, ist der C-Code, weil er einen solchen Überlauf verursacht.
Wovor sollte da denn auch gewarnt werden? Die Übergabe eines kleineren Integers, als eigentlich von der Funktion erwartet, ist ausdrücklich erlaubt (impliziter Cast). Und von diesem Feature wird auch reger Gebrauch gemacht (oft unbewusst). Es dürfte kaum ein (nicht triviales) Programm geben, in dem das nicht vorkommt.- Zu jeder anderen Variablen/Funktions-Kombination, also eigentlich mit falschem Parametertyp, hatte es keinerlei Warning vom Compiler gegeben. Auch dann nicht, wenn alle Funktionen als Prototypen angegeben waren.
Was tatsächlich bleibt, ist die Tatsache, dass das Ergebnis aus der Kombination "erlaubter impliziter Cast" und "provozieren eines nicht erlaubten signed Overflows" hier manchen verwundern mag, es aber kein Fehler des Compilers ist.Es bleibt: Parameter müssen passen.
MfG
Stefan
Es geht darum:Zitat von sternst
Wenn Variable int8_t a mit a=a+1 hochgezählt wird sieht die Ausgabe wie erwartet so aus:ich bin verwirrt!
Lieber Asuro programieren als arbeiten gehen.
Und was willst du mir damit jetzt sagen? Dass es dich verwirrt, dass mit unterschiedlichem Code nicht immer das selbe unerwartete Ergebnis entsteht?Zitat von Sternthaler
"undefiniert" heißt "alles Mögliche kann passieren". Das schließt auch mit ein, dass nach einer kleinen Änderung am Code das Ergebnis auf einmal ganz anders aussieht.
MfG
Stefan
Lesezeichen