2016-01-06 12 views
14

Ich untersuche einen Standard für mein Team um size_t vs int (oder long, etc.). Der größte Nachteil, den ich gesehen habe, ist, dass die Verwendung von zwei size_t-Objekten zu Problemen führen kann (ich bin nicht sicher, ob bestimmte Probleme aufgetreten sind - vielleicht wurde etwas nicht ergänzt und der Compiler von signed/unsigned). Ich schrieb ein schnelles Programm in C++ die V120 VS2013-Compiler, die mir erlaubt, die folgendes zu tun:Ist es sicher, die Differenz von zwei size_t Objekten zu nehmen?

#include <iostream> 

main() 
{ 
    size_t a = 10; 
    size_t b = 100; 
    int result = a - b; 
} 

Das Programm in -90 geführt, die zwar richtig, mich über Art Mismatches nervös macht, mit/ohne Vorzeichen Probleme oder einfach undefiniertes Verhalten, wenn size_t in komplexen Mathe verwendet wird.

Meine Frage ist, ob es sicher ist, Mathe mit size_t Objekten zu tun, insbesondere, den Unterschied zu machen? Ich überlege, size_t als Standard für Dinge wie Indizes zu verwenden. Ich habe einige interessante Beiträge zum Thema hier gesehen, aber sie befassen sich nicht mit dem Thema Mathematik (oder ich habe es verpasst).

What type for subtracting 2 size_t's?

typedef for a signed type that can contain a size_t?

+2

Wenn die Subtraktion zu einer negativen Zahl führt, [wird es umgebrochen] (https://stackoverflow.com/questions/1269019/what-should-happen-to-the-negation-of-asize-tie -sizeofstruct-foo) nach [zwei Kompliment] (https://en.wikipedia.org/wiki/Two%27s_complement) – CoryKramer

+4

Die Art, wie Sie es haben, ist es nicht sicher. Aber nicht wegen der Mathematik selbst. Sie werfen ein "size_t" -Ergebnis (das wahrscheinlich "unsigned long" ist) in ein "int" (vorzeichenbehaftet), was wahrscheinlich ein kleinerer Datentyp ist. –

+5

Der entsprechende Unterschiedstyp von size_t ist ptrdiff_t, nicht int –

Antwort

14

Dies ist nicht portabel arbeiten garantiert, ist aber auch nicht UB. Der Code muss ohne Fehler ausgeführt werden, aber der resultierende int Wert ist Implementierung definiert. Solange Sie also auf Plattformen arbeiten, die das gewünschte Verhalten garantieren, ist dies in Ordnung (solange der Unterschied durch eine int natürlich dargestellt werden kann), ansonsten verwenden Sie einfach überall signierte Typen (siehe letzten Absatz).

Subtraktion von zwei std::size_t s wird eine neue std::size_t und sein Wert ergeben wird durch Umwickeln bestimmt werden. In Ihrem Beispiel wird unter der Annahme von 64 Bit size_t, a - b gleich 18446744073709551526. Dies passt nicht in ein (allgemein verwendetes 32 Bit) int, daher wird ein implementierungsdefinierter Wert result zugewiesen.

Um ehrlich zu sein, würde ich empfehlen, keine Ganzzahlen ohne Vorzeichen für etwas anderes als etwas Magie zu verwenden. Mehrere Mitglieder des Standard-Komitee mir zustimmen: https://channel9.msdn.com/Events/GoingNative/2013/Interactive-Panel-Ask-Us-Anything 09.50, 42:40, 1:02:50

Daumenregel (paraphrasieren Chandler Carruth aus dem obigen Video): Wenn Sie es könnten sich zählen, verwenden int Verwenden Sie andernfalls std::int64_t.


Es sei denn, seine Umwandlung Rang kleiner als int, z.B. wenn std::size_tunsigned short ist. In diesem Fall ist das Ergebnis ein int und alles wird gut funktionieren (es sei denn int ist nicht breiter als short). Jedoch

  1. Ich kenne keine Plattform, die dies tut.
  2. Dies wäre immer noch plattformspezifisch, siehe ersten Absatz.
+0

Es gibt viele Plattformen, wo 'sizeof (int) == sizeof (kurz)' und 'sizeof (int) == sizeof (size_t)' (alle 16 Bits). Das ist fast auf jedem 8 und 16 Bit Mikrocontroller der Fall (AVR, PIC10-18, MSP430, ...) – 12431234123412341234123

5

Wenn Sie nicht size_t nicht verwenden, werden Sie geschraubt: size_t ist ein Typ, der für Speichergrößen verwendet werden, vorhanden ist, und daher immer groß genug für diesen Zweck garantiert sein. (uintptr_t ist ziemlich ähnlich, aber es ist weder der erste solche Typ noch wird es von den Standardbibliotheken verwendet, noch ist es ohne stdint.h verfügbar.) Wenn Sie eine int verwenden, können Sie undefiniertes Verhalten erhalten, wenn Ihre Zuordnungen 2GiB der Adresse überschreiten Speicherplatz (oder 32kB, wenn Sie auf einer Plattform sind, wo int nur 16 Bit ist!), obwohl die Maschine mehr Arbeitsspeicher hat und Sie im 64-Bit-Modus ausführen.

Wenn Sie einen Unterschied von size_t benötigen, der negativ werden kann, verwenden Sie die signierte Variante ssize_t.

+1

"Verwenden Sie die signierte Variante' ssize_t' "... wie? Würden Sie vor oder nach der Subtraktion spielen? Was ist mit der verlorenen Reichweite? –

+0

@BenVoigt Der verlorene Bereich mit 'ssize_t' ist ein Bit, der verlorene Bereich von' int' ist häufig 32 Bit, es kann sogar 48 Bit sein (wenn 'int' 16 Bit und' size_t' 64 Bit ist). Ergo: 'ssize_t' ist definitiv viel besser als' int'. – cmaster

+1

@BenVoigt Es ist nicht wirklich wichtig, wenn du den Cast machst: Wenn du einen Overflow bekommst, bist du sowieso mit "int" und "ssize_t" verschraubt. Alle Gesundheitsüberprüfungen müssen durchgeführt werden, bevor die Differenz berechnet wird. – cmaster

4

Der Typ size_t ist unsigniert. Die Subtraktion von zwei beliebigen size_t Werten ist definiert - Verhalten

Zunächst ist das Ergebnis jedoch implementierungsdefiniert, wenn ein größerer Wert von einem kleineren subtrahiert wird. Das Ergebnis ist der mathematische Wert, reduziert auf den kleinsten positiven Rest modulo SIZE_T_MAX + 1. Wenn zum Beispiel der größte Wert von size_t 65535 ist und das Ergebnis der Subtraktion von zwei size_t Werten -3 ist, wird das Ergebnis 65536 - 3 = 65533 sein. Auf einem anderen Compiler oder einer anderen Maschine mit einem anderen size_t wird der numerische Wert sein anders.

Zweitens könnte ein size_t Wert außerhalb des Bereichs des Typs int liegen. Wenn dies der Fall ist, erhalten wir ein zweites umsetzungsdefiniertes Ergebnis, das aus der erzwungenen Umwandlung resultiert. In dieser Situation kann jedes Verhalten gelten; es muss lediglich durch die Implementierung dokumentiert werden und die Konvertierung darf nicht fehlschlagen. Zum Beispiel könnte das Ergebnis in den Bereich int geklemmt werden, wodurch INT_MAX erzeugt wird. Ein allgemeines Verhalten bei Zweierkomplement-Maschinen (praktisch alle) bei der Umwandlung breiterer (oder gleicher Breite) vorzeichenloser Typen in schmalere vorzeichenbehaftete Typen ist eine einfache Bitverkürzung: Es werden genügend Bits aus dem vorzeichenlosen Wert genommen, um den vorzeichenbehafteten Wert einschließlich seines Vorzeichens zu füllen Bit.

Aufgrund der Funktionsweise von Zweierkomplement, wenn das ursprüngliche arithmetisch korrekte abstrakte Ergebnis selbst in int passt, wird die Konvertierung das Ergebnis liefern. Nehmen wir beispielsweise an, dass die Subtraktion eines Paars von 64-Bit-Werten size_t auf einer Zweierkomplement-Maschine den abstrakten arithmetischen Wert -3 ergibt, der zum positiven Wert 0xFFFFFFFFFFFFFFFD wird. Wenn dies in ein 32 Bit int erzwungen wird, dann ist das übliche Verhalten, das in vielen Compilern für Zweierkomplement-Maschinen gesehen wird, dass die unteren 32 Bits als das Abbild des resultierenden int: 0xFFFFFFFD genommen werden. Und das ist natürlich nur der Wert -3 in 32 Bits.

So das Fazit ist, dass Code de facto ganz tragbar ist, weil fast alle Mainstream-Maschinen sind Zweierkomplement mit Konvertierungsregeln auf Basis von Vorzeichenerweiterung und Bit Abschneiden, auch zwischen mit und ohne Vorzeichen.

Mit Ausnahme dieser Zeichenerweiterung tritt nicht auf, wenn ein Wert bei der Konvertierung von unsigned in signed erweitert wird. So ist er ein Problem die seltene Situation, in der int breiter ist als size_t.Wenn ein 16-Bit-Ergebnis size_t 65533 ist, weil 4 von 1 subtrahiert wird, erzeugt dies kein -3, wenn es in ein 32-Bit konvertiert wird int; es wird 65533 produzieren!

+0

Vielleicht meinst du" implementierungsabhängig "und nicht" implementierungsdefiniert "? Der letztere Begriff hat eine formale Bedeutung, die durch den C++ - Standard zugewiesen wird. –

+0

Auch Ihr letzter Absatz hat vergessen, die integralen Promotions zu berücksichtigen, die durchgeführt werden, bevor die Subtraktion stattfindet. –

+1

@BenVoigt Richtig; aber das wird selten sein. Wie üblich ist es, dass "size_t" mit "unsigned short" oder "unsigned char" übereinstimmt? Immer noch, gültiger Punkt. Soweit "implementation-defined result" geht, ist diese Verwendung von ISO C in mir verankert. Alles in dieser Antwort gilt für C und C++. – Kaz