it-swarm.com.de

was ist der Punkt, in dem unsigned int in C verwendet wird?

Ich dachte, dass Integer ohne Vorzeichen nur Ganzzahlen> = 0 ..__ speichern kann. Aber ich habe versucht, einem vorzeichenlosen Int ein Negativ zuzuweisen, es ist nichts Besonderes passiert. Es scheint, als würde der Wert ohne Probleme gespeichert.

Was ist also der Unterschied zwischen vorzeichenbehaftetem und vorzeichenlosem int und was ist der Punkt, wenn es überhaupt irgendeinen Wert speichern kann?

8
Danny

Eine Aussage wie 

unsigned int t = -1;
printf("%u", t);

ist vollständig legal und in C gut definiert. Negative Werte, die einem vorzeichenlosen Integraltyp zugewiesen werden, werden implizit konvertiert (vgl. zB this online-C-Standardentwurf):

6.3.1.3 Vorzeichenbehaftete und vorzeichenlose Ganzzahlen

(2) Andernfalls wird der Wert von .__ konvertiert, wenn der neue Typ unsigniert ist. mehrmals addieren oder subtrahieren, den Wert von kann im neuen Typ dargestellt werden, bis der Wert im Bereich von .__ liegt. der neue Typ.

Die Ausgabe des obigen Programms ist ein vorzeichenloser Wert, d. H.

4294967295

So können Sie vorzeichenlosen Integraltypen "negative" Werte zuweisen, das Ergebnis ist jedoch kein negativer Wert im eigentlichen Sinne. Dies ist besonders relevant, wenn Sie vorzeichenlose Integralwerte mit negativen Werten vergleichen. Betrachten Sie zum Beispiel die folgenden zwei Schleifen:

int i = 10;
while (--i >= 0) {  // 10 iterations
    printf("i: %d\n", i);
}

unsigned int u = 10;
while (--u >= 0) {  // endless loop; warning provided.
    printf("u: %u\n", u);
}

Der erste endet nach 10 Iterationen, während der zweite nie endet: Nicht vorzeichenbehaftete Integralwerte können nicht negativ werden, sodass u >= 0 immer wahr ist.

6
Stephan Lechner

Die Verwendung von unsigned int in C ist folgender:

  • Es gibt Ihnen einen größeren Bereich für positive Werte (mindestens 32.767 für signierte vs. mindestens 65.535 für unsignierte Werte).
  • Es gibt Ihnen die Möglichkeit, die Nummer zum Maskieren zu verwenden und undefiniertes Verhalten beim Bit-Verschieben der Nummer zu vermeiden
  • Auf diese Weise kann der Compiler prüfen, ob Sie der Nummer keine unzulässigen Werte zuweisen (wenn Sie wissen, dass die Zahl nicht vorzeichenbehaftet ist). Dies wäre in Ihrem Fall der Fall, wenn Sie die Warnmeldungen aktiviert hätten.
2
mnistic

Sie haben richtig, dass unsigned int nur ganze Zahlen> = 0 speichern kann. (Natürlich gibt es auch eine Obergrenze, und diese Obergrenze hängt von Ihrer Architektur ab und ist in UINT_MAX in limits.h definiert).

Durch das Zuweisen eines vorzeichenbehafteten int-Wertes zu einem unsigned int rufen Sie eine implizite Typkonvertierung auf. Die C-Sprache hat sehr genaue Regeln, wie dies geschieht. Wenn immer möglich, versucht der Compiler, den Wert möglichst zu erhalten. Nehmen Sie das zum Beispiel:

int x = 5;
unsigned int y;

y = x;

Der obige Code führt auch eine Typkonvertierung durch. Da der Wert "5" jedoch sowohl in vorzeichenbehafteten als auch vorzeichenlosen Ganzzahlbereichen darstellbar ist, kann der Wert beibehalten werden, sodass y auch den Wert 5 hat.

Nun bedenken Sie:

x = -5;
y = x;

In diesem Fall weisen Sie speziell einen Wert zu, der nicht innerhalb des darstellbaren Bereichs von unsigned int ist. Daher muss der Compiler den Wert in einen Wert innerhalb des Bereichs konvertieren. Der C-Standard schreibt vor, dass der Wert 1 + UINT_MAX dem Wert hinzugefügt wird, bis er sich innerhalb des Bereichs von unsigned int befindet. In den meisten Systemen ist UINT_MAX heutzutage als 4294967925 (2 ^ 32 - 1) definiert, sodass der Wert von y tatsächlich 4294967921 (oder 0xFFFFFFFB in Hex) ist.

Es ist wichtig zu beachten, dass auf Zwei-Komplement-Rechnern (fast überall vorhanden) die binären Repräsentationen eines signed int-Werts von -5 ebenfalls 0xFFFFFFFB sind, dies ist jedoch nicht erforderlich. Der C-Standard erlaubt und unterstützt Maschinen, die unterschiedliche ganzzahlige Kodierungen verwenden. Daher sollte Portable Code niemals davon ausgehen, dass die binäre Darstellung nach einer solchen impliziten Konvertierung erhalten bleibt.

Hoffe das hilft!

2
Joe Hickey

Ein wichtiger Punkt ist, dass das Überfließen einer vorzeichenbehafteten Ganzzahl ein undefiniertes Verhalten ist, wohingegen vorzeichenlose Ganzzahlen als Umlauf definiert werden. Tatsächlich geschieht dies, wenn Sie einem einen negativen Wert zuweisen: Er wird einfach umbrochen, bis der Wert im Bereich liegt.

Während dieses Wrap-around-Verhalten von nicht signierten Typen bedeutet, dass es in der Tat absolut zulässig ist, ihnen negative Werte zuzuweisen, ist die Rückkonvertierung in signierte Typen nicht so gut definiert (bestenfalls ist es implementierungsdefiniert, im schlimmsten Fall undefiniertes Verhalten, abhängig von) Wie machst du es). Und obwohl es vielleicht sogar stimmt, dass auf vielen gängigen Plattformen die vorzeichenbehafteten und vorzeichenlosen Ganzzahlen intern gleich sind, ist die beabsichtigte Bedeutung des Werts für Vergleiche, Konvertierungen (z. B. in Fließkommazahlen) sowie für die Compiler-Optimierung von Bedeutung.

Zusammenfassend sollten Sie einen vorzeichenlosen Typ verwenden, wenn Sie eine genau definierte Umlaufsemantik für Über- und Unterlauf benötigen und/oder positive ganze Zahlen darstellen müssen, die größer als das Maximum des entsprechenden (oder größten geeigneten) vorzeichenbehafteten Typs sind. Technisch gesehen könnten Sie signs -Typen in den meisten Fällen vermeiden, indem Sie negative Zahlen über vorzeichenlose Typen implementieren (schließlich können Sie bestimmte Bitmuster einfach als negative Zahlen interpretieren), aber… warum, wenn die Sprache diesen Dienst anbietet "kostenlos". Das einzige wirkliche Problem mit vorzeichenbehafteten Ganzzahlen in C besteht darin, auf Überlauf achten zu müssen. Im Gegenzug erhalten Sie jedoch möglicherweise eine bessere Optimierung.

2
Arkku

Unsignierte haben 1) höhere Maxima und 2) definierten Überlauf.

Wenn mit unendlicher Präzision

 (unxigned_c = unsigned_a + unsinged_b) >= UINT_MAX

dann wird unsigned_c modulo UINT_MAX+1 reduziert:

#include <limits.h>
#include <stdio.h>
int main()
{
    printf("%u\n", UINT_MAX+1); //prints 0
    printf("%u\n", UINT_MAX+2); //prints 1
    printf("%u\n", UINT_MAX+3); //prints 2
}

Ähnliches geschieht, wenn Sie vorzeichenbehaftete Werte in einem vorzeichenlosen ..__ speichern. In diesem Fall gilt 6.3.1.3p2 - UINT_MAX+1 wird konzeptionell zum Wert hinzugefügt.

Bei vorzeichenbehafteten Typen dagegen ist Überlauf undefiniert, dh wenn Sie zulassen, dass Ihr Programm ausgeführt wird, ist Ihr Programm nicht mehr gut ausgebildet, und der Standard gibt keine Garantie für das Verhalten. Compiler nutzen dies zur Optimierung aus, indem sie davon ausgehen, dass dies niemals passieren wird.

Zum Beispiel, wenn Sie kompilieren 

#include <limits.h>
#include <stdio.h>

__attribute__((noinline,noclone)) //or skip the attr & define it in another tu
_Bool a_plus1_gt_b(int a, int b) { return a + 1 > b; }

int main()
{
    printf("%d\n", a_plus1_gt_b(INT_MAX,0)); //0
    printf("%d\n", INT_MAX+1); //1
}

auf gcc mit -O3 wird es sehr wahrscheinlich gedruckt

1
-2147483648
0
PSkocik