webentwicklung-frage-antwort-db.com.de

Soll ich std :: size_t oder int in meinen for-Schleifen verwenden?

Ich frage mich nur, ob ich std::size_t Für Schleifen und andere Dinge anstelle von int verwenden soll. Zum Beispiel:

#include <cstdint>

int main()
{
    for (std::size_t i = 0; i < 10; ++i) {
        // std::size_t OK here? Or should I use, say, unsigned int instead?
    }
}

Was ist im Allgemeinen die beste Vorgehensweise für die Verwendung von std::size_t?

181
nhaa123

Eine gute Faustregel gilt für alles, was Sie im Loop-Zustand mit etwas vergleichen müssen, das natürlich selbst ein std::size_t Ist.

std::size_t Ist der Typ eines beliebigen sizeof-Ausdrucks und kann garantiert die maximale Größe eines Objekts (einschließlich eines Arrays) in C++ ausdrücken. Durch die Erweiterung wird auch garantiert, dass es groß genug für jeden Array-Index ist, so dass es ein natürlicher Typ für eine Schleife nach Index über ein Array ist.

Wenn Sie nur bis zu einer Zahl zählen, ist es möglicherweise sinnvoller, entweder den Typ der Variablen zu verwenden, die diese Zahl enthält, oder ein int oder unsigned int (Falls groß genug), wie dies sein sollte eine natürliche Größe für die Maschine sein.

168
CB Bailey

size_t ist der Ergebnistyp des Operators sizeof.

Verwenden size_t für Variablen, die Größe oder Index in einem Array modellieren. size_t vermittelt Semantik: Sie wissen sofort, dass es sich um eine Größe in Bytes oder einen Index handelt und nicht nur um eine andere Ganzzahl.

Auch mit size_t, um eine Größe in Byte darzustellen, trägt dazu bei, den Code portabel zu machen.

69
Gregory Pakosz

Das size_t type soll size von etwas angeben, so dass es natürlich ist, es zu verwenden, zum Beispiel die Länge eines Strings abzurufen und dann jedes Zeichen zu verarbeiten:

for (size_t i = 0, max = strlen (str); i < max; i++)
    doSomethingWith (str[i]);

Sie müssen natürlich do auf Randbedingungen achten, da es sich um einen nicht signierten Typ handelt. Die Grenze am oberen Ende ist normalerweise nicht so wichtig, da das Maximum normalerweise groß ist (obwohl es ist möglich ist, dorthin zu gelangen). Die meisten Leute verwenden einfach ein int für diese Art von Dingen, weil sie selten Strukturen oder Arrays haben, die groß genug sind, um die Kapazität dieses int zu überschreiten.

Aber achte auf Dinge wie:

for (size_t i = strlen (str) - 1; i >= 0; i--)

dies führt zu einer Endlosschleife aufgrund des Umbruchverhaltens von Werten ohne Vorzeichen (obwohl ich gesehen habe, dass Compiler davor warnen). Dies kann auch durch Folgendes gelindert werden (etwas schwieriger zu verstehen, aber zumindest immun gegen Verpackungsprobleme):

for (size_t i = strlen (str); i-- > 0; )

Durch Verschieben der Dekrementierung in einen Nebeneffekt der Fortsetzungsbedingung nach dem Überprüfen wird der Wert before dekrementiert, wobei jedoch der dekrementierte Wert innerhalb der Schleife verwendet wird (dh warum die Schleife von len .. 1 eher, als len-1 .. 0).

30
paxdiablo

Per Definition, size_t ist das Ergebnis des Operators sizeof. size_t wurde erstellt, um sich auf Größen zu beziehen.

Die Häufigkeit, mit der Sie etwas tun (10 in Ihrem Beispiel), hat nichts mit Größen zu tun. Warum also size_t? int oder unsigned int, sollte in Ordnung sein.

Natürlich ist es auch wichtig, was Sie mit i innerhalb der Schleife tun. Wenn Sie es an eine Funktion übergeben, die ein unsigned int, zum Beispiel, wähle unsigned int.

In jedem Fall empfehle ich, implizite Typkonvertierungen zu vermeiden. Machen Sie alle Typkonvertierungen explizit.

12
Daniel Daranas

size_t ist eine sehr gut lesbare Methode, um die Größenabmessung eines Elements anzugeben - Länge eines Strings, Anzahl der Bytes, die ein Zeiger benötigt usw. Es ist auch plattformübergreifend portierbar - Sie werden feststellen, dass sich 64-Bit- und 32-Bit-Funktionen gut mit Systemfunktionen verhalten und size_t - etwas, das unsigned int möglicherweise nicht (z. B. wann sollten Sie unsigned long

10
Ofir

Verwenden Sie std :: size_t zum Indizieren/Zählen von Arrays im C-Stil.

Für AWL-Container haben Sie (zum Beispiel) vector<int>::size_type, das zum Indizieren und Zählen von Vektorelementen verwendet werden soll.

In der Praxis handelt es sich in der Regel um beide nicht signierte Ints, dies ist jedoch nicht garantiert, insbesondere wenn benutzerdefinierte Zuweiser verwendet werden.

8
Peter Alexander

Bald werden die meisten Computer 64-Bit-Architekturen mit 64-Bit-Betriebssystemen sein, auf denen Programme ausgeführt werden, die auf Containern mit Milliarden von Elementen ausgeführt werden. Dann muss benutze size_t anstelle von int als Schleifenindex, andernfalls wird Ihr Index mlaufend am 2 ^ 32: ten Element auf 32- und 64-Bit-Systemen.

Bereite dich auf die Zukunft vor!

8
Nordlöw

kurze Antwort:

fast nie

lange Antwort:

Wann immer Sie einen Vektor mit mehr als 2 GB Zeichen auf einem 32-Bit-System benötigen. In jedem anderen Anwendungsfall ist die Verwendung eines vorzeichenbehafteten Typs viel sicherer als die Verwendung eines vorzeichenlosen Typs.

beispiel:

std::vector<A> data;
[...]
// calculate the index that should be used;
size_t i = calc_index(param1, param2);
// doing calculations close to the underflow of an integer is already dangerous

// do some bounds checking
if( i - 1 < 0 ) {
    // always false, because 0-1 on unsigned creates an underflow
    return LEFT_BORDER;
} else if( i >= data.size() - 1 ) {
    // if i already had an underflow, this becomes true
    return RIGHT_BORDER;
}

// now you have a bug that is very hard to track, because you never 
// get an exception or anything anymore, to detect that you actually 
// return the false border case.

return calc_something(data[i-1], data[i], data[i+1]);

Das unterzeichnete Äquivalent von size_t ist ptrdiff_t, nicht int. Aber die Verwendung von int ist in den meisten Fällen immer noch viel besser als size_t. ptrdiff_t ist long auf 32- und 64-Bit-Systemen.

Dies bedeutet, dass Sie immer von und nach size_t konvertieren müssen, wenn Sie mit std :: -Containern interagieren, die nicht sehr schön sind. Aber auf einer laufenden Native-Konferenz erwähnten die Autoren von c ++, dass das Entwerfen von std :: vector mit einem vorzeichenlosen size_t ein Fehler war.

Wenn Ihr Compiler Sie bei impliziten Konvertierungen von ptrdiff_t nach size_t warnt, können Sie dies mit der Konstruktorsyntax explizit machen:

calc_something(data[size_t(i-1)], data[size_t(i)], data[size_t(i+1)]);

wenn Sie eine Sammlung ohne Einschränkung durchlaufen möchten, verwenden Sie den Bereich basierend auf:

for(const auto& d : data) {
    [...]
}

hier ein paar Worte von Bjarne Stroustrup (C++ - Autorin) bei Going Native

Für einige Leute ist dieser signierte/nicht signierte Designfehler in der STL Grund genug, nicht den std :: vector zu verwenden, sondern eine eigene Implementierung.

7
Arne

Seien Sie bei der Verwendung von size_t vorsichtig mit dem folgenden Ausdruck

size_t i = containner.find("mytoken");
size_t x = 99;
if (i-x>-1 && i+x < containner.size()) {
    cout << containner[i-x] << " " << containner[i+x] << endl;
}

Sie erhalten im if-Ausdruck den Wert false, unabhängig davon, welchen Wert Sie für x haben. Ich habe mehrere Tage gebraucht, um dies zu realisieren (der Code ist so einfach, dass ich keinen Komponententest durchgeführt habe), obwohl es nur wenige Minuten dauerte, bis ich die Ursache des Problems herausgefunden hatte. Ich bin mir nicht sicher, ob es besser ist, einen Wurf oder eine Null zu machen.

if ((int)(i-x) > -1 or (i-x) >= 0)

Beide Möglichkeiten sollten funktionieren. Hier ist mein Testlauf

size_t i = 5;
cerr << "i-7=" << i-7 << " (int)(i-7)=" << (int)(i-7) << endl;

Die Ausgabe: i-7 = 18446744073709551614 (int) (i-7) = - 2

Ich möchte die Kommentare anderer.

4
Kemin Zhou

size_t wird von verschiedenen Bibliotheken zurückgegeben, um anzuzeigen, dass die Größe dieses Containers nicht Null ist. Sie verwenden es, wenn Sie einmal zurückkommen: 0

In Ihrem obigen Beispiel ist das Durchschleifen von size_t jedoch ein potenzieller Fehler. Folgendes berücksichtigen:

for (size_t i = thing.size(); i >= 0; --i) {
  // this will never terminate because size_t is a typedef for
  // unsigned int which can not be negative by definition
  // therefore i will always be >= 0
  printf("the never ending story. la la la la");
}

die Verwendung von Ganzzahlen ohne Vorzeichen kann zu solchen subtilen Problemen führen. Daher bevorzuge ich imho, size_t nur zu verwenden, wenn ich mit Containern/Typen interagiere, die dies erfordern.

2
ascotan