unsigned char und 0



  • Wieso gibt es bei der 3. for-Schleife eine Endlos-Schleife ?
    Ist ein unsigned char nicht im Bereich von 0..255 ?

    int main(){
    
    	for (char i = 3; i >= 0; i--) {
         std::cout << "char " << i + 0 << std::endl;
    	}
    
    	for (signed char i = 3; i >= 0; i--) {
         std::cout << "signed char " << i + 0 << std::endl;
    	}
    
    	for (unsigned char i = 3; i >= 0; i--) {
         std::cout << "unsigned char: " << i + 0 << std::endl;
    	}
    
        return 0;
     }
    


  • Du prüfst auf i >= 0 und dekrementierst i . i hat dann die Werte 3, 2, 1, 0, 255, 254 ... und weil du auf größer oder gleich prüfst, ist die Bedingung immer wahr, und die Schleife wird nie verlassen.



  • Mathuas schrieb:

    Ist ein unsigned char nicht im Bereich von 0..255 ?

    Das ist mindestens der Wertebereich von unsigned char .
    Der kann auch größer sein.

    Bei unsigned Typen ist der Über- bzw. Unterlauf von Standard definiert.



  • for (unsigned char i = 3; i < 4; i--) {
         std::cout << "unsigned char: " << i + 0 << std::endl;
    	}
    

    So würde es gehen, aber sauber finde ich dies nicht.

    Wie kann ich von 255 runterzählen bis 0, ohne das ich einen int nehmen muss ?



  • for (unsigned char c = 4; c--;)
    {
        // code, hier ist c 3, 2, 1, 0
    }
    

    Von 255 runterzählen klappt damit aber auch nicht.
    Wenn dich ein int aber so sehr stört, gibt es auch noch short.



  • for(i=3;;i--)
    {
    //cout
    
    if(!i)
    break;
    }
    


  • Mathuas schrieb:

    Wie kann ich von 255 runterzählen bis 0, ohne das ich einen int nehmen muss ?

    Weshalb willst du keinen int verwenden?


  • Mod

    Mathuas schrieb:

    Wie kann ich von 255 runterzählen bis 0, ohne das ich einen int nehmen muss ?

    Das Problem besteht darin, dass bei einer Schleife, bei der das letzte angebene Element noch durchlaufen werden soll (also allgemein die Sequenz (begin,end] statt [begin,end) durchlaufen werden soll), das Inkrementieren/Dekrementieren der Schleifenvariablen erst nach der Prüfung der Schleifenvariablen durchgeführt werden muss.

    for ( int foo = 0; i != 4; ++i ) {
      // ...
    }
    

    ist äquivalent zu

    for ( int i = 0; i != 4; ) {
        // do something
        ++i;
    }
    

    wenn keine continue vorkommen.
    Wollen wir jetzt die Reihenfolge umkehren und das Ende gegen 0 prüfen, muss das Dekrementieren vor den eingentlich Schleifenkörper wandern:

    for ( int i = 4; i != 0; ) {
        --foo;
        // do something
    }
    

    was auch druch Postfixdekrement in der Schleifenbedingung erreicht werden kann ( wie DrakoXP gezeigt hat )

    for (unsigned char i = 4; i-- != 0; ) {
         std::cout << "unsigned char: " << i + 0 << std::endl;
        }
    


  • Erledigt



  • Ich sagte dazu aber auch, dass man damit trotzdem nicht das Range [255,0] abdecken kann, weil durch das Postdekrement in der Schleifenbedingung bereits der erste Durchlauf bei 254 ist.

    Eine mögliche Schleife, die ohne größeren Typ auskommt, sieht so aus:

    #include <iostream>
    
    int main()
    {
        unsigned char c = 255u;
        do
        {
            std::cout << static_cast<int>(c) << std::endl;
        } while (c--);
        return 0;
    }
    

    Der Unterschied ist hier, dass die for-Schleife ihre Bedingung bereits vor
    dem ersten Schleifendurchlauf prüft.
    Das gezeigte do-while überprüft ihre Bedingung erst danach, wodurch die 255 im ersten Durchlauf erhalten bleibt.


  • Mod

    DirkB schrieb:

    In deiner zweiten for-Schleife ist zweimal ++i

    Korrigiert.



  • Somit ist wohl

    unsigned char i = 255;
        do
        {
            std::cout << i + 0 << std::endl;
        } while (i--);
    

    die einzige Lösung ?
    Eine for-to Schleife sieht auf Assembler ebene nicht viel anders aus.

    Weshalb willst du keinen int verwenden?

    Weil bei einem Mikro-Kontroller der Speicher begrenzt ist.

    Unter Pascal funtioniert so was ganz einfach, nur bin ich für einen Mikro-Kontroller auf C++ angewiesen.

    var
      b: byte;
    begin
      for b := 0 to 255 do begin
        Write(b: 4);
      end;
      WriteLn();
      for b := 255 downto 0 do begin
        Write(b: 4);
      end;
    


  • Mathuas schrieb:

    Weshalb willst du keinen int verwenden?

    Weil bei einem Mikro-Kontroller der Speicher begrenzt ist.

    Die Schleifenvariable sollte eigemtlich nur im Register leben.

    Mathuas schrieb:

    Unter Pascal funtioniert so was ganz einfach, nur bin ich für einen Mikro-Kontroller auf C++ angewiesen.

    Da siehst du halt nicht, was der Compiler daraus macht.
    Der Code kann mehr Speicher verbrauchen, als ein zusätzliches Byte für die Variable.



  • Die Schleifenvariable sollte eigemtlich nur im Register leben.

    Dafür gab es bei einem 8088er das CX-Zählregister.
    Ob das heute ECX. oder sonst was ist, kann ich nicht sagen.



  • Heutzutage ecx (32 Bit) bzw. rcx (64 Bit), wobei das ja nur für x86 und x86_64 zutrifft. ARM, ARM64, MIPS, Itanium etc. haben völlig andere Register.
    Und da der TE erwähnte, dass er hier für Microcontroller programmiert,
    kann man nicht pauschal von der Existenz eines predestinierten Zählregisters ausgehen.

    Ebenso ist nicht sicher gestellt, dass ein solches Register, falls vorhanden, größer als 8 Bit ist. Beim AVR scheinen die Register allgemein nur 8 Bit groß zu sein, mit Ausnahme einiger Registerpaare, die zusammen jeweils ein 16 Bit Zeiger-Register bilden.



  • DrakoXP schrieb:

    Eine mögliche Schleife, die ohne größeren Typ auskommt, sieht so aus:

    ...
        unsigned char c = 255u;
    ...
    

    Schrott. Reine Digit-Literale haben den Typ int, du wandelst ihn in unsigned int nur um ihn dann gleich wieder auf unsigned char zu reduzieren.
    Hier sind damit sogar 2 größere Typen im Spiel und nicht wie du vermutest gar keiner.



  • Wutz schrieb:

    DrakoXP schrieb:

    Eine mögliche Schleife, die ohne größeren Typ auskommt, sieht so aus:

    ...
        unsigned char c = 255u;
    ...
    

    Schrott. Reine Digit-Literale haben den Typ int, du wandelst ihn in unsigned int nur um ihn dann gleich wieder auf unsigned char zu reduzieren.
    Hier sind damit sogar 2 größere Typen im Spiel und nicht wie du vermutest gar keiner.

    255u ist ein unsigned int .
    Deczimal, oktal oder hex ist dann egal - in keinem Fall ist da ein (signed) int .



  • Wer lesen kann ist klar im Vorteil.



  • Wutz schrieb:

    DrakoXP schrieb:

    Eine mögliche Schleife, die ohne größeren Typ auskommt, sieht so aus:

    ...
        unsigned char c = 255u;
    ...
    

    Schrott. Reine Digit-Literale haben den Typ int, du wandelst ihn in unsigned int nur um ihn dann gleich wieder auf unsigned char zu reduzieren.
    Hier sind damit sogar 2 größere Typen im Spiel und nicht wie du vermutest gar keiner.

    Wow, einfach nur wow.

    Generell wird durch das u-Suffix gar nichts in unsigned umgewandelt.
    Tatsächlich sagt das u dem Compiler, dass der Literal schon den Typ "unsigned int" haben soll.

    OH MEIN GOTT, DA STEHT JA INT 😮

    Ja, warum auch nicht?
    1. gibt es in C und C++ ohnehin keine kleineren Ganzzahlliterale.
    Sowas wie 23l (long) oder 42ll (long long) gibt es leider für char und short nicht.
    Falls du jetzt an Character-Literale denkst, so lass dich belehren, dass auch die vom Typ int sind.
    2. hat die Zielvariable, die mit dem Literal initialisiert wird, den gewünschten Typ "unsigned char".
    Dir mag sich möglicherweise entziehen, dass die Konvertierung des Literals in diesen Typ bereits zur Compile-Time passiert und somit im fertigen Programm lediglich ein unsigned char mit dem Wert 255 auftaucht.

    Und selbst, wenn ich mich geirrt hätte, so wäre "Schrott" nicht die adäquateste Weise gewesen, mich darauf hinzuweisen 😉


Anmelden zum Antworten