Warum verhält sich das Komplement durch printf anders?

Lesezeit: 8 Minuten

Benutzeravatar von Sanketssj5
Sanketssj5

Ich las ein Kapitel über bitweise Operatoren, stieß auf das 1er-Komplement-Operatorprogramm und beschloss, es auf Visual C++ auszuführen.

int main ()
{
   unsigned char c = 4, d;
   d = ~c;
   printf("%d\n", d);
}

Es gibt die gültige Ausgabe: 251

Dann statt zu verwenden d als Variable, um den Wert von zu halten ~chabe ich mich entschieden, den Wert von direkt zu drucken ~c.

int main ()
{
   unsigned char c=4;
   printf("%d\n", ~c);
}

Es gibt die Ausgabe -5.

Warum hat es nicht funktioniert?

  • Hinweis: 1er-Komplement von 4 entspricht der Zweierkomplementdarstellung von -5

    – Vagisch

    17. Februar 2015 um 10:55 Uhr

  • es ist ergänzennicht Kompliment

    – phuklv

    18. Februar 2015 um 15:46 Uhr

  • @LưuVĩnhPhúc deine Ergänzung verdient ein Kompliment.

    – Déjà-vu

    2. März 2015 um 22:57 Uhr

Benutzeravatar von Grzegorz Szpetkowski
Grzegorz Szpetkowski

In dieser Aussage:

printf("%d",~c);

das c umgewandelt wird int1 Typ Vor ~ (bitweises Komplement) wird angewendet. Das ist wegen ganzzahlige Aktionendie als Operand von aufgerufen werden ~. In diesem Fall ein Objekt von unsigned char Typ wird befördert zu (signiert) intwas dann (nach ~ Operatorauswertung) verwendet von printf Funktion, mit Anpassung %d Formatbezeichner.

Beachte das Standardargument-Promotions (wie printf ist eine variadische Funktion) spielt hier keine Rolle, da Objekt bereits vom Typ ist int.

Andererseits in diesem Code:

unsigned char c = 4, d;
d = ~c;
printf("%d", d);

folgende Schritte erfolgen:

  • c ist ein Thema ganzzahlige Aktionen durch ~ (so wie oben beschrieben)
  • ~c rvalue wird ausgewertet als (signed) int Wert (zB -5)
  • d=~c macht eine implizite Konvertierung von int zu unsigned charwie d hat einen solchen Typ. Sie können es sich genauso vorstellen wie d = (unsigned char) ~c. Beachte das d kann nicht negativ sein (dies ist die allgemeine Regel für alle vorzeichenlosen Typen).
  • printf("%d", d); ruft Standardargument-Promotionsdaher d umgewandelt wird int und der (nicht negative) Wert bleibt erhalten (dh die int Typ kann alle Werte von darstellen unsigned char Typ).

1) davon ausgegangen int kann alle Werte der darstellen unsigned char (siehe Kommentar von TC unten), aber es ist sehr wahrscheinlich auf diese Weise passieren. Genauer gesagt gehen wir davon aus INT_MAX >= UCHAR_MAX hält. Typischerweise die sizeof(int) > sizeof(unsigned char) Holds und Byte bestehen aus acht Bits. Ansonsten der c umgewandelt werden würde unsigned int (wie in C11 Unterabschnitt §6.3.1.1/p2), und der Formatbezeichner sollte ebenfalls entsprechend geändert werden %u um ein UB zu vermeiden (C11 §7.21.6.1/p9).

  • Aber das passiert dann auch in d=~c. Der wirkliche Unterschied ist dann die Konvertierung (zurück). unsigend char das passiert in d=~caber nicht im printf Anruf.

    – Marc van Leeuwen

    17. Februar 2015 um 12:44 Uhr

  • @MarcvanLeeuwen: Die d Typ hat unsigned charkann also nicht negativ sein (auch nach der Konvertierung in intdurch printf‘s Standardargument-Promotions). Da hast du eigentlich recht d=~c Ganzzahlige Beförderung hat auch stattgefunden (auf die gleiche Weise, wie ich in der Antwort oben beschrieben habe), aber die Zuweisung konvertiert int zu unsigned char wieder zurück. Im zweiten Fall dagegen printf Funktion übernimmt int Argument “wie es ist” (dh %d Formatbezeichner ist korrekt), also Standardargumente Promotionen dort nichts tun.

    – Grzegorz Szpetkowski

    17. Februar 2015 um 12:56 Uhr


  • Für weitere Leser: Ich habe meine Antwort aktualisiert, um Marcs Beobachtung widerzuspiegeln. Möglicherweise finden Sie die ursprüngliche (kürzere) Antwort im Revisionsverlauf. Hoffe jetzt ist alles klar.

    – Grzegorz Szpetkowski

    17. Februar 2015 um 19:36 Uhr

  • Beachten Sie, dass in einigen Systemen unsigned char befördert werden könnte unsigned int durch die Integer-Promotions (z. B. if sizeof(int) == 1), in diesem Fall die printf würde ein undefiniertes Verhalten hervorrufen, weil es den falschen Bezeichner verwendet.

    – TC

    18. Februar 2015 um 15:08 Uhr

  • @TC: Sie haben Recht, aber nehmen wir (der Einfachheit halber) an, dass wir in einer Welt leben, in der ein Byte aus acht Bits besteht. Ich weiß, dass der Standard es nicht erzwingt, aber zum Beispiel der POSIX tut es.

    – Grzegorz Szpetkowski

    18. Februar 2015 um 15:18 Uhr


Benutzeravatar von hackks
hackt

char befördert wird int in printf Erklärung vor der Operation ~ im zweiten Ausschnitt. So cwelches ist

0000 0100 (2's complement)  

in binär wird befördert zu (unter der Annahme einer 32-Bit-Maschine)

0000 0000 0000 0000 0000 0000 0000 0100 // Say it is x  

und sein bitweises Komplement ist gleich dem Zweierkomplement des Werts minus eins (~x = −x − 1)

1111 1111 1111 1111 1111 1111 1111 1011  

welches ist -5 in Dezimalform im Zweierkomplement.

Beachten Sie, dass die Standardpromotion von char c zu int wird auch in durchgeführt

d = ~c;

vor der Komplementoperation, aber das Ergebnis wird zurück konvertiert unsigned char wie d ist vom Typ unsigned char.

C11: 6.5.16.1 Einfache Zuordnung (p2):

Bei einfacher Zuordnung (=), wird der Wert des rechten Operanden in den Typ des Zuweisungsausdrucks konvertiert und ersetzt den Wert, der in dem durch den linken Operanden bezeichneten Objekt gespeichert ist.

und

6.5.16 (p3):

Der Typ eines Zuweisungsausdrucks ist der Typ, den der linke Operand nach der lvalue-Konvertierung haben würde.

Benutzeravatar von Grijesh Chauhan
Grijesh Chauhan

Um das Verhalten Ihres Codes zu verstehen, müssen Sie das Konzept namens lernen ‘Integer-Aktionen’ (Das passiert in Ihrem Code implizit vor der bitweisen NICHT-Operation auf einer unsigned char Operand) Wie im Entwurf des N1570-Komitees erwähnt:

§ 6.5.3.3 Unäre arithmetische Operatoren

  1. Das Ergebnis der ~ operator ist das bitweise Komplement seines (heraufgestuften) Operanden (d. h. jedes Bit im Ergebnis wird genau dann gesetzt, wenn das entsprechende Bit im konvertierten Operanden nicht gesetzt ist). Die ganzzahligen Heraufstufungen werden für den Operanden durchgeführt, und das Ergebnis hat den heraufgestuften Typ. Wenn der heraufgestufte Typ ein ” ‘unsigned type’ ist, wird der Ausdruck ~E entspricht dem maximal darstellbaren Wert in diesem Typ minus E“.

Da unsigned char Typ ist schmaler als (da er weniger Bytes benötigt) int Typ, – implizite Typumwandlung, die von der abstrakten Maschine (Compiler) und dem Wert der Variablen durchgeführt wird c befördert wird int zum Zeitpunkt der Kompilierung (vor Anwendung der Komplementoperation ~). Es wird für die korrekte Ausführung des Programms benötigt, weil ~ brauchen einen ganzzahligen Operanden.

§ 6.5 Ausdrücke

  1. Einige Operatoren (der unäre Operator ~, und die binären Operatoren <<, >>, &, ^und |zusammen als bitweise Operatoren bezeichnet) müssen Operanden vom Typ Integer haben. Diese Operatoren liefern Werte, die von den internen Darstellungen ganzer Zahlen abhängen, und haben implementierungsdefinierte und nicht definierte Aspekte für vorzeichenbehaftete Typen.

Compiler sind intelligent genug, um Ausdrücke zu analysieren, die Semantik von Ausdrücken zu überprüfen, Typprüfungen und arithmetische Konvertierungen durchzuführen, falls erforderlich. Das ist der Grund, sich zu bewerben ~ an char type müssen wir nicht explizit schreiben ~(int)c — genannt explizite Typumwandlung (und vermeiden Sie Fehler).

Notiz:

  1. Wert von c befördert wird int im Ausdruck ~caber Art von c ist immer noch unsigned char – sein Typ nicht. Lassen Sie sich nicht verwirren.

  2. Wichtig: Ergebnis von ~ Betrieb ist aus int Typ!, überprüfen Sie den folgenden Code (ich habe keinen vs-Compiler, ich verwende gcc):

    #include<stdio.h>
    #include<stdlib.h>
    int main(void){
       unsigned char c = 4;
       printf(" sizeof(int) = %zu,\n sizeof(unsigned char) = %zu",
                sizeof(int),
                sizeof(unsigned char));
       printf("\n sizeof(~c) = %zu", sizeof(~c));        
       printf("\n");
       return EXIT_SUCCESS;
    }
    

    kompilieren Sie es und führen Sie es aus:

    $ gcc -std=gnu99 -Wall -pedantic x.c -o x
    $ ./x
    sizeof(int) = 4,
    sizeof(unsigned char) = 1
    sizeof(~c) = 4
    

    Notiz: Größe des Ergebnisses von ~c ist gleich wie von intaber nicht gleich unsigned char – Ergebnis von ~ Operator in diesem Ausdruck ist int! das wie gesagt 6.5.3.3 Unäre arithmetische Operatoren

    1. Das Ergebnis des unären - operator ist das Negative seines (heraufgestuften) Operanden. Die ganzzahligen Heraufstufungen werden am Operanden und durchgeführt das Ergebnis hat den geförderten Typ.

Nun, wie @hackks auch in seiner Antwort erklärte -das Ergebnis von ~c auf 32-Bit-Maschine und für den Wert von c = 4 ist:

1111 1111 1111 1111 1111 1111 1111 1011

dezimal ist es -5 – das ist die Ausgabe von Ihrem zweiter Code!

In deiner erster Codeeine weitere Zeile ist interessant zu verstehen b = ~c;Weil b ist ein unsigned char Variable und Ergebnis von ~c ist von int Typ, um den Wert des Ergebnisses von aufzunehmen ~c zu b Ergebniswert (~c) ist abgeschnitten, um in den unsigned char-Typ zu passen folgendermaßen:

    1111 1111 1111 1111 1111 1111 1111 1011  // -5 & 0xFF
 &  0000 0000 0000 0000 0000 0000 1111 1111  // - one byte      
    -------------------------------------------          
                                  1111 1011  

Dezimaläquivalent von 1111 1011 ist 251. Sie können den gleichen Effekt erzielen mit:

printf("\n ~c = %d", ~c  & 0xFF); 

oder wie von @ouah in seiner Antwort vorgeschlagen mit explizitem Casting.

  • @hackks danke, ja, ich komme hauptsächlich hierher, um zu lernen, anstatt teilzunehmen, und derzeit arbeite ich hauptsächlich mit Python, Django und Web-Zeugs.

    – Grijesh Chauhan

    17. Februar 2015 um 17:54 Uhr


  • +int (PI / 3) zum tatsächlichen Zitieren der relevanten Spezifikationsteile … keine andere Antwort erklärt es tatsächlich warum Die Aktion findet hier statt! dies->Some operators (the unary operator ~, and the binary operators <<, >>, &, ^, and |, collectively described as bitwise operators) are required to have operands that have integer type. hat mir heute den Tag versüßt.

    Benutzer719662

    18. Februar 2015 um 9:25 Uhr


  • @vaxquis danke, – als ich zu diesem Beitrag kam, bemerkte ich, dass die meisten Antworten erklärten, “wie die Ergebnisse unterschiedlich sind”, also beschloss ich, meine Antwort hinzuzufügen und den Grund für “warum so” hervorzuheben – zum Zeitpunkt, als Sie es gelesen haben Bei meiner Antwort fehlte mir eine weitere relevante aus dem Entwurf, die jetzt hinzugefügt wurde.

    – Grijesh Chauhan

    18. Februar 2015 um 15:09 Uhr

Bei der Anwendung der ~ Betreiber zu c es wird befördert intdas Ergebnis ist ein int auch.

Dann

  • im 1. Beispiel wird das Ergebnis umgewandelt in unsigned char und dann befördert signed int und gedruckt.
  • im 2. Beispiel wird das Ergebnis als ausgegeben signed int.

Es gibt die op -5. warum hat es nicht funktioniert?

Anstatt von:

printf("%d",~c);

verwenden:

printf("%d", (unsigned char) ~c);

um das gleiche Ergebnis wie in Ihrem ersten Beispiel zu erhalten.

~ Der Operand wird einer ganzzahligen Heraufstufung unterzogen, und die Heraufstufung des Standardarguments wird auf das Argument von variadischen Funktionen angewendet.

Benutzeravatar von David Ranieri
David Ranieri

Integer-Promotion, vom Standard:

Wenn der Typ des Operanden mit vorzeichenbehafteter Ganzzahl alle Werte des Typs des Operanden mit vorzeichenloser Ganzzahl darstellen kann, muss der Operand mit vorzeichenloser Ganzzahl in den Typ des Operanden mit vorzeichenbehafteter Ganzzahl konvertiert werden.

1412680cookie-checkWarum verhält sich das Komplement durch printf anders?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy