webentwicklung-frage-antwort-db.com.de

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

Dies ist eine ziemlich dumme Frage, aber warum wird int häufig anstelle von unsigned int verwendet, wenn eine for-Schleife für ein Array in C oder C++ definiert wird?

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

Ich erkenne die Vorteile der Verwendung von int, wenn Sie etwas anderes als die Array-Indizierung ausführen, und die Vorteile eines Iterators, wenn Sie C++ - Container verwenden. Ist es nur, weil es keine Rolle spielt, wenn Sie ein Array durchlaufen? Oder sollte ich alles zusammen vermeiden und einen anderen Typ wie size_t verwenden?

38
Elpezmuerto

Dies ist ein allgemeineres Phänomen. Oft verwenden Menschen nicht die richtigen Typen für ihre Ganzzahlen. Modernes C verfügt über semantische Typedefs, die den primitiven Ganzzahlentypen vorzuziehen sind. Zum Beispiel sollte alles, was eine "Größe" ist, als size_t eingegeben werden. Wenn Sie die semantischen Typen systematisch für Ihre Anwendungsvariablen verwenden, sind Schleifenvariablen auch bei diesen Typen wesentlich einfacher.

Und ich habe mehrere Bugs gesehen, bei denen es schwierig war, sie zu erkennen, wenn int verwendet wurde. Code, der plötzlich auf große Matrizen und ähnliches abgestürzt ist. Nur das richtige Codieren mit den richtigen Typen vermeidet das.

29
Jens Gustedt

Die Verwendung von int ist aus logischer Sicht korrekter für die Indexierung eines Arrays.

unsigned semantisch bedeutet in C und C++ nicht wirklich "nicht negativ", sondern eher "Bitmaske" oder "Modulo-Ganzzahl".

Um zu verstehen, warum unsigned kein guter Typ für eine "nicht negative" Zahl ist, beachten Sie bitte

  • Durch Hinzufügen einer möglicherweise negativen Ganzzahl zu einer nicht negativen Ganzzahl erhalten Sie eine nicht negative Ganzzahl
  • Die Differenz zweier nicht negativer Ganzzahlen ist immer eine nicht negative Ganzzahl
  • Wenn Sie eine nicht negative ganze Zahl mit einer negativen ganzen Zahl multiplizieren, erhalten Sie ein nicht negatives Ergebnis

Offensichtlich macht keiner der oben genannten Ausdrücke Sinn ... aber es ist, wie C und C++ unsigned tatsächlich funktionieren.

Die Verwendung eines unsigned-Typs für die Größe von Containern ist eigentlich ein Konstruktionsfehler von C++, und leider sind wir jetzt dazu verdammt, diese falsche Wahl für immer zu verwenden (aus Gründen der Abwärtskompatibilität). Sie mögen den Namen "unsigned", weil er ähnlich wie "nicht negativ" 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 ist meine persönlich bevorzugte Form beim Codieren der meisten Schleifen auf Vektoren:

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

(Natürlich wird angenommen, dass sich die Größe des Vektors während der Iteration nicht ändert und dass ich tatsächlich den Index im Körper brauche, da ansonsten die for (auto& x : v)... besser ist).

Dieses schnellstmögliche Weglaufen von unsigned und die Verwendung von einfachen Ganzzahlen hat den Vorteil, dass die Fallen vermieden werden, die eine Folge von unsigned size_t-Konstruktionsfehlern sind. 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 hat Probleme, wenn der pts-Vektor leer ist, da pts.size()-1 in diesem Fall eine große Unsinnzahl ist. Der Umgang mit Ausdrücken, bei denen a < b-1 selbst für häufig verwendete Werte nicht mit a+1 < b identisch ist, ist wie das Tanzen in einem Minenfeld.

Historisch ist die Begründung dafür, dass size_t unsigniert ist, dass das zusätzliche Bit für die Werte verwendet werden kann, z. 65535 Elemente in Arrays anstelle von nur 32767 auf 16-Bit-Plattformen. Meiner Meinung nach war der Mehraufwand für diese falsche semantische Entscheidung meiner Meinung nach nicht den Gewinn wert (und wenn 32767 Elemente jetzt nicht ausreichen, dann sind 65535 ohnehin nicht lang genug).

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 normal signierte Ganzzahlen viel besser, da die Semantik das ist, was Sie erwarten würden.

Vorzeichenlose Werte sind der ideale Typ, wenn Sie die modulo-arithmetische Eigenschaft benötigen oder wenn Sie auf Bitebene arbeiten möchten.

35
6502

Es ist reine Faulheit und Ignoranz. Sie sollten immer die richtigen Typen für Indizes verwenden. Wenn Sie nicht über weitere Informationen verfügen, die den Bereich möglicher Indizes einschränken, ist size_t der richtige Typ.

Wenn die Dimension aus einem Einzelbyte-Feld in einer Datei gelesen wurde, wissen Sie natürlich, dass sie im Bereich von 0 bis 255 liegt, und int wäre ein durchaus sinnvoller Indextyp. Ebenso wäre int in Ordnung, wenn Sie eine festgelegte Anzahl von Wiederholungen ausführen, beispielsweise 0 bis 99. Es gibt jedoch noch einen weiteren Grund, int nicht zu verwenden: Wenn Sie i%2 in Ihrem Schleifenkörper verwenden, um gerade/ungerade Indizes unterschiedlich zu behandeln, ist i%2 viel teurer, wenn i signiert ist als wenn i unsigniert ist ...

4
R..

Kein großer Unterschied. Ein Vorteil von int ist das Signieren. So macht int i < 0 Sinn, während unsigned i < 0 nicht viel macht.

Wenn Indizes berechnet werden, kann dies vorteilhaft sein (z. B. können Sie Fälle erhalten, in denen Sie niemals eine Schleife betreten, wenn ein Ergebnis negativ ist).

Und ja, es ist weniger zu schreiben :-)

4
littleadv

Die Verwendung von int zum Indexieren eines Arrays ist zwar veraltet, wird aber immer noch weit verbreitet. int ist nur ein allgemeiner Nummerntyp und entspricht nicht den Adressierungsfunktionen der Plattform. Falls es kürzer oder länger ist, kann es zu merkwürdigen Ergebnissen kommen, wenn Sie versuchen, ein sehr großes Array zu indizieren, das darüber hinausgeht.

Auf modernen Plattformen garantieren off_t, ptrdiff_t und size_t weitaus mehr Portabilität.

Ein weiterer Vorteil dieser Typen ist, dass sie jemandem, der den Code liest, context geben. Wenn Sie die obigen Typen sehen, wissen Sie, dass der Code Array-Subskriptionen oder Zeigerarithmetik ausführt, nicht nur irgendeine Berechnung.

Wenn Sie also kugelsicheren, portablen und kontextsensitiven Code schreiben möchten, können Sie dies auf Kosten einiger Tastatureingaben tun.

GCC unterstützt sogar eine Erweiterung typeof, die Sie davon abhält, überall den gleichen Typnamen einzugeben:

typeof(arraySize) i;

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

Wenn Sie dann den Typ von arraySize ändern, ändert sich der Typ von i automatisch.

2

Ich verwende int, da weniger physische Eingaben erforderlich sind und es spielt keine Rolle - sie beanspruchen dieselbe Menge an Speicherplatz. Wenn Ihr Array nur wenige Milliarden Elemente enthält, werden Sie nicht überlaufen, wenn Sie keinen 16-Bit-Compiler verwenden , was ich normalerweise nicht bin. 

0
Claudiu

Das hängt wirklich vom Codierer ab. Einige Codierer bevorzugen den Typ-Perfektionismus, daher verwenden sie den Typ, mit dem sie verglichen werden. Wenn Sie beispielsweise einen C-String durchlaufen, sehen Sie möglicherweise Folgendes:

size_t sz = strlen("hello");
for (size_t i = 0; i < sz; i++) {
    ...
}

Wenn sie nur zehnmal etwas tun, sehen Sie wahrscheinlich immer noch int:

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

Wenn Sie nicht über ein Array mit einer Größe von mehr als zwei Gigabyte des Typs char oder 4 Gigabyte des Typs short oder 8 Gigabytes des Typs int usw. verfügen, spielt es keine Rolle, ob die Variable signiert ist.

Warum also mehr eingeben, wenn Sie weniger eingeben können?

0
Shahbaz

Abgesehen von dem Problem, dass die Eingabe kürzer ist, können negative Zahlen zugelassen werden.

Da wir nicht im Voraus sagen können, ob ein Wert jemals negativ sein kann, übernehmen die meisten Funktionen, die Ganzzahlargumente annehmen, die vorzeichenbehaftete Vielfalt. Da die meisten Funktionen vorzeichenbehaftete Ganzzahlen verwenden, ist es oft weniger Arbeit, vorzeichenbehaftete Ganzzahlen für beispielsweise Schleifen zu verwenden. Andernfalls besteht die Möglichkeit, dass Sie eine Reihe von Typumwandlungen hinzufügen müssen.

Bei der Umstellung auf 64-Bit-Plattformen sollte der vorzeichenlose Bereich einer vorzeichenbehafteten Ganzzahl für die meisten Zwecke mehr als ausreichend sein. In diesen Fällen gibt es keinen Grund, keine vorzeichenbehaftete Ganzzahl zu verwenden.

0
Jonathan Wood

Betrachten Sie das folgende einfache Beispiel:

int max = some_user_input; // or some_calculation_result
for(unsigned int i = 0; i < max; ++i)
    do_something;

Wenn max ein negativer Wert ist, beispielsweise -1, wird der -1 als UINT_MAX betrachtet (wenn zwei Ganzzahlen mit dem sam-Rang, aber unterschiedlichen Vorzeichen verglichen werden, wird die vorzeichenbehaftete als vorzeichenlose Zahl behandelt). Auf der anderen Seite hätte der folgende Code dieses Problem nicht:

int max = some_user_input;
for(int i = 0; i < max; ++i)
    do_something;

Geben Sie eine negative max-Eingabe an, die Schleife wird sicher übersprungen.

0
Infinite