it-swarm.dev

Was sind die Best Practices für nicht signierte Ints?

Ich verwende überall vorzeichenlose Ints und bin mir nicht sicher, ob ich sollte. Dies kann von Datenbank-Primärschlüssel-ID-Spalten bis hin zu Zählern usw. reichen. Wenn eine Zahl niemals negativ sein sollte, verwende ich immer ein vorzeichenloses int.

Ich bemerke jedoch aus dem Code anderer, dass niemand anderes dies zu tun scheint. Gibt es etwas Entscheidendes, das ich übersehen habe?

Bearbeiten: Seit dieser Frage ist mir auch aufgefallen, dass in C negative Werte für Fehler zurückgegeben werden, anstatt Ausnahmen wie in C++ auszulösen.

45
wting

Gibt es etwas Entscheidendes, das ich übersehen habe?

Wenn Berechnungen sowohl vorzeichenbehaftete als auch vorzeichenlose Typen sowie unterschiedliche Größen umfassen, können die Regeln für die Typheraufstufung komplex sein und zu nerwartetes Verhalten führen.

Ich glaube, dies ist der Hauptgrund, warum Java vorzeichenlose int-Typen weggelassen hat.

28

Ich denke, dass Michael einen gültigen Punkt hat, aber IMO der Grund, warum jeder die ganze Zeit int verwendet (besonders in for (int i = 0; i < max, i++) ist, dass wir es so gelernt haben. Wenn jedes einzelne Beispiel in einem Buch ' zum Erlernen des Programmierens ' int in einer for -Schleife verwendet, sind es nur sehr wenige wird diese Praxis jemals in Frage stellen.

Der andere Grund ist, dass int 25% kürzer als uint ist und wir alle faul sind ... ;-)

18
Treb

Das Codieren von Bereichsinformationen in Typen ist eine gute Sache. Es erzwingt die Verwendung angemessener Zahlen zur Kompilierungszeit.

Viele Architekturen scheinen spezielle Anweisungen für den Umgang mit int -> float Konvertierungen zu haben. Die Konvertierung von unsigned kann langsamer sein (ein kleines bisschen) .

11

Das Mischen von signierten und nicht signierten Typen kann Sie in eine Welt voller Schmerzen versetzen. Und Sie können nicht alle vorzeichenlosen Typen verwenden, da Sie auf Dinge stoßen, die entweder einen gültigen Bereich haben, der negative Zahlen enthält, oder einen Wert benötigen, um einen Fehler anzuzeigen, und -1 am natürlichsten ist. Das Nettoergebnis ist also, dass viele Programmierer alle vorzeichenbehafteten Ganzzahltypen verwenden.

8
David Schwartz

Ich benutze unsigned int in C++ meistens für Array-Indizes und für jeden Zähler, der bei 0 beginnt. Ich denke, es ist gut, explizit zu sagen, "diese Variable kann nicht negativ sein".

7
quant_dev

Für mich geht es bei Typen viel um Kommunikation. Wenn Sie explizit ein vorzeichenloses int verwenden, sagen Sie mir, dass vorzeichenbehaftete Werte keine gültigen Werte sind. Auf diese Weise kann ich beim Lesen Ihres Codes zusätzlich zum Variablennamen einige Informationen hinzufügen. Im Idealfall würde mir ein nicht anonymer Typ mehr sagen, aber er gibt mir mehr Informationen, als wenn Sie überall Ints verwendet hätten.

Leider ist sich nicht jeder sehr bewusst, was sein Code kommuniziert, und das ist wahrscheinlich der Grund, warum Sie überall Ints sehen, obwohl die Werte zumindest ohne Vorzeichen sind.

7
daramarak

Sie sollten sich darum kümmern, wenn Sie mit einer Ganzzahl arbeiten, die sich möglicherweise tatsächlich den Grenzen eines signierten Int nähert oder diese überschreitet. Da das positive Maximum einer 32-Bit-Ganzzahl 2.147.483.647 beträgt, sollten Sie ein vorzeichenloses int verwenden, wenn Sie wissen, dass es a) niemals negativ sein wird und b) möglicherweise 2.147.483.648 erreicht. In den meisten Fällen, einschließlich Datenbankschlüsseln und Zählern, werde ich mich diesen Zahlen niemals nähern, sodass ich mir keine Sorgen darüber mache, ob das Vorzeichenbit für einen numerischen Wert oder zur Angabe des Vorzeichens verwendet wird.

Ich würde sagen: Verwenden Sie int, es sei denn, Sie wissen, dass Sie ein vorzeichenloses int benötigen.

3
Joel Etherton

Es ist ein Kompromiss zwischen Einfachheit und Zuverlässigkeit. Je mehr Fehler beim Kompilieren abgefangen werden können, desto zuverlässiger ist die Software. Unterschiedliche Personen und Organisationen befinden sich in diesem Bereich in unterschiedlichen Punkten.

Wenn Sie in Ada jemals eine hochzuverlässige Programmierung durchführen, verwenden Sie sogar verschiedene Typen für Variablen wie Entfernung in Fuß und Entfernung in Metern, und der Compiler kennzeichnet sie, wenn Sie versehentlich eine der anderen zuweisen. Das ist perfekt für die Programmierung einer Lenkwaffe, aber übertrieben (Wortspiel beabsichtigt), wenn Sie ein Webformular validieren. Es ist nicht unbedingt irgendetwas falsch, solange es den Anforderungen entspricht.

3
Karl Bielefeldt

Ich bin geneigt, Joel Ethertons Argumentation zuzustimmen, komme aber zu dem gegenteiligen Schluss. So wie ich es sehe, gibt es, selbst wenn Sie wissen, dass Zahlen wahrscheinlich nie an die Grenzen eines vorzeichenbehafteten Typs gelangen, wenn Sie wissen, dass negative Zahlen nicht vorkommen, kaum einen Grund, sie zu verwenden die signierte Variante eines Typs.

Aus dem gleichen Grund habe ich in einigen ausgewählten Fällen in SQL Server-Tabellen BIGINT (64-Bit-Ganzzahl) anstelle von INTEGER (32-Bit-Ganzzahl) verwendet. Die Wahrscheinlichkeit, dass die Daten innerhalb eines angemessenen Zeitraums die 32-Bit-Grenze erreichen, ist gering. In diesem Fall können die Folgen jedoch in einigen Situationen ziemlich verheerend sein. Stellen Sie nur sicher, dass Sie die Typen zwischen den Sprachen richtig zuordnen, sonst werden Sie wirklich weit unten auf interessante Verrücktheiten stoßen ...

Für einige Dinge, wie z. B. vorzeichenbehaftete oder nicht signierte Datenbank-Primärschlüsselwerte, spielt dies jedoch keine Rolle, da Sie sich nie direkt mit dem Wert befassen, es sei denn, Sie reparieren fehlerhafte Daten manuell oder ähnliches. Es ist eine Kennung, nichts weiter. In diesen Fällen ist die Konsistenz wahrscheinlich wichtiger als die genaue Wahl der Signatur. Andernfalls erhalten Sie einige Fremdschlüsselspalten, die signiert sind, und andere, die nicht signiert sind, ohne erkennbares Muster - oder wieder diese interessante Verrücktheit.

2
a CVn

Ich würde empfehlen, außerhalb von Speicherplatzbeschränkungen und Datenaustauschkontexten generell signierte Typen zu verwenden. In den meisten Fällen, in denen eine vorzeichenbehaftete 32-Bit-Ganzzahl zu klein wäre, aber ein vorzeichenloser 32-Bit-Wert für heute ausreichen würde, dauert es nicht lange, bis der vorzeichenlose 32-Bit-Wert auch nicht groß genug ist.

Die primären Zeiten, in denen vorzeichenlose Typen verwendet werden sollten, sind, wenn entweder mehrere Werte zu einem größeren zusammengesetzt werden (z. B. vier Bytes in eine 32-Bit-Zahl konvertiert werden) oder größere Werte in kleinere zerlegt werden (z. B. Speichern einer 32-Bit-Zahl als vier Bytes) ), oder wenn eine Menge vorhanden ist, von der erwartet wird, dass sie regelmäßig "überrollt", und man sich damit befassen muss (denken Sie an einen Haushaltszähler für Privathaushalte; die meisten von ihnen haben genügend Ziffern, um sicherzustellen, dass sie möglicherweise nicht zwischen den Ablesungen überschlagen wenn sie dreimal im Jahr abgelesen werden, aber nicht genug, um sicherzustellen, dass sie nicht innerhalb der Nutzungsdauer des Messgeräts überlaufen). Vorzeichenlose Typen haben oft genug "Verrücktheit", dass sie nur in Fällen verwendet werden sollten, in denen ihre Semantik erforderlich ist.

1
supercat

Ich verwende vorzeichenlose Ints, um meinen Code und seine Absicht klarer zu machen. Eine Sache, die ich tue, um unerwartete implizite Konvertierungen beim Rechnen mit vorzeichenbehafteten und vorzeichenlosen Typen zu vermeiden, ist die Verwendung eines vorzeichenlosen Kurzschlusses (normalerweise 2 Bytes) für meine vorzeichenlosen Variablen. Dies ist aus mehreren Gründen wirksam:

  • Wenn Sie mit Ihren vorzeichenlosen Kurzvariablen und Literalen (vom Typ int) oder Variablen vom Typ int rechnen, wird sichergestellt, dass die vorzeichenlose Variable vor der Auswertung des Ausdrucks immer zu einem int heraufgestuft wird, da int immer einen höheren Rang als short hat . Dies vermeidet jedes unerwartete Verhalten beim Rechnen mit vorzeichenbehafteten und vorzeichenlosen Typen, vorausgesetzt, das Ergebnis des Ausdrucks passt natürlich in ein vorzeichenbehaftetes int.
  • In den meisten Fällen überschreiten die von Ihnen verwendeten vorzeichenlosen Variablen nicht den Maximalwert eines vorzeichenlosen 2-Byte-Kurzschlusses (65.535).

Das allgemeine Prinzip ist, dass der Typ Ihrer nicht signierten Variablen einen niedrigeren Rang als der Typ der signierten Variablen haben sollte, um die Beförderung zum signierten Typ sicherzustellen. Dann haben Sie kein unerwartetes Überlaufverhalten. Natürlich können Sie dies nicht immer sicherstellen, aber (meistens) ist es möglich, dies sicherzustellen.

Zum Beispiel hatte ich kürzlich einige for-Schleifen wie diese:

const unsigned short cuint = 5;
for(unsigned short i=0; i<10; ++i)
{
    if((i-2)%cuint == 0)
    {
       //Do something
    }
}

Das Literal '2' ist vom Typ int. Wenn i ein vorzeichenloses int anstelle eines vorzeichenlosen Kurzschlusses wäre, würde 2 im Unterausdruck (i-2) zu einem vorzeichenlosen int heraufgestuft (da vorzeichenloses int eine höhere Priorität als vorzeichenbehaftetes int hat). Wenn i = 0 ist, ist der Unterausdruck gleich (0u-2u) = ein massiver Wert aufgrund eines Überlaufs. Dieselbe Idee mit i = 1. Da i jedoch ein nicht signierter Kurzfilm ist, wird er auf den gleichen Typ wie das Literal '2' hochgestuft, das int signiert ist, und alles funktioniert einwandfrei.

Für zusätzliche Sicherheit: In dem seltenen Fall, in dem die Architektur, für die Sie implementieren, int 2 Byte verursacht, kann dies dazu führen, dass beide Operanden im arithmetischen Ausdruck auf unsigned int heraufgestuft werden, wenn die vorzeichenlose kurze Variable nicht passt in das vorzeichenbehaftete 2-Byte-Int, von dem letzteres einen Maximalwert von 32.767 <65.535 hat. (Weitere Informationen finden Sie unter https://stackoverflow.com/questions/17832815/c-implicit-conversion-signed-unsigned ). Um dies zu verhindern, können Sie Ihrem Programm einfach einen static_assert wie folgt hinzufügen:

static_assert(sizeof(int) == 4, "int must be 4 bytes");

und es wird nicht auf Architekturen kompiliert, bei denen int 2 Bytes beträgt.

1
AdmiralAdama