Konvertieren Sie den Typ des Berechnungsergebnisses in if() in uint8_t

Ich schreibe in C mit dem Atmel Studio (AVR-C.)

Ich habe eine if-Anweisung:

if( (rxProcessing < (rxWritePos-1) ) )

Wo rxProcessingund rxWritePossind bereits Typ uint8_t. und ich möchte, dass das Ergebnis von (rxWritePos-1)ein 8-Bit-Int ohne Vorzeichen ist (so dass zum Beispiel, wenn rxWritePos 0 ist, 0-1 = 255 ist.)

Ich möchte sicher sein, dass das Ergebnis der Subtraktion dort immer ein 8-Bit-Wert ohne Vorzeichen ist. Wie kann ich das Ergebnis konvertieren (oder sicherstellen, dass) (rxWritePos-1)immer ein vorzeichenloses 8-Bit-Int sein?

Ich denke, das wird reichen:

if( (rxProcessing < ((uint8_t)rxWritePos-1) ) )

Beachten Sie, dass , und ich weiß, dass ich eine Variable wie folgt in uint8_t umwandeln/umwandeln kann: uint8_t y = (uint8_t) x;aber ich möchte sicherstellen, dass die Umwandlung innerhalb von if() stattfindet.

Genau wie Variablen können Sie es wie folgt umwandeln: (uint8_t) (rxWritePos-1) ... aber in Ihrem Fall müssen Sie das eigentlich nicht.
Diese Frage ist softwarebezogen und gehört zu stackoverflow
@JasonS Stapelüberlauf ist so giftig, dass ich dort Fragen vermeide. Da ich AVR-C für Atmel MCU verwende, dachte ich, ich würde hier fragen.
@JasonS Hier ist es in Ordnung, da die Frage einen Aspekt der Mikrocontroller-Programmierung hat - nämlich wie man idealen Code für AVR schreibt, was eine träge 8-Bittere ist. Sie können nicht einfach Code für solche wie jedes generische C schreiben. Teilweise, weil die Codeeffizienz auf 8-Bittern so gut wie nicht vorhanden ist, teilweise, weil die implizite Heraufstufung ein Alptraum für Ziele ist, die kleiner als 32 Bit sind.

Antworten (3)

Sie benötigen Klammern um den gesamten Ausdruck: (uint8_t)(rxWritePos-1).

Wenn rxWritePos= 0:

  • rxWritePos - 1= −1
  • rxWritePos - 1u= eine große Zahl (unsignierter Wrap-Around mit dem Typ von 1u)
  • (uint8_t)(rxWritePos-1)= (uint8_t)(-1)= 255.

Dies wird alles durch die Type-Promotion-Regeln von C geregelt, die etwas unintuitiv sein können. Seien Sie besser offensichtlich.

Wenn Sie nun sagen, dass die Konvertierung innerhalb der if-Anweisung erfolgen soll, ist dies eine gültige Frage, um die richtige Syntax zu finden. Dies wird jedoch höchstwahrscheinlich zu genau demselben Maschinencode kompiliert wie das Deklarieren einer neuen Variablen direkt vor der if-Anweisung, was klarer sein kann (bezüglich: besser offensichtlich sein).

Ist der dritte Punkt wirklich definiertes Verhalten? Ich dachte, Wrap-Around ist nur für Ganzzahlen ohne Vorzeichen definiert? Bei den meisten CPUs ist dies jedoch das Verhalten, das Sie sehen.
@Michael Ja, es ist wirklich gut definiert. Siehe stackoverflow.com/questions/7221409/… .
Ooops, Dummkopf, für eine vorzeichenbehaftete 0 - 1 gibt es keinen Über- oder Unterlauf :D

rxWritePosist schon uint8_tso offensichtlich das die besetzung (uint8_t)rxWritePosunsinnig ist - du begibst es auf den typ den es schon hat. Ebenso ist es riskant, "viele Berechnungen durchzuführen und dann in den beabsichtigten Typ umzuwandeln". Die Berechnung selbst könnte unbeabsichtigte Dinge enthalten.

Hier geht es hauptsächlich um implizite Type Promotion . intOperanden von binären Operatoren werden normalerweise zu , einem vorzeichenbehafteten Typ, heraufgestuft . Wir wollen fast nie signierte Typen in eingebetteten Systemen haben, das ist also problematisch.

1Darüber hinaus ist die Integer-Konstante auch vom Typ int, der vorzeichenbehaftet ist. Wie es der Zufall will, hätte es viele Probleme gelöst, es so zu schreiben 1uund durchzusetzen, wie es ist .unsigned int

Abgesehen von der Signierbarkeit wollen wir auf allen alten 8-Bit-MCUs 16-Bit-Arithmetik möglichst vermeiden. Wenn es also aufgrund impliziter Heraufstufung zu versehentlichen Vorzeichenänderungen kommt, können diese auch die Optimierung blockieren und den Compiler zwingen, die Berechnung in 16 Bit durchzuführen. Weil es nicht wissen kann, ob Sie den Code geschrieben haben und auf die implizite Beförderung zählen oder nicht.

Die richtige/robusteste Lösung für Ihr Problem ist jedoch wahrscheinlich, die Berechnung in mehreren Schritten mit einer temporären Variablen durchzuführen.

uint8_t writePos = rxWritePos;
writePos--;
if(rxProcessing < writePos)

Hier writePos--wird garantiert nicht befördert und es wird vorzeichenlose 8-Bit-Arithmetik verwendet. Ganzzahlen ohne Vorzeichen können nicht über-/unterlaufen, sondern haben stattdessen einen wohldefinierten Umlauf von 255 bis 0, was Sie möchten.

(uint8_t)((rxWritePos-1) & 255)

Bei jeder Berechnung müssen Sie eine Maske anlegen, dann erhalten Sie immer das richtige Ergebnis.

Muss ich Udort nicht immer noch das Präfix verwenden? Wie:255U
@ChristianidisVasileios Ich bin kein C-Experte. Ich habe Ihnen einen Ansatz gegeben, aber Sie müssen die korrekte Syntax anwenden.
@ChristianidisVasileios Im Allgemeinen sollten Sie immer das Suffix U verwenden. In diesem speziellen Fall scheint es egal zu sein. In anderen Fällen vielleicht.