mikrocontroller.net

Forum: Compiler & IDEs Warum uint8_t in 2 Registern?


Autor: tobi (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo,

ich habe mir gerade etwas Code angeguckt, der vom GCC prouziert worden 
ist. Dabei wunder ich mich darüber, dass adiw genutzt wird um einen 
uint8_t zu inkrementieren.

Bei writer_out handelt es sich um einen static uint8_t.

Beispiel 1 (#define TX_BUFFER_SIZE (32)):
  wirter_out = (wirter_out + 1) % TX_BUFFER_SIZE;
 39c:  01 96         adiw  r24, 0x01  ; 1
 39e:  8f 71         andi  r24, 0x1F  ; 31
 3a0:  90 70         andi  r25, 0x00  ; 0
 3a2:  80 93 84 00   sts  0x0084, r24

Beispiel 2 (#define TX_BUFFER_SIZE (255)):
  wirter_out = (wirter_out + 1) % TX_BUFFER_SIZE;
 39c:  01 96         adiw  r24, 0x01  ; 1
 39e:  6f ef         ldi  r22, 0xFF  ; 255
 3a0:  70 e0         ldi  r23, 0x00  ; 0
 3a2:  0e 94 57 04   call  0x8ae  ; 0x8ae <__divmodhi4>
 3a6:  80 93 84 00   sts  0x0084, r24

Warum wird für writer_out 2 Register verwendet? Was für ein Call 
geschieht im zweiten Beispiel, bzw. wofür ist der gut? Ist der nicht 
überflüssig?
Der Call hat eine ganz schön tiefe Verschalchtelung. Frisst das nicht 
massiv Rechenzeit?

Habe Optimierung -Os und WINAVR 20090313 und das Programm wird für einen 
ATmega32 übersetzt.

Der Code von __divmodhi4 sieht so aus:
000008ae <__divmodhi4>:
 8ae:  97 fb         bst  r25, 7
 8b0:  09 2e         mov  r0, r25
 8b2:  07 26         eor  r0, r23
 8b4:  0a d0         rcall  .+20       ; 0x8ca <__divmodhi4_neg1>
 8b6:  77 fd         sbrc  r23, 7
 8b8:  04 d0         rcall  .+8        ; 0x8c2 <__divmodhi4_neg2>
 8ba:  0c d0         rcall  .+24       ; 0x8d4 <__udivmodhi4>
 8bc:  06 d0         rcall  .+12       ; 0x8ca <__divmodhi4_neg1>
 8be:  00 20         and  r0, r0
 8c0:  1a f4         brpl  .+6        ; 0x8c8 <__divmodhi4_exit>

000008c2 <__divmodhi4_neg2>:
 8c2:  70 95         com  r23
 8c4:  61 95         neg  r22
 8c6:  7f 4f         sbci  r23, 0xFF  ; 255

000008c8 <__divmodhi4_exit>:
 8c8:  08 95         ret

000008ca <__divmodhi4_neg1>:
 8ca:  f6 f7         brtc  .-4        ; 0x8c8 <__divmodhi4_exit>
 8cc:  90 95         com  r25
 8ce:  81 95         neg  r24
 8d0:  9f 4f         sbci  r25, 0xFF  ; 255
 8d2:  08 95         ret

000008d4 <__udivmodhi4>:
 8d4:  aa 1b         sub  r26, r26
 8d6:  bb 1b         sub  r27, r27
 8d8:  51 e1         ldi  r21, 0x11  ; 17
 8da:  07 c0         rjmp  .+14       ; 0x8ea <__udivmodhi4_ep>

000008dc <__udivmodhi4_loop>:
 8dc:  aa 1f         adc  r26, r26
 8de:  bb 1f         adc  r27, r27
 8e0:  a6 17         cp  r26, r22
 8e2:  b7 07         cpc  r27, r23
 8e4:  10 f0         brcs  .+4        ; 0x8ea <__udivmodhi4_ep>
 8e6:  a6 1b         sub  r26, r22
 8e8:  b7 0b         sbc  r27, r23

000008ea <__udivmodhi4_ep>:
 8ea:  88 1f         adc  r24, r24
 8ec:  99 1f         adc  r25, r25
 8ee:  5a 95         dec  r21
 8f0:  a9 f7         brne  .-22       ; 0x8dc <__udivmodhi4_loop>
 8f2:  80 95         com  r24
 8f4:  90 95         com  r25
 8f6:  bc 01         movw  r22, r24
 8f8:  cd 01         movw  r24, r26
 8fa:  08 95         ret

~tobi

Autor: gast (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
meines wissens ist gcc nicht für 8bit µc gemacht, sondern wird vielmehr 
zweckentfremdet. es sit eigentlich für 16bit prozessoren gemacht, d.h. 
jede 8bit variable wird erst einmal in eine 16 bit variable aufgebohrt 
und belegt dadurch auch 2 register

ps: für diese aussage lege ich jetzt aber nicht meine hand ins feuer

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
tobi schrieb:
> Hallo,
>
> ich habe mir gerade etwas Code angeguckt, der vom GCC prouziert worden
> ist. Dabei wunder ich mich darüber, dass adiw genutzt wird um einen
> uint8_t zu inkrementieren.

gcc ist bekannt dafür manchmal 16 Bit Arithmetik zu benutzen, wenn 
eigentlich 8 Bit auch reichen würden.
Dies kommt daher, weil C vorschreibt, dass Ganzzahlarithmetik 
zuallererst einmal int-Arithmetik bedeutet. Wenn 8 Bit reichen würden 
ist es Sache des Compilers das zu merken und zu optimieren.


> Beispiel 2 (#define TX_BUFFER_SIZE (255)):
>
>   wirter_out = (wirter_out + 1) % TX_BUFFER_SIZE;
>  39c:  01 96         adiw  r24, 0x01  ; 1
>  39e:  6f ef         ldi  r22, 0xFF  ; 255
>  3a0:  70 e0         ldi  r23, 0x00  ; 0
>  3a2:  0e 94 57 04   call  0x8ae  ; 0x8ae <__divmodhi4>
>  3a6:  80 93 84 00   sts  0x0084, r24
> 
>
> Warum wird für writer_out 2 Register verwendet? Was für ein Call
> geschieht im zweiten Beispiel, bzw. wofür ist der gut? Ist der nicht
> überflüssig?

Das wird die Modulo-Division sein. 255 ist keine 2-er Potenz

Autor: A. K. (prx)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
tobi schrieb:

> Warum wird für writer_out 2 Register verwendet?

Diese Rechnung muss 16bittig erfolgen, weil sonst bei writer_out=255 ein 
falsches Ergebnis rauskommt. Denn writer_out+1 ist per C-Definition 256, 
nicht 0.

Autor: tobi (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Schonmal danke für die Antworten.

Das mit der 2er Potenz hatte ich nicht bedacht. Dann ist klar, dass dort 
ein Subroutine genutzt werden muss.

Gibt es denn eine Optimierung, mit der der GCC wirklich nur 8 Bit 
Arithmetik betreibt?

Habe mich bislang noch nicht wirklich mit den Optimierungen des avrgcc 
befasst.

Autor: tobi (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@A.K.
Wird also immer ein Register mehr genommen als benötigt, um den Überlauf 
abzufangen?

Autor: A. K. (prx)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Das ist etwas zu komplex um hier alles aufzuzählen. Doch existiert in 
der Runtime vermutlich keine 8-bit Divisionsroutine, insofern wird eine 
echte Division immer auf 16-bit Rechnung hinauslaufen.

Anonsten hilft ein bischeh mitdenken. Mit
  uint8_t x;
können x+1 und x<<1 über die Grenzen eines 8-bit Wertes hinauswachsen, 
müssen also 16bittig gerechnet werden, x|1 und x>>1 jedoch nicht. Manche 
dieser Erkenntnisse besitzt auch der Compiler und handelt entsprechend.

Wenn bekannt ist, dass x+1 nicht überlaufen wird, kann man u.U. mit 
(uint8_t)(x+1) aushelfen.

Autor: A. K. (prx)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
tobi schrieb:

> Wird also immer ein Register mehr genommen als benötigt, um den Überlauf
> abzufangen?

Definition von C: Arithmetik mit Typen kleiner als "int" muss in "int" 
durchgeführt werden, oder zumindest muss das gleiche dabei rauskommen. 
Und deshalb sind wie eben skizziert manche Operationen dabei etwas 
teurer als andere.

Autor: Oliver (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
>Diese Rechnung muss 16bittig erfolgen, weil sonst bei writer_out=255 ein
>falsches Ergebnis rauskommt. Denn writer_out+1 ist per C-Definition 256,
>nicht 0.

Und warum mach er dann das hier?
volatile static uint8_t wirter_out=0; // damit der Optimierer noch was übrig lässt...
wirter_out = (++wirter_out) % 32;
ergibt
  92:  80 91 60 00   lds  r24, 0x0060
  96:  8f 5f         subi  r24, 0xFF  ; 255
  98:  80 93 60 00   sts  0x0060, r24 ; <= ist nur drin wegen volatile 
  9c:  80 91 60 00   lds  r24, 0x0060 ; <= ist nur drin wegen volatile 
  a0:  8f 71         andi  r24, 0x1F  ; 31
  a2:  80 93 60 00   sts  0x0060, r24

Oliver

Autor: tobi (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hmm... OK... das ist etwas unvorteilhaft für 8-Bitter.

Aber so sollte es dann besser sein:
  writer_out = (++writer_out) % TX_BUFFER_SIZE;
 39c:  8f 5f         subi  r24, 0xFF  ; 255
 39e:  8f 71         andi  r24, 0x1F  ; 31
 3a0:  80 93 84 00   sts  0x0084, r24

Allerdings bekomm ich nun eine Warnung:
../bufferedUSART.c: In function 'usart_putc':
../bufferedUSART.c:181: warning: operation on 'writer_out' may be undefined

Das wird wohl genau den Grund haben, dass dort ein eventueller Überlauf 
stattfinden kann, da nurnoch 8 Bit Arithmetik genutzt wird.

Gibt es hier sowas wie man es vom Javaprogrammieren in Eclipse kennt? 
Also sowas wie @SuppressWarnings(...), womit man solche Warnungen 
ausblenden kann?
An dieser Stelle weiß ich ja, dass die Warnung keinen bösen Effekt haben 
wird, da ein "Überlauf", an dieser Stelle, nichts schlimmes wär.

Autor: Peter Dannegger (peda)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Der GCC ist manchmal etwas eigensinnig.
Die Puffergröße sollte man besser nicht mit Division berechnen.
Hier mal ein Macro für sowas:
#define ROLLOVER( x, max )      x = ++x >= max ? 0 : x
                                        // count up and wrap around
...
ROLLOVER( tx_out, TX0_SIZE );

und das Listing:
  ROLLOVER( tx_out, TX0_SIZE );
 1f6:   ef 5f           subi    r30, 0xFF       ; 255
 1f8:   e8 30           cpi     r30, 0x08       ; 8
 1fa:   08 f0           brcs    .+2             ; 0x1fe
 1fc:   e0 e0           ldi     r30, 0x00       ; 0

Der vollständige FIFO ist hier:

Beitrag "AVR-GCC: UART mit FIFO"


Peter

Autor: A. K. (prx)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
tobi schrieb:

> Das wird wohl genau den Grund haben, dass dort ein eventueller Überlauf
> stattfinden kann, da nurnoch 8 Bit Arithmetik genutzt wird.

Nein. Hier ist das Problem, dass zwei Zuweisungen erfolgen, bei denen 
die Reihenfolge nicht definiert ist. Nämlich ++writer_out und 
writer_out=... Das lässt sich als
   writer_out = writer_out + 1;
   writer_out = writer_out % ...
oder als
   uint8_t temp = writer_out + 1;
   writer_out = temp % 32;
   writer_out = temp;
interpretieren.

Die Compiler-Meldung ist ein freundlicher hinweis auf genau diese 
Zweideutigkeit.

Autor: A. K. (prx)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Oliver schrieb:

> Und warum mach er dann das hier?

Was stört dich hier? x+1 kann 256 werden, ++x nicht.

Autor: tobi (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
  writer_out = ++writer_out >= TX_BUFFER_SIZE ? 0 : writer_out;
 39c:  8f 5f         subi  r24, 0xFF  ; 255
 39e:  80 32         cpi  r24, 0x20  ; 32
 3a0:  08 f0         brcs  .+2        ; 0x3a4 <usart_putc+0x26>
 3a2:  80 e0         ldi  r24, 0x00  ; 0
 3a4:  80 93 84 00   sts  0x0084, r24

Diese Methode braucht ein klein wenig länger als die Methode mit 
Preinkrement und Modulo, aber dafür keine Warnung.

Wenn es keine Möglichkeit gibt, gezielt Warnungen zu unterdrücken, dann 
werde ich wohl die letztere Nehmen.

Autor: A. K. (prx)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Apropos: Peters Methode funktioniert nur bis max=255. Besser:
  #define ROLLOVER(x,max) x = (x == (max)-1) ? 0 : x+1)

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
tobi schrieb:
>
>   writer_out = ++writer_out >= TX_BUFFER_SIZE ? 0 : writer_out;
> 

Meines Wissens gibt es hier keinen Sequence-Point, der die komplette 
Sequence definiert machen würde.

>
> Diese Methode braucht ein klein wenig länger als die Methode mit
> Preinkrement und Modulo, aber dafür keine Warnung.

Nur weil der Compiler keine Warnung ausspuckt, heißt das nicht, dass 
diese Sequenz richtig ist. Sie hat immer noch undefiniertes Verhalten. 
Als Merkregel: Wenn du in einem Ausdruck versuchst eine Variable 2mal zu 
verändern, hast du undefiniertes Verhalten.

Genau das versuchst du
* einmal mittels ++writer_out
* das zweite mal durch die Zuweisung

> Wenn es keine Möglichkeit gibt, gezielt Warnungen zu unterdrücken, dann
> werde ich wohl die letztere Nehmen.

Tus nicht

Autor: tobi (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Was wäre denn eine sichere und möglichst effiziente Methode um auf einem 
8-Bitter einen Ringpufferindex zu inkrementieren?

Autor: Peter Dannegger (peda)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
A. K. schrieb:
> Apropos: Peters Methode funktioniert nur bis max=255.

Nö, funktioniert auch bei 256.

Ich nehme sie hauptsächlich deshalb, weil ich dann nicht an 2-er 
Potenzen gebunden bin, sondern beliebige Puffergrößen nehmen kann und 
damit den Speicher besser auslasten kann.


Peter

Autor: A. K. (prx)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Peter Dannegger schrieb:

> Nö, funktioniert auch bei 256.

Stimmt. Ist dann sogar recht effizient, weil das ?: komplett rausfliegt 
und Überlauf/Wrap ersetzt wird. Nicht mein Stil, aber wer's mag... (bei 
int8_t und 128 geht es schief).

Das Problem mit dem fehlenden Sequence Point ist dort immer noch drin. 
Stammt das wirklich aus echtem Code?

Autor: Peter Dannegger (peda)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
tobi schrieb:
> Allerdings bekomm ich nun eine Warnung:
>
> ../bufferedUSART.c: In function 'usart_putc':
> ../bufferedUSART.c:181: warning: operation on 'writer_out' may be
> undefined
> 

Warum diese Warnung bei Deinem Code kommt, bei meinem aber nicht, kann 
ich mir auch nicht erklären.

Sie dürfte erst dann kommen, wenn z.B. ++writer_out zweimal vorkommt und 
damit unterschiedliche Ergebnisse rauskommen, je nachdem, welcher zuerst 
ausgeführt wird.

Das ist aber bei unseren beiden Codes nicht der Fall, daß Ergebnis ist 
immer eindeutig.

Der GCC warnt leider gerne mal auch an Stellen, wo es garnichts zu 
warnen gibt.  Ich ärgere mich z.B. regelmäßig über die Forderung nach 
überflüssiger Klammerung.

Ich glaube nicht, daß es viel Sinn macht, bei überflüssigen Warnungen 
einen Bugreport zu schreiben.


Peter

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Peter Dannegger schrieb:


> Das ist aber bei unseren beiden Codes nicht der Fall, daß Ergebnis ist
> immer eindeutig.

Hier liegst du falsch.

Das Ergebnis von

   i = ++i;

ist genauso undefiniert, wie das von

    i = ++i > KONSTANT ? 0 : i

oder Variationen davon. Auch wenn die Operation Preinkrement heißt, wird 
der Compiler nicht dazu gezwungen i selbst zu verändern nachdem die 
Erhöhung abgelaufen ist. Wenn der Compiler will, kann er das Ergebnis 
von ++i erst ganz zum Schluss an i selbst zuweisen, nachdem alles andere 
schon durchgeführt wurde.

Autor: A. K. (prx)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Die entscheidende Frage ist, ob ?: einen sequence point darstellt. Das 
Web gibt dazu unterschiedliche Informationen.

Wenn doch, dann ist x = ++x ? a : b; definiert, x = ++x; aber nicht. 
Wenn nicht, dann sind beide gleich undefiniert, denn obzwar ein Compiler 
das ++x gern vorher abschliessen wird ist dies dann nicht gewährleistet.

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
A. K. schrieb:
> Die entscheidende Frage ist, ob ?: einen sequence point enthält. Das Web
> gibt dazu unterschiedliche Informationen.

Mein Gedächtnis kramt da nichts raus.

Laut Wiki wäre da einer.
http://en.wikipedia.org/wiki/Sequence_point

In dem Fall nehme ich alles zurück. Der ganze Ausdruck ist dann 
wohldefiniert.

Autor: Peter Dannegger (peda)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Karl heinz Buchegger schrieb:
> Das Ergebnis von
>
>    i = ++i;
>
> ist genauso undefiniert

Habs ausprobiert, das gibt ne Warnung, aber warum?

Wenn z.B. i voher 5 ist, wie soll denn da was anderes als 6 rauskommen?


Peter

Autor: A. K. (prx)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Peter Dannegger schrieb:

>> ist genauso undefiniert
>
> Habs ausprobiert, das gibt ne Warnung, aber warum?

Ok, das war zu kurz. Ich bezog mich dabei auf die obige Variante
  i = ++i % 32,
also mit Nachverarbeitung. Dann ist nicht definiert ob
  temp = i + 1;
  i = temp % 32;
  i = temp;
oder
  i = i + 1;
  i = i % 32;
gerechnet wird.

Bei i = ++i ist das "zufällig" gleich.

Autor: Mark Brandis (markbrandis)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Peter Dannegger schrieb:
> Der GCC warnt leider gerne mal auch an Stellen, wo es garnichts zu
> warnen gibt.  Ich ärgere mich z.B. regelmäßig über die Forderung nach
> überflüssiger Klammerung.

Ein Beispiel bitte?

Autor: A. K. (prx)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Er meckert beispielsweise über
    a & b | c & d
Manche finden das gut, andere nervt es.

Autor: Mark Brandis (markbrandis)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Naja wenn ich meinen Code so schreibe, dass sämtliche "operator 
precedence" Regeln stets zu 100% ausgenutzt werden... dann hoffe ich, 
dass niemand meinen Code je lesen muss. ;-)

Autor: tobi (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Also ist nun
  writer_out = ++writer_out >= TX_BUFFER_SIZE ? 0 : writer_out;
 39c:  8f 5f         subi  r24, 0xFF  ; 255
 39e:  80 32         cpi  r24, 0x20  ; 32
 3a0:  08 f0         brcs  .+2        ; 0x3a4 <usart_putc+0x26>
 3a2:  80 e0         ldi  r24, 0x00  ; 0
 3a4:  80 93 84 00   sts  0x0084, r24
die effizienteste Lösung, einen Ringpufferindex definiert zu 
inkrementieren? Zumindest sofern Wiki mit den Sequenz Points recht hat?

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Peter Dannegger schrieb:

>>    i = ++i;
>>
>> ist genauso undefiniert
>
> Habs ausprobiert, das gibt ne Warnung, aber warum?
>
> Wenn z.B. i voher 5 ist, wie soll denn da was anderes als 6 rauskommen?

Das liegt an der (bescheuerten) Definition über Sequence Points in C.
In obigem sitzt der Sequence Point im ;
Das bedeutet erst zu diesem Zeitpunk müssen alle Nebeneffekte 
angeschlossen sein.

++i definiert, dass als Wert für die weitere Verwendung im Ausdruck, der 
um 1 erhöhte Wert von i benutzt wird. Nebeneffekt dieser Anweisung ist 
es, dass der um 1 erhöhte Wert wieder in i abgespeichert wird. Und genau 
darum geht es: Der Compiler darf diesen Nebeneffekt in der 
Ausführungssequenz hin und herschieben, wie es ihm passt. Er muss nur 
beim Sequence Point damit fertig sein.

Auch wenn das kein Compiler machen wird, dürfte er

   i = ++i + 5;
so compilieren (mit einer etwas komplizierteren Expression sieht man das 
alles besser)

   tmp = i + 1;    ( ++i wertmässig evaluieren )
   i = tmp + 5;    ( ++i + 5; an i zuweisen)
   i = tmp         ( ++i fertig stellen, indem das Inkrement
                     zugewiesen wird )

Im Standard liest sich das dann so:
"Between the previous and next sequence point an object shall have its 
stored value modified at most once by the evaluation of an expression. 
Furthermore, the prior value shall be accessed only to determine the 
value to be stored."

Auf deutsch: wird versucht zwischen 2 Sequence Points einer Variablen 
2-mal ein Wert zu verpassen, hat man undefiniertes Verhalten, weil nicht 
geregelt ist, welche Wertzuweisung zuerst erfolgt.

Autor: Peter Dannegger (peda)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Also ich hab im Web zu "undefined behavior" ausschließlich Beispiele mit 
Postincrement gefunden und keines mit nur einem Preincrement.


Peter

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Peter Dannegger schrieb:
> Also ich hab im Web zu "undefined behavior" ausschließlich Beispiele mit
> Postincrement gefunden und keines mit nur einem Preincrement.

Es ist trotzdem das gleiche Prinzip.

Autor: Peter Dannegger (peda)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ich glaub, jetzt verstehe ich es langsam.
i = ++i; ist ein Spezialfall, der immer ein eindeutiges Ergebnis hat.

Ich war bisher immer davon ausgegangen, daß rechts vor links gilt.
Also der Compiler wird zwar in der Regel die linke Seite des "=" als 
letztes ausführen, ist dazu aber nicht gezwungen.

Und der ?: Operator gestattet wie || und && die bedingte Ausführung von 
Ausdrücken und muß dazu den Entscheidungsausdruck komplett abgeschlossen 
haben. Daher ist es eindeutig.


Peter

Autor: tobi (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Um mich selbst zu zitieren:


Also ist nun
  writer_out = ++writer_out >= TX_BUFFER_SIZE ? 0 : writer_out;
 39c:  8f 5f         subi  r24, 0xFF  ; 255
 39e:  80 32         cpi  r24, 0x20  ; 32
 3a0:  08 f0         brcs  .+2        ; 0x3a4 <usart_putc+0x26>
 3a2:  80 e0         ldi  r24, 0x00  ; 0
 3a4:  80 93 84 00   sts  0x0084, r24
die effizienteste Lösung, einen Ringpufferindex definiert zu
inkrementieren? Zumindest sofern Wiki mit den Sequenz Points recht hat?

Autor: Rolf Magnus (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
> Ich glaub, jetzt verstehe ich es langsam.
> i = ++i; ist ein Spezialfall, der immer ein eindeutiges Ergebnis hat.

Nein. i = ++i ruft undefiniertes Verhalten hervor, da es zwischen zwei 
Sequenzpunkten die Variable i zweimal modifiziert. Ob nun eins davon 
eine Zuweisung ist oder nicht, spielt dabei keine Rolle. Es ist also 
genauso undefiniert, wie z.B. ein ++i * ++i

Und ja, ich habe schon unterschiedliche Ergebnisse bekommen, nachdem ich 
es auf verschiedenen Compilern übersetzt hatte.

> Ich war bisher immer davon ausgegangen, daß rechts vor links gilt.
> Also der Compiler wird zwar in der Regel die linke Seite des "=" als
> letztes ausführen, ist dazu aber nicht gezwungen.

"Undefiniertes Verhalten" heißt, daß der Compiler machen darf, was er 
will. Jedes Ergebnis ist korrekt.

> Und der ?: Operator gestattet wie || und && die bedingte Ausführung
> von Ausdrücken und muß dazu den Entscheidungsausdruck komplett
> abgeschlossen haben. Daher ist es eindeutig.

Richtig. Deshalb gibt es bei diesen Operatoren einen Sequenzpunkt.

>Also ist nun
>  writer_out = ++writer_out >= TX_BUFFER_SIZE ? 0 : writer_out;
>
> die effizienteste Lösung, einen Ringpufferindex definiert zu
> inkrementieren?

Ein einfaches:
    writer_out++;
    writer_out %= TX_BUFFER_SIZE;

verwandelt mein avr-gcc in:
        subi r24,lo8(-(1))
        andi r24,lo8(31)

Autor: A. K. (prx)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Nur wenn es eine Zweierpotenz ist. Die gezeigte Alternative ist in 
diesem Fall einen Hauch weniger effizient, bei anderen Puffergrössen 
aber dramatisch effizienter.

Autor: Rolf Magnus (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Natürlich. Aber die Puffergröße hat man ja selbst in der Hand, und daß 
da Zweierpotenzen meistens Vorteile haben, ist nichts neues.

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.