Was ist der Unterschied zwischen Literalen und Variablen in C (signed vs unsigned short ints)?

8

Ich habe den folgenden Code im Buch Computersysteme: Die Perspektive eines Programmierers, 2 / E gesehen. Dies funktioniert gut und erzeugt die gewünschte Ausgabe. Die Ausgabe kann durch die Differenz von Vorzeichen und Vorzeichen erklärt werden.

%Vor%

Der obige Code liefert -1 >= 0u , jedoch der folgende Code, der derselbe wie oben sein soll, nicht! Mit anderen Worten,

%Vor%

ergibt -1 < 0u . Warum ist das passiert? Ich kann das nicht erklären.

Beachten Sie, dass ich ähnliche Fragen wie this , aber sie helfen nicht.

PS. Wie @Abhineet sagte, kann das Dilemma gelöst werden, indem short in int geändert wird. Wie aber kann man dieses Phänomen erklären? Mit anderen Worten, -1 in 4 Bytes ist 0xff ff ff ff und in 2 Bytes ist 0xff ff . Wenn sie als 2s-Komplement angegeben werden, die als unsigned interpretiert werden, haben sie entsprechende Werte von 4294967295 und 65535 . Sie beide sind nicht weniger als 0 und ich denke in beiden Fällen muss die Ausgabe -1 >= 0u sein, d. H.% Co_de%.

Eine Beispielausgabe für ein kleines Endian-Intel-System:

Kurz gesagt:

%Vor%

Für int:

%Vor%     
Ali Shakiba 26.10.2015, 06:47
quelle

4 Antworten

10
  

Der obige Code ergibt -1 & gt; = 0u

Alle Ganzzahl-Literale (numerische Konstanten) haben einen Typ und daher auch eine Signedness. Standardmäßig haben sie den Typ int , der signiert ist. Wenn Sie das Suffix u anhängen, wird das Literal in unsigned int umgewandelt.

Für jeden C-Ausdruck, bei dem Sie einen signierten und einen ungesicherten Operanden haben, gilt die Regel des Wechsels (formal: die üblichen arithmetischen Konvertierungen ) konvertiert den signierten Typ implizit in unsigned.

Die Konvertierung von vorzeichenbehaftet nach vorzeichenlos ist wohldefiniert (6.3.1.3):

  

Wenn der neue Typ unsigniert ist, wird der Wert andernfalls durch wiederholtes Hinzufügen von oder konvertiert   Subtrahieren von mehr als dem maximalen Wert, der in dem neuen Typ dargestellt werden kann   bis der Wert im Bereich des neuen Typs ist.

Für 32-Bit-Ganzzahlen auf einem Standard-Zweierkomplement-System lautet der Maximalwert einer vorzeichenlosen Ganzzahl beispielsweise 2^32 - 1 (4294967295, UINT_MAX in limits.h). Ein mehr als der maximale Wert ist 2^32 . Und -1 + 2^32 = 4294967295 , daher wird das Literal -1 in einen unsigned int mit dem Wert 4294967295 konvertiert. Welches ist größer als 0.

Wenn Sie die Typen jedoch auf "short" setzen, erhalten Sie einen kleinen Integer-Typ . Dies ist der Unterschied zwischen den beiden Beispielen. Immer wenn ein kleiner Integer-Typ Teil eines Ausdrucks ist, konvertiert die Integer-Promotion-Regel diesen implizit in einen größeren int (6.3.1.1):

  

Wenn ein int alle Werte des Originaltyps darstellen kann (als eingeschränkt   durch die Breite für ein Bitfeld) wird der Wert in ein int umgewandelt;   Andernfalls wird es in einen unsigned int konvertiert. Diese heißen die   ganzzahlige Werbeaktionen Alle anderen Typen sind unverändert durch die ganze Zahl   Werbeaktionen.

Wenn short auf der angegebenen Plattform kleiner ist als int (wie bei 32- und 64-Bit-Systemen), wird jede short oder unsigned short daher immer in int konvertiert, weil sie kann in einen passen.

Für den Ausdruck if (x < u) haben Sie also tatsächlich if((int)x < (int)u) , das sich wie erwartet verhält (-1 ist kleiner als 0).

    
Lundin 26.10.2015, 07:29
quelle
3

Sie stoßen auf die C-Integer-Werberegeln.

Operatoren für Typen kleiner als int stellen ihre Operanden automatisch auf int oder unsigned int hoch. Siehe Kommentare für detailliertere Erklärungen. Es gibt einen weiteren Schritt für binäre Operatoren (zwei Operanden), wenn die Typen danach immer noch nicht übereinstimmen (z.B. unsigned int vs. int). Ich werde nicht versuchen, die Regeln detaillierter zusammenzufassen. Siehe Lundins Antwort .

Dieser Blogbeitrag behandelt dies detaillierter, mit einem ähnlichen Beispiel deins: signed und unsigned char. Es zitiert die C99-Spezifikation:

  

Wenn ein int alle Werte des ursprünglichen Typs darstellen kann, lautet der Wert   konvertiert zu einem int; Andernfalls wird es in einen unsigned int konvertiert.   Diese werden als ganzzahlige Werbeaktionen bezeichnet. Alle anderen Arten sind unverändert   durch die Integer-Promotions.

Sie können damit leichter auf etwas wie godbolt spielen, mit einer Funktion, die eins oder null zurückgibt . Sehen Sie sich die Compiler-Ausgabe an, um zu sehen, was passiert.

%Vor%     
Peter Cordes 26.10.2015 07:05
quelle
2

Anders als Sie annehmen, ist dies keine Eigenschaft der speziellen Breite der Typen, hier 2 Byte gegenüber 4 Byte, sondern eine Frage der Regeln, die angewendet werden sollen. In den Regeln für ganzzahlige Werbung wird angegeben, dass short und unsigned short auf allen Plattformen, auf denen der entsprechende Wertebereich in int passt, in int konvertiert werden. Da dies der Fall ist, bleiben beide Werte erhalten und erhalten den Typ int . -1 ist in int perfekt darstellbar, wie auch in 0 . Die Testergebnisse in -1 sind also kleiner als 0 .

Wenn Sie -1 gegen 0u testen, wählt die allgemeine Konvertierung den Typ unsigned als einen gemeinsamen Typ, in den beide konvertiert werden. -1 konvertiert in unsigned ist der Wert UINT_MAX , der größer ist als 0u .

Dies ist ein gutes Beispiel, warum Sie nie "schmale" Typen verwenden sollten, um Arithmetik oder Vergleich durchzuführen. Verwenden Sie sie nur, wenn Sie eine Servergrößenbeschränkung haben. Dies wird für einfache Variablen selten der Fall sein, aber meistens für große Arrays, bei denen Sie wirklich von einem Speichern in einem engen Typ profitieren können.

    
Jens Gustedt 26.10.2015 07:18
quelle
0
  

0u ist nicht unsigned short , es ist unsigned int .

Bearbeiten :: Die Erklärung für das Verhalten, Wie wird der Vergleich durchgeführt?

Wie von Jens Gustedt beantwortet,

  

Das nennt man "übliche arithmetische Konvertierungen" nach dem Standard und   gilt immer dann, wenn zwei verschiedene Integer-Typen als Operanden des Typs auftreten   derselbe Operator.

     

Im Wesentlichen was ist

     

wenn die Typen unterschiedliche Breite haben (genauer gesagt was der Standard ist   ruft Conversion-Rang auf), dann konvertiert es in den breiteren Typ, wenn beides   Typen haben die gleiche Breite, neben wirklich seltsamen Architekturen, die   nicht signiert von ihnen gewinnt Signed zu unsigned Konvertierung des Wertes -1   mit welcher Art auch immer ergibt sich immer der höchste darstellbare Wert   vom unsignierten Typ.

Der mehr erklärende Blog von ihm könnte hier .

    
Abhineet 26.10.2015 06:58
quelle