53 Stimmen

Warum wird in C und C++ für for-Schleifen eher int als unsigned int verwendet?

Dies ist eine eher dumme Frage, aber warum ist int üblicherweise verwendet anstelle von unsigned int bei der Definition einer for-Schleife für ein Array in C oder C++?

for(int i;i<arraySize;i++){}
for(unsigned int i;i<arraySize;i++){}

Ich erkenne die Vorteile der Verwendung von int wenn man etwas anderes als Array-Indizierung macht und die Vorteile eines Iterators bei der Verwendung von C++-Containern. Ist es nur, weil es keine Rolle, wenn Schleife durch ein Array? Oder sollte ich es ganz vermeiden und einen anderen Typ verwenden, wie z. B. size_t ?

43voto

6502 Punkte 108136

Verwendung von int ist aus logischer Sicht für die Indizierung eines Arrays besser geeignet.

unsigned semantisch in C und C++ nicht wirklich "nicht negativ" bedeutet, sondern eher wie "Bitmaske" oder "Modulo Integer" ist.

Um zu verstehen, warum unsigned kein guter Typ für eine "nicht-negative" Zahl ist, beachten Sie bitte diese völlig absurden Aussagen:

  • Addiert man eine möglicherweise negative ganze Zahl zu einer nichtnegativen ganzen Zahl, erhält man eine nichtnegative ganze Zahl
  • Die Differenz zweier nichtnegativer ganzer Zahlen ist immer eine nichtnegative ganze Zahl
  • Multipliziert man eine nichtnegative ganze Zahl mit einer negativen ganzen Zahl, erhält man ein nichtnegatives Ergebnis

Offensichtlich ergibt keiner der obigen Sätze irgendeinen Sinn... aber so sind C und C++ unsigned semantisch tatsächlich funktioniert.

Die tatsächliche Verwendung eines unsigned Typ für die Größe von Containern ist ein Designfehler von C++ und leider sind wir nun dazu verdammt, diese falsche Wahl für immer zu verwenden (aus Gründen der Abwärtskompatibilität). Man mag den Namen "unsigned" mögen, weil er ähnlich wie "non-negative" ist, aber der Name ist irrelevant und was zählt, ist die Semantik... und unsigned ist sehr weit von "nicht-negativ" entfernt.

Aus diesem Grund bevorzuge ich persönlich bei der Codierung der meisten Schleifen über Vektoren die Form:

for (int i=0,n=v.size(); i<n; i++) {
    ...
}

(natürlich unter der Annahme, dass sich die Größe des Vektors während der Iteration nicht ändert und dass ich den Index tatsächlich im Körper benötige, da sonst die for (auto& x : v)... ist besser).

Dieses Weglaufen von unsigned und die Verwendung einfacher Ganzzahlen hat den Vorteil, dass die Fallen vermieden werden, die eine Folge der unsigned size_t Designfehler. Betrachten Sie zum Beispiel:

// draw lines connecting the dots
for (size_t i=0; i<pts.size()-1; i++) {
    drawLine(pts[i], pts[i+1]);
}

der obige Code wird Probleme haben, wenn die pts Vektor leer ist, weil pts.size()-1 ist in diesem Fall eine große unsinnige Zahl. Der Umgang mit Ausdrücken, bei denen a < b-1 ist nicht dasselbe wie a+1 < b selbst für häufig verwendete Werte ist wie ein Tanz in einem Minenfeld.

Historisch gesehen ist die Rechtfertigung für size_t unsigned ist für die Möglichkeit, die zusätzlichen Bits für die Werte zu verwenden, z. B. 65535 Elemente in Arrays statt nur 32767 auf 16-Bit-Plattformen zu haben. Meiner Meinung nach waren die zusätzlichen Kosten für diese falsche semantische Wahl schon damals den Gewinn nicht wert (und wenn 32767 Elemente jetzt nicht ausreichen, dann werden 65535 sowieso nicht mehr lange ausreichen).

Vorzeichenlose Werte sind großartig und sehr nützlich, aber NICHT für die Darstellung der Containergröße oder für Indizes; für Größe und Index funktionieren reguläre vorzeichenbehaftete Ganzzahlen viel besser, weil die Semantik das ist, was man erwarten würde.

Werte ohne Vorzeichen sind der ideale Typ, wenn Sie die arithmetische Eigenschaft modulo benötigen oder auf Bitebene arbeiten wollen.

32voto

Jens Gustedt Punkte 74457

Dies ist ein allgemeines Phänomen, denn oft verwenden die Leute nicht die richtigen Typen für ihre ganzen Zahlen. Modernes C hat semantische Typendefinitionen, die den primitiven Integer-Typen weit vorzuziehen sind. Z.B. sollte alles, was eine "Größe" ist, einfach typisiert werden als size_t . Wenn Sie die semantischen Typen systematisch für Ihre Anwendungsvariablen verwenden, kommen auch Schleifenvariablen viel leichter mit diesen Typen zurecht.

Und ich habe mehrere schwer zu entdeckende Fehler gesehen, die auf die Verwendung von int oder so. Code, der bei großen Matrizen und ähnlichem plötzlich abstürzte. Eine korrekte Kodierung mit den richtigen Typen vermeidet das.

5voto

Das ist reine Faulheit und Ignoranz. Sie sollten immer die richtigen Typen für Indizes verwenden, es sei denn, Sie haben weitere Informationen, die den Bereich der möglichen Indizes einschränken, size_t ist der richtige Typ.

Wenn die Dimension aus einem Ein-Byte-Feld in einer Datei gelesen wurde, dann wissen Sie natürlich, dass sie im Bereich 0-255 liegt, und int wäre ein durchaus sinnvoller Index-Typ. Gleichermaßen, int wäre in Ordnung, wenn man eine feste Anzahl von Schleifen durchläuft, z.B. von 0 bis 99. Aber es gibt noch einen weiteren Grund, der gegen die Verwendung von int : wenn Sie i%2 in Ihrem Schleifenkörper, um gerade/ungerade Indizes unterschiedlich zu behandeln, i%2 viel teurer ist, wenn i unterschrieben wird, als wenn i ist ohne Vorzeichen...

4voto

littleadv Punkte 19953

Kein großer Unterschied. Ein Vorteil von int wird sie unterzeichnet. Daher int i < 0 Sinn macht, während unsigned i < 0 nicht viel.

Wenn Indizes berechnet werden, kann dies von Vorteil sein (z. B. kann es vorkommen, dass man nie in eine Schleife kommt, wenn ein Ergebnis negativ ist).

Und ja, es ist weniger zu schreiben :-)

2voto

Blagovest Buyukliev Punkte 40952

Verwendung von int um ein Array zu indizieren, ist veraltet, aber immer noch weit verbreitet. int ist nur ein allgemeiner Zahlentyp und entspricht nicht den Adressierungsmöglichkeiten der Plattform. Falls er kürzer oder länger ist, kann es zu seltsamen Ergebnissen kommen, wenn Sie versuchen, ein sehr großes Array zu indizieren, das darüber hinausgeht.

Auf modernen Plattformen, off_t , ptrdiff_t y size_t garantieren viel mehr Mobilität.

Ein weiterer Vorteil dieser Typen ist, dass sie Kontext für jemanden, der den Code liest. Wenn Sie die oben genannten Typen sehen, wissen Sie, dass der Code Array Subscripting oder Zeigerarithmetik durchführt, nicht nur irgendeine Berechnung.

Wenn Sie also einen kugelsicheren, portablen und kontextsensitiven Code schreiben wollen, können Sie dies auf Kosten einiger weniger Tastenanschläge tun.

Der GCC unterstützt sogar eine typeof Erweiterung, die es Ihnen erspart, überall denselben Typennamen einzugeben:

typeof(arraySize) i;

for (i = 0; i < arraySize; i++) {
  ...
}

Wenn Sie dann den Typ von arraySize die Art der i ändert sich automatisch.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X