Forum: Compiler & IDEs Warum uint8_t in 2 Registern?


von tobi (Gast)


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)):
1
  wirter_out = (wirter_out + 1) % TX_BUFFER_SIZE;
2
 39c:  01 96         adiw  r24, 0x01  ; 1
3
 39e:  8f 71         andi  r24, 0x1F  ; 31
4
 3a0:  90 70         andi  r25, 0x00  ; 0
5
 3a2:  80 93 84 00   sts  0x0084, r24

Beispiel 2 (#define TX_BUFFER_SIZE (255)):
1
  wirter_out = (wirter_out + 1) % TX_BUFFER_SIZE;
2
 39c:  01 96         adiw  r24, 0x01  ; 1
3
 39e:  6f ef         ldi  r22, 0xFF  ; 255
4
 3a0:  70 e0         ldi  r23, 0x00  ; 0
5
 3a2:  0e 94 57 04   call  0x8ae  ; 0x8ae <__divmodhi4>
6
 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:
1
000008ae <__divmodhi4>:
2
 8ae:  97 fb         bst  r25, 7
3
 8b0:  09 2e         mov  r0, r25
4
 8b2:  07 26         eor  r0, r23
5
 8b4:  0a d0         rcall  .+20       ; 0x8ca <__divmodhi4_neg1>
6
 8b6:  77 fd         sbrc  r23, 7
7
 8b8:  04 d0         rcall  .+8        ; 0x8c2 <__divmodhi4_neg2>
8
 8ba:  0c d0         rcall  .+24       ; 0x8d4 <__udivmodhi4>
9
 8bc:  06 d0         rcall  .+12       ; 0x8ca <__divmodhi4_neg1>
10
 8be:  00 20         and  r0, r0
11
 8c0:  1a f4         brpl  .+6        ; 0x8c8 <__divmodhi4_exit>
12
13
000008c2 <__divmodhi4_neg2>:
14
 8c2:  70 95         com  r23
15
 8c4:  61 95         neg  r22
16
 8c6:  7f 4f         sbci  r23, 0xFF  ; 255
17
18
000008c8 <__divmodhi4_exit>:
19
 8c8:  08 95         ret
20
21
000008ca <__divmodhi4_neg1>:
22
 8ca:  f6 f7         brtc  .-4        ; 0x8c8 <__divmodhi4_exit>
23
 8cc:  90 95         com  r25
24
 8ce:  81 95         neg  r24
25
 8d0:  9f 4f         sbci  r25, 0xFF  ; 255
26
 8d2:  08 95         ret
27
28
000008d4 <__udivmodhi4>:
29
 8d4:  aa 1b         sub  r26, r26
30
 8d6:  bb 1b         sub  r27, r27
31
 8d8:  51 e1         ldi  r21, 0x11  ; 17
32
 8da:  07 c0         rjmp  .+14       ; 0x8ea <__udivmodhi4_ep>
33
34
000008dc <__udivmodhi4_loop>:
35
 8dc:  aa 1f         adc  r26, r26
36
 8de:  bb 1f         adc  r27, r27
37
 8e0:  a6 17         cp  r26, r22
38
 8e2:  b7 07         cpc  r27, r23
39
 8e4:  10 f0         brcs  .+4        ; 0x8ea <__udivmodhi4_ep>
40
 8e6:  a6 1b         sub  r26, r22
41
 8e8:  b7 0b         sbc  r27, r23
42
43
000008ea <__udivmodhi4_ep>:
44
 8ea:  88 1f         adc  r24, r24
45
 8ec:  99 1f         adc  r25, r25
46
 8ee:  5a 95         dec  r21
47
 8f0:  a9 f7         brne  .-22       ; 0x8dc <__udivmodhi4_loop>
48
 8f2:  80 95         com  r24
49
 8f4:  90 95         com  r25
50
 8f6:  bc 01         movw  r22, r24
51
 8f8:  cd 01         movw  r24, r26
52
 8fa:  08 95         ret

~tobi

von gast (Gast)


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

von Karl H. (kbuchegg)


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

von (prx) A. K. (prx)


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.

von tobi (Gast)


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.

von tobi (Gast)


Lesenswert?

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

von (prx) A. K. (prx)


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.

von (prx) A. K. (prx)


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.

von Oliver (Gast)


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?
1
volatile static uint8_t wirter_out=0; // damit der Optimierer noch was übrig lässt...
2
wirter_out = (++wirter_out) % 32;
ergibt
1
  92:  80 91 60 00   lds  r24, 0x0060
2
  96:  8f 5f         subi  r24, 0xFF  ; 255
3
  98:  80 93 60 00   sts  0x0060, r24 ; <= ist nur drin wegen volatile 
4
  9c:  80 91 60 00   lds  r24, 0x0060 ; <= ist nur drin wegen volatile 
5
  a0:  8f 71         andi  r24, 0x1F  ; 31
6
  a2:  80 93 60 00   sts  0x0060, r24

Oliver

von tobi (Gast)


Lesenswert?

Hmm... OK... das ist etwas unvorteilhaft für 8-Bitter.

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

Allerdings bekomm ich nun eine Warnung:
1
../bufferedUSART.c: In function 'usart_putc':
2
../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.

von Peter D. (peda)


Lesenswert?

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

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

Der vollständige FIFO ist hier:

Beitrag "AVR-GCC: UART mit FIFO"


Peter

von (prx) A. K. (prx)


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.

von (prx) A. K. (prx)


Lesenswert?

Oliver schrieb:

> Und warum mach er dann das hier?

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

von tobi (Gast)


Lesenswert?

1
  writer_out = ++writer_out >= TX_BUFFER_SIZE ? 0 : writer_out;
2
 39c:  8f 5f         subi  r24, 0xFF  ; 255
3
 39e:  80 32         cpi  r24, 0x20  ; 32
4
 3a0:  08 f0         brcs  .+2        ; 0x3a4 <usart_putc+0x26>
5
 3a2:  80 e0         ldi  r24, 0x00  ; 0
6
 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.

von (prx) A. K. (prx)


Lesenswert?

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

von Karl H. (kbuchegg)


Lesenswert?

tobi schrieb:
>
1
>   writer_out = ++writer_out >= TX_BUFFER_SIZE ? 0 : writer_out;
2
>

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

von tobi (Gast)


Lesenswert?

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

von Peter D. (peda)


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

von (prx) A. K. (prx)


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?

von Peter D. (peda)


Lesenswert?

tobi schrieb:
> Allerdings bekomm ich nun eine Warnung:
>
1
> ../bufferedUSART.c: In function 'usart_putc':
2
> ../bufferedUSART.c:181: warning: operation on 'writer_out' may be
3
> undefined
4
>

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

von Karl H. (kbuchegg)


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.

von (prx) A. K. (prx)


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.

von Karl H. (kbuchegg)


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.

von Peter D. (peda)


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

von (prx) A. K. (prx)


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.

von Mark B. (markbrandis)


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?

von (prx) A. K. (prx)


Lesenswert?

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

von Mark B. (markbrandis)


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. ;-)

von tobi (Gast)


Lesenswert?

Also ist nun
1
  writer_out = ++writer_out >= TX_BUFFER_SIZE ? 0 : writer_out;
2
 39c:  8f 5f         subi  r24, 0xFF  ; 255
3
 39e:  80 32         cpi  r24, 0x20  ; 32
4
 3a0:  08 f0         brcs  .+2        ; 0x3a4 <usart_putc+0x26>
5
 3a2:  80 e0         ldi  r24, 0x00  ; 0
6
 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?

von Karl H. (kbuchegg)


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.

von Peter D. (peda)


Lesenswert?

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


Peter

von Karl H. (kbuchegg)


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.

von Peter D. (peda)


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

von tobi (Gast)


Lesenswert?

Um mich selbst zu zitieren:


Also ist nun
1
  writer_out = ++writer_out >= TX_BUFFER_SIZE ? 0 : writer_out;
2
 39c:  8f 5f         subi  r24, 0xFF  ; 255
3
 39e:  80 32         cpi  r24, 0x20  ; 32
4
 3a0:  08 f0         brcs  .+2        ; 0x3a4 <usart_putc+0x26>
5
 3a2:  80 e0         ldi  r24, 0x00  ; 0
6
 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?

von Rolf Magnus (Gast)


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:
1
    writer_out++;
2
    writer_out %= TX_BUFFER_SIZE;

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

von (prx) A. K. (prx)


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.

von Rolf Magnus (Gast)


Lesenswert?

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

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.