In welchen Fällen passen die Datentypen 'uint' und 'short' besser als die Standard-Int (32)?

24

Ich verstehe die Unterschiede in der Kapazität und den Werten, die sie darstellen können, aber es scheint, als würden die Leute immer etwas verwenden, Int32unabhängig davon, ob es angemessen ist. Niemand scheint jemals die vorzeichenlose Version ( uint) zu verwenden, obwohl sie häufig besser passt, da sie einen Wert beschreibt, der nicht negativ sein kann (möglicherweise um eine ID eines Datenbankdatensatzes darzustellen). Auch scheint niemand jemals short/Int16unabhängig von der erforderlichen Kapazität des Werts zu verwenden.

Objektiv gesehen, gibt es Fälle, in denen es besser ist, sie zu verwenden, uintoder, short/Int16wenn ja, welche?

Alternatex
quelle
13
Popularität ist nicht immer eine brauchbare Messgröße für die Bewertung von Software-Design-Entscheidungen. Nur weil eine Praxis beliebt ist, heißt das noch lange nicht, dass sie für Ihre spezielle Anwendung geeignet oder sogar eine gute Praxis ist.
Robert Harvey
1
Die kurze Antwort, denke ich, ist, dass sich Programmierer an die vorzeichenbehaftete Semantik gewöhnt haben und dazu neigen, diese anzunehmen, selbst wenn es sich um vorzeichenlose Typen (und damit um vorzeichenlose Semantik) handelt. Die meisten Leute gehen davon aus, dass der Programmierer faul oder ungebildet ist, aber der betreffende Programmierer ist möglicherweise sehr gebildet und sehr vorsichtig und möchte subtile Fallstricke vermeiden. Wenn Sie möchten, werfen Sie einen Blick auf soundsoftware.ac.uk/c-pitfall-unsigned und anteru.net/2010/05/17/736 .
Theodoros Chatzigiannakis
Bei einer vorzeichenlosen Zahl ist das Vorzeichen mehr nullals positiv oder negativ. Wenn Sie es als etwas betrachten, das niemals negativ oder immer positiv sein kann, werden Sie über die Ergebnisse überrascht (und oft wütend) sein, weil es nicht wirklich so funktioniert, besonders wenn es mit / verglichen oder subtrahiert wird. von vorzeichenbehafteten Werten.
Adam D. Ruppe
1
Nach meiner Erfahrung interessieren sich viele Programmierer, die jemals in der Sprache C programmiert haben, immer noch für Bytes an GB Speicher und Speicherplatz.
user1451111

Antworten:

25

Ich vermute, Sie beziehen sich auf eine Perspektive, die von Ihren eigenen Erfahrungen geprägt ist, in der Sie nicht mit Leuten gearbeitet haben, die Integraltypen richtig verwenden. Dies mag durchaus ein häufiges Ereignis sein, aber ich habe die Erfahrung gemacht, dass die Leute sie normalerweise auch richtig anwenden.

Der Vorteil ist der Speicherplatz und die CPU-Zeit, möglicherweise auch der E / A-Speicherplatz, je nachdem, ob die Typen jemals über das Kabel oder auf eine Festplatte gesendet werden. Mit unsignierten Typen können Sie Compilerprüfungen durchführen, um sicherzustellen, dass bestimmte Operationen, die nicht möglich sind, nicht ausgeführt werden. Außerdem können Sie den verfügbaren Bereich erweitern und die kleinere Größe beibehalten, um die Leistung bei Bedarf zu steigern.

Die korrekte Verwendung ist wie man erwarten würde - wann immer Sie wissen , für bestimmte sie nutzen zu können permanent (nicht beschränken nicht ohne Sicherheit oder Sie werden es später bereuen).

  • Wenn Sie versuchen, etwas darzustellen, das niemals vernünftigerweise negativ sein könnte ( public uint NumberOfPeople), verwenden Sie einen Typ ohne Vorzeichen.
  • Wenn Sie versuchen, etwas darzustellen, das vernünftigerweise niemals größer als 255 ( public byte DamagedToothCount) sein könnte, verwenden Sie ein Byte.
  • Wenn Sie versuchen, etwas darzustellen, das angemessenerweise größer als 255, aber niemals eine signifikante Anzahl von Tausenden sein könnte , verwenden Sie short ( public short JimmyHoffasBankBalance).
  • Wenn Sie versuchen, etwas darzustellen, das viele Hunderttausende, sogar Millionen, aber wahrscheinlich nie mehrere Milliarden erreichen wird, verwenden Sie int ( public int HoursSinceUnixEpoch).
  • Wenn Sie sicher sind, dass diese Zahl einen unbegrenzt großen Wert hat, oder wenn Sie denken, dass sie mehrere Milliarden hat, aber Sie nicht sicher sind, wie viele Milliarden es sind, ist long Ihre beste Wette. Wenn long nicht groß genug ist, haben Sie ein interessantes Problem und müssen sich zunächst mit willkürlichen Präzisionszahlen befassen ( public long MyReallyGreatAppsUserCountThisIsNotWishfulThinkingAtAll).

Diese Argumentation kann bei der Auswahl zwischen vorzeichenbehafteten, vorzeichenlosen und unterschiedlichen Schriftgrößen verwendet werden. Denken Sie nur an die logischen Wahrheiten der Daten, die Sie in der Realität darstellen.

Jimmy Hoffa
quelle
11
+1, obwohl ich klarstellen muss, dass es sich bei den Telefonnummern nicht um Zahlen handelt, sondern um Ziffernfolgen und optionale Formatierungen. Sie scheinen sich dessen bewusst zu sein, aber wir wollen kein schlechtes Beispiel geben, oder? Eine willkürliche Einschränkung des Wertebereichs ist ein kurzsichtiges Gegenmuster - intüberall, wenn Sie nicht wissen, dass die Problemdomäne den Wert tatsächlich einschränkt -, und keine Bank möchte die Anzahl der Konten auf 33.000 Pfund (und denkt an den Spaß) beschränken wenn das überläuft ...!).
Amon
3
Neues Lebensziel: Erhebliche Überziehung, die unter dem integralen Typ meines Bankkontos liegt.
recursion.ninja
11
Es gibt gute Gründe, an bestimmten Stellen keine vorzeichenlosen Typen zu verwenden, z. B. wenn Arithmetik zwischen vorzeichenbehafteten und vorzeichenlosen Typen gemischt wird. Siehe Was sind die Best Practices für nicht signierte Ints? .
19
Ich bin mit der Begründung hier nicht einverstanden. Vorzeichenlose Typen sind häufig ein Fehler, da Subtraktionen und Vergleiche unerwartet sind, wenn Sie an Ints gewöhnt sind (sie funktionieren konsistent, sind aber nicht "immer positiv"). Ich würde sie vermeiden, es sei denn, Sie haben einen ganz bestimmten Grund, sie zu verwenden. Warum ist die Größe auch wichtig für byte vs short vs int? Oft sparen Sie nicht einmal Platz, da Strukturen diese Elemente oder Arrays an eine bestimmte Ausrichtung anpassen. Ich würde ein Byte nur dann verwenden, wenn die Größe wirklich wichtig ist (insbesondere bei C # -Code, den ich gesehen habe, unwahrscheinlich) oder wenn Sie speziell einen Umbruch bei 255 für etwas wünschen.
Adam D. Ruppe
4
"Vorteil ist Speicherplatz und CPU-Zeit" ... Ich sehe keinen Fall, in dem winzige Typen tatsächlich CPU-Zeit sparen würden. Ganzzahlige Operationen werden nie schneller als bei maschinengroßen Typen, dh was die CPU betrifft, können Sie sie auch verwenden long. Das Speichern von Speicher kann natürlich indirekt Zeit sparen, indem die Effizienz der Cache-Zeilen usw. verbessert wird, aber OTOH-Ausrichtungsprobleme bei kleinen Typen können indirekt Zeit kosten.
Am
16

Klar, es gibt Fälle, in denen es besser ist, uintoder shortoder zu verwenden Int16. Wenn Sie wissen, dass Ihr Datenbereich in die Einschränkungen dieses Variablentyps passt, ist es in Ordnung, diesen Typ zu verwenden.

In speicherbeschränkten Umgebungen oder bei der Verarbeitung großer Mengen von Objekten kann es sinnvoll sein, die kleinste Größenvariable zu verwenden. Zum Beispiel gibt es einen signifikanten Größenunterschied für ein Million-Elemente-Array von ints vs. shorts.

In der Regel tritt dies aus einem oder mehreren der folgenden Gründe im tatsächlichen Code nicht auf:

  • Datenbeschränkungen waren nicht im Voraus bekannt
  • Es bestand die Möglichkeit, dass die Datenbeschränkungen nicht solide waren oder wahrscheinlich geändert wurden
  • Es bestand die Hoffnung, die Funktion mit einem breiteren Datenbereich wiederzuverwenden
  • Der Entwickler hat sich nicht die Zeit genommen, die Einschränkungen zu überdenken
  • Speichereinsparungen waren unerheblich, um die Verwendung eines kleineren Variablentyps zu rechtfertigen

Es gibt noch viele weitere mögliche Gründe, die sich jedoch auf Folgendes beschränken: Die Zeit, die für die Entscheidung und die Verwendung eines anderen Variablentyps erforderlich ist, hat nicht ausgereicht, um dies zu rechtfertigen.


quelle
8

In C wurden in Kontexten ohne ganzzahlige Heraufstufung vorzeichenlose Werte angegeben, die sich wie Mitglieder eines abstrakten algebraischen Rings "umhüllen" verhalten (also liefert XY für jedes X und Y einen eindeutigen Wert, der bei Addition zu Y X ergibt ), während vorzeichenbehaftete Integer-Typen so angegeben wurden, dass sie sich wie Ganzzahlen verhalten, wenn die Berechnungen innerhalb eines bestimmten Bereichs bleiben, und überhaupt nichts tun dürfen, wenn die Berechnungen darüber hinausgehen. Die numerische Semantik in C # ist jedoch völlig anders. Innerhalb eines überprüften numerischen Kontexts verhalten sich sowohl vorzeichenbehaftete als auch vorzeichenlose Typen wie Ganzzahlen, vorausgesetzt, die Berechnungen bleiben im Bereich und werden ausgelöst, OverflowExceptionwenn dies nicht der Fall ist. In einem ungeprüften Kontext verhalten sich beide wie algebraische Ringe.

In der Regel lohnt es sich nur, einen Datentyp zu verwenden, der kleiner Int32ist als derjenige, der zum Packen oder Auspacken von Gegenständen für eine kompakte Lagerung oder einen kompakten Transport erforderlich ist. Wenn eine halbe Milliarde positive Zahlen gespeichert werden müssen und alle im Bereich von 0 bis 100 liegen, werden durch die Verwendung von jeweils einem Byte anstelle von vier 1,5 Gigabyte Speicherplatz eingespart. Das ist eine große Ersparnis. Wenn ein Codeteil jedoch insgesamt ein paar Hundert Werte speichern muss, werden durch die Angabe eines Bytes anstelle von vier etwa 600 Bytes eingespart. Wahrscheinlich nicht lohnenswert.

In Bezug auf vorzeichenlose Typen sind sie nur dann wirklich nützlich, wenn Sie den Informationsaustausch durchführen oder wenn Sie Zahlen in Teile unterteilen. Wenn beispielsweise 96-Bit-Ganzzahlen berechnet werden müssen, ist es wahrscheinlich viel einfacher, die Berechnungen für Gruppen mit drei vorzeichenlosen 32-Bit-Ganzzahlen durchzuführen, als für Gruppen mit vorzeichenbehafteten Ganzzahlen. Andernfalls gibt es nicht viele Situationen, in denen der Bereich eines vorzeichenbehafteten 32- oder 64-Bit-Werts nicht ausreichend wäre, aber die gleiche Größe eines vorzeichenlosen Werts ausreicht.

Superkatze
quelle
4

Es ist im Allgemeinen eine schlechte Idee, nicht signierte Typen zu verwenden, da diese auf unangenehme Weise überlaufen. x = 5-6ist plötzlich eine Zeitbombe in Ihrem Code. In der Zwischenzeit laufen die Vorteile von nicht signierten Typen auf ein einziges zusätzliches Maß an Präzision hinaus. Wenn sich das für Sie lohnt, sollten Sie mit ziemlicher Sicherheit einen größeren Typ verwenden.

Es gibt Anwendungsfälle, in denen ein kleinerer Typ sinnvoll sein könnte. Wenn Sie sich jedoch keine Sorgen über die Speichernutzung machen oder Daten für die Übertragung oder die Cache-Effizienz packen müssen oder eine Handvoll anderer Probleme, ist die Verwendung eines kleineren Typs in der Regel nicht vorteilhaft . Darüber hinaus ist es bei vielen Architekturen tatsächlich langsamer , diese Typen zu verwenden, sodass sie tatsächlich geringe Kosten verursachen können.

Jack Aidley
quelle
3
In C ist der vorzeichenbehaftete Überlauf sogar noch schlimmer als der vorzeichenlose Überlauf (da das Verhalten undefiniert ist, während der vorzeichenlose Überlauf wie ein Kilometerzähler angegeben ist). OTOH, signierter Über- / Unterlauf, ist in der Praxis viel seltener als nicht signierter Unterlauf.
Kevin
Ein wahrer, aber signierter Überlauf ist normalerweise offensichtlicher und vorhersehbarer.
Jack Aidley
Ich stimme im Allgemeinen, aber Sie bewusst sein müssen, zum Beispiel, dass moderne Compiler optimieren kann i+1>iin , 1wenn iunterzeichnet, zusammen mit einer ganzen Reihe von anderen fiesen Verhalten. Ein vorzeichenloser Überlauf kann einen Fehler in einem Eckfall verursachen. Ein signierter Überlauf kann Ihr gesamtes Programm bedeutungslos machen .
Kevin
@ JackAidley Ich bin mir ziemlich sicher, was Sie sagen, macht keinen Sinn, da 5-6 das gleiche Bitmuster ergibt, egal ob es vorzeichenlos ist oder nicht.
Ingo
@Ingo: Wie oft siehst du dir Bitmuster an? Entscheidend ist die Bedeutung des Bitmusters und nicht, welche Bits ein- oder ausgeschaltet sind.
Jack Aidley
2

Wenn Sie sich speziell mit .NET-Typen befassen, wird die CLS-Kompatibilität häufig vergessen und möglicherweise als tangential für Ihre Frage angesehen . Nicht alle Typen sind für alle auf .NET Framework basierenden Sprachen verfügbar.

Wenn Sie Code für andere Sprachen als C # schreiben und sicherstellen möchten, dass dieser Code mit so vielen .NET-Sprachen wie möglich zusammenarbeitet, müssen Sie die Verwendung Ihres Typs auf CLS-kompatible Sprachen beschränken.

Beispielsweise unterstützten frühe Versionen von VB.NET (7.0 und 7.1) keine Ganzzahlen ohne Vorzeichen ( UInteger):

http://msdn.microsoft.com/en-us/library/aa903459(v=vs.71).aspx

Ganzzahlen ohne Vorzeichen sind nicht CLS-konform und sollten daher mit Vorsicht verwendet werden, wenn Sie sich nicht sicher sind, wer Ihr Klassenbibliotheks-Consumer sein wird.

Kev
quelle