Im Google C ++ Style Guide zum Thema "Ganzzahlen ohne Vorzeichen" wird dies empfohlen
Aufgrund eines historischen Unfalls verwendet der C ++ - Standard auch vorzeichenlose Ganzzahlen, um die Größe von Containern darzustellen. Viele Mitglieder des Standardkörpers halten dies für einen Fehler, aber es ist derzeit praktisch unmöglich, ihn zu beheben. Die Tatsache, dass vorzeichenlose Arithmetik nicht das Verhalten einer einfachen Ganzzahl modelliert, sondern durch den Standard zur Modellierung modularer Arithmetik (Umlaufen bei Überlauf / Unterlauf) definiert wird, bedeutet, dass eine signifikante Klasse von Fehlern vom Compiler nicht diagnostiziert werden kann.
Was ist falsch an modularer Arithmetik? Ist das nicht das erwartete Verhalten eines Int ohne Vorzeichen?
Auf welche Art von Fehlern (eine bedeutende Klasse) bezieht sich der Leitfaden? Überlaufende Bugs?
Verwenden Sie keinen vorzeichenlosen Typ, um lediglich zu behaupten, dass eine Variable nicht negativ ist.
Ein Grund, warum ich mir vorstellen kann, signiertes int anstelle von nicht signiertem int zu verwenden, ist, dass es leichter zu erkennen ist, wenn es überläuft (zu negativ).
quelle
unsigned int x = 0; --x;
und zu sehen, wasx
wird. Ohne Grenzwertprüfungen könnte die Größe plötzlich einen unerwarteten Wert erhalten, der leicht zu UB führen könnte.int
Überlauf und Unterlauf sind UB. Es ist weniger wahrscheinlich, dass eine Situationint
auftritt, in der versucht wird, einen Wert auszudrücken, der nicht möglich ist, als eine Situation, in der ein Wertunsigned int
unter Null verringert wird. Die Art von Personen, die vom Verhalten derunsigned int
Arithmetik überrascht wären, ist jedoch die Art von Personen, die dies auch könnten Schreiben Sie Code, der einenint
überlaufbedingten UB verursachen würde, wiea < a + 1
z. B. die Überprüfung auf Überlauf.Antworten:
Einige der Antworten hier erwähnen die überraschenden Heraufstufungsregeln zwischen vorzeichenbehafteten und vorzeichenlosen Werten, aber dies scheint eher ein Problem beim Mischen von vorzeichenbehafteten und vorzeichenlosen Werten zu sein und erklärt nicht unbedingt, warum vorzeichenbehaftete Variablen außerhalb von Mischungsszenarien gegenüber vorzeichenlosen bevorzugt werden.
Nach meiner Erfahrung gibt es außerhalb gemischter Vergleiche und Beförderungsregeln zwei Hauptgründe, warum vorzeichenlose Werte Fehlermagnete sind:
Vorzeichenlose Werte haben eine Diskontinuität bei Null, dem häufigsten Wert bei der Programmierung
Sowohl vorzeichenlose als auch vorzeichenbehaftete Ganzzahlen weisen bei ihren Minimal- und Maximalwerten Diskontinuitäten auf, bei denen sie umlaufen (vorzeichenlos) oder undefiniertes Verhalten verursachen (vorzeichenbehaftet). Für
unsigned
diese Punkte sind bei Null undUINT_MAX
. Dennint
sie sind beiINT_MIN
undINT_MAX
. Typische Werte vonINT_MIN
undINT_MAX
auf einem System mit 4-Byte-int
Werten sind-2^31
und2^31-1
, und auf einem solchen SystemUINT_MAX
ist typischerweise2^32-1
.Das Hauptproblem
unsigned
, das Fehler verursacht , besteht darin,int
dass es eine Diskontinuität bei Null aufweist . Null ist natürlich ein sehr häufiger Wert in Programmen, zusammen mit anderen kleinen Werten wie 1,2,3. Es ist üblich, kleine Werte, insbesondere 1, in verschiedenen Konstrukten zu addieren und zu subtrahieren. Wenn Sie etwas von einemunsigned
Wert subtrahieren und dieser zufällig Null ist, erhalten Sie nur einen massiven positiven Wert und einen fast sicheren Fehler.Betrachten Sie Code-Iterationen über alle Werte in einem Vektor nach Index mit Ausnahme der letzten 0,5 :
for (size_t i = 0; i < v.size() - 1; i++) { // do something }
Dies funktioniert einwandfrei, bis Sie eines Tages einen leeren Vektor übergeben. Anstatt null Iterationen durchzuführen, erhalten Sie
v.size() - 1 == a giant number
1 und 4 Milliarden Iterationen und haben fast eine Pufferüberlauf-Sicherheitsanfälligkeit.Sie müssen es so schreiben:
for (size_t i = 0; i + 1 < v.size(); i++) { // do something }
So kann es in diesem Fall "behoben" werden, aber nur durch sorgfältiges Nachdenken über die unsignierte Natur von
size_t
. Manchmal können Sie den obigen Fix nicht anwenden, weil Sie anstelle eines konstanten einen variablen Offset haben, den Sie anwenden möchten, der positiv oder negativ sein kann. Welche "Seite" des Vergleichs Sie also anwenden müssen, hängt von der Signatur ab - Jetzt wird der Code wirklich chaotisch.Es gibt ein ähnliches Problem mit Code, der versucht, bis einschließlich Null zu iterieren. So etwas
while (index-- > 0)
funktioniert gut, aber das scheinbar Äquivalentwhile (--index >= 0)
wird niemals für einen vorzeichenlosen Wert beendet. Ihr Compiler warnt Sie möglicherweise, wenn die rechte Seite buchstäblich Null ist, aber sicherlich nicht, wenn es sich um einen zur Laufzeit ermittelten Wert handelt.Kontrapunkt
Einige könnten argumentieren, dass signierte Werte auch zwei Diskontinuitäten aufweisen. Warum also nicht signierte auswählen? Der Unterschied besteht darin, dass beide Diskontinuitäten sehr (maximal) weit von Null entfernt sind. Ich halte dies wirklich für ein separates Problem des "Überlaufs". Sowohl vorzeichenbehaftete als auch vorzeichenlose Werte können bei sehr großen Werten überlaufen. In vielen Fällen ist ein Überlauf aufgrund von Einschränkungen des möglichen Wertebereichs nicht möglich, und ein Überlauf vieler 64-Bit-Werte kann physikalisch unmöglich sein. Selbst wenn dies möglich ist, ist die Wahrscheinlichkeit eines Fehlers im Zusammenhang mit einem Überlauf im Vergleich zu einem Fehler "bei Null" häufig gering, und ein Überlauf tritt auch bei vorzeichenlosen Werten auf . So unsigned kombiniert das Schlimmste aus beiden Welten: potenzieller Überlauf mit sehr großen Größenwerten und eine Diskontinuität bei Null. Signiert hat nur der erstere.
Viele werden argumentieren, "Sie verlieren ein bisschen" mit unsigniert. Dies ist häufig der Fall - aber nicht immer (wenn Sie Unterschiede zwischen vorzeichenlosen Werten darstellen müssen, verlieren Sie dieses Bit sowieso: So viele 32-Bit-Dinge sind sowieso auf 2 GiB beschränkt, oder Sie haben eine seltsame Grauzone, in der es heißt Eine Datei kann 4 GiB groß sein, aber Sie können bestimmte APIs für die zweite Hälfte von 2 GiB nicht verwenden.
Selbst in den Fällen, in denen unsignierte Sie ein bisschen kaufen: Es kauft Ihnen nicht viel: Wenn Sie mehr als 2 Milliarden "Dinge" unterstützen müssten, müssten Sie wahrscheinlich bald mehr als 4 Milliarden unterstützen.
Vorzeichenlose Werte sind logischerweise eine Teilmenge vorzeichenbehafteter Werte
Mathematisch gesehen sind vorzeichenlose Werte (nicht negative Ganzzahlen) eine Teilmenge vorzeichenbehafteter Ganzzahlen (nur _integers genannt). 2 . Doch unterzeichnet natürlich Werte Pop aus Operationen ausschließlich auf unsigned Werte wie Subtraktion. Wir könnten sagen, dass vorzeichenlose Werte nicht unter Subtraktion geschlossen werden . Gleiches gilt nicht für vorzeichenbehaftete Werte.
Möchten Sie das "Delta" zwischen zwei vorzeichenlosen Indizes in einer Datei finden? Nun, Sie sollten die Subtraktion besser in der richtigen Reihenfolge durchführen, sonst erhalten Sie die falsche Antwort. Natürlich benötigen Sie häufig eine Laufzeitprüfung, um die richtige Reihenfolge zu ermitteln! Wenn Sie vorzeichenlose Werte als Zahlen behandeln, werden Sie häufig feststellen, dass (logisch) signierte Werte sowieso immer wieder angezeigt werden. Sie können also genauso gut mit signierten Werten beginnen.
Kontrapunkt
Wie in Fußnote (2) oben erwähnt, sind vorzeichenbehaftete Werte in C ++ keine Teilmenge von vorzeichenlosen Werten derselben Größe, sodass vorzeichenlose Werte dieselbe Anzahl von Ergebnissen darstellen können wie vorzeichenbehaftete Werte.
Stimmt, aber der Bereich ist weniger nützlich. Betrachten Sie die Subtraktion und vorzeichenlose Zahlen mit einem Bereich von 0 bis 2N und vorzeichenbehaftete Zahlen mit einem Bereich von -N bis N. Beliebige Subtraktionen führen in beiden Fällen zu Ergebnissen im Bereich von -2N bis 2N, und beide Arten von Ganzzahlen können nur darstellen die Hälfte. Nun, es stellt sich heraus, dass der Bereich, der um Null von -N bis N zentriert ist, normalerweise viel nützlicher ist (enthält mehr tatsächliche Ergebnisse im Code der realen Welt) als der Bereich 0 bis 2N. Betrachten Sie eine andere typische Verteilung als die einheitliche (log, zipfian, normal, was auch immer) und ziehen Sie in Betracht, zufällig ausgewählte Werte von dieser Verteilung zu subtrahieren: Viel mehr Werte enden in [-N, N] als in [0, 2N] (tatsächlich resultierende Verteilung ist immer auf Null zentriert).
64-Bit schließt die Tür aus vielen Gründen, vorzeichenbehaftete Werte als Zahlen zu verwenden
Ich denke , die Argumente oben bereits überzeugend waren für 32-Bit - Werte, aber die Überlauffälle, die bei verschiedenen Schwellenwerten sowohl mit und ohne Vorzeichen beeinflussen, kann auftreten , für 32-Bit - Werte, da „2000000000“ eine Zahl , die von vielen überschritten können abstrakte und physikalische Größen (Milliarden von Dollar, Milliarden von Nanosekunden, Arrays mit Milliarden von Elementen). Wenn also jemand genug von der Verdoppelung des positiven Bereichs für vorzeichenlose Werte überzeugt ist, kann er den Fall vertreten, dass ein Überlauf eine Rolle spielt und vorzeichenlos etwas bevorzugt.
Außerhalb spezialisierter Domänen beseitigen 64-Bit-Werte dieses Problem weitgehend. Vorzeichenbehaftete 64-Bit-Werte haben einen oberen Bereich von 9.223.372.036.854.775.807 - mehr als neun Billionen . Das sind viele Nanosekunden (ungefähr 292 Jahre) und viel Geld. Es ist auch ein größeres Array, als jeder Computer wahrscheinlich lange Zeit RAM in einem kohärenten Adressraum hat. Vielleicht reichen 9 Billionen für alle (vorerst)?
Wann werden vorzeichenlose Werte verwendet?
Beachten Sie, dass der Styleguide die Verwendung von Zahlen ohne Vorzeichen nicht verbietet oder sogar unbedingt davon abhält. Es schließt mit:
In der Tat gibt es gute Verwendungsmöglichkeiten für vorzeichenlose Variablen:
Wenn Sie eine N-Bit-Menge nicht als Ganzzahl, sondern einfach als "Beutel mit Bits" behandeln möchten. Zum Beispiel als Bitmaske oder Bitmap oder als N-Boolesche Werte oder was auch immer. Diese Verwendung geht oft Hand in Hand mit den Typen mit fester Breite wie
uint32_t
unduint64_t
da Sie häufig die genaue Größe der Variablen wissen möchten. Ein Hinweis darauf , dass eine bestimmte Variable diese Behandlung verdient , ist , dass man nur auf sich mit den arbeitet bitweise Operatoren wie~
,|
,&
,^
,>>
und so weiter, und nicht mit den arithmetischen Operationen wie+
,-
,*
,/
usw.Unsigned ist hier ideal, da das Verhalten der bitweisen Operatoren genau definiert und standardisiert ist. Vorzeichenbehaftete Werte weisen verschiedene Probleme auf, z. B. undefiniertes und nicht angegebenes Verhalten beim Verschieben und eine nicht angegebene Darstellung.
Wenn Sie tatsächlich modulare Arithmetik wollen. Manchmal möchten Sie tatsächlich 2 ^ N modulare Arithmetik. In diesen Fällen ist "Überlauf" eine Funktion, kein Fehler. Vorzeichenlose Werte geben Ihnen hier das, was Sie wollen, da sie für die Verwendung modularer Arithmetik definiert sind. Vorzeichenbehaftete Werte können überhaupt nicht (einfach, effizient) verwendet werden, da sie eine nicht spezifizierte Darstellung haben und der Überlauf undefiniert ist.
0.5 Nachdem ich das geschrieben hatte, wurde mir klar, dass dies fast identisch mit Jarods Beispiel ist , das ich nicht gesehen hatte - und aus gutem Grund ist es ein gutes Beispiel!
1 Wir sprechen
size_t
hier also normalerweise von 2 ^ 32-1 auf einem 32-Bit-System oder 2 ^ 64-1 auf einem 64-Bit-System.2 In C ++ ist dies nicht genau der Fall, da vorzeichenlose Werte am oberen Ende mehr Werte enthalten als der entsprechende vorzeichenbehaftete Typ. Es besteht jedoch das Grundproblem, dass die Bearbeitung vorzeichenloser Werte zu (logisch) vorzeichenbehafteten Werten führen kann, aber es gibt kein entsprechendes Problem mit vorzeichenbehafteten Werten (da vorzeichenbehaftete Werte bereits vorzeichenlose Werte enthalten).
quelle
-ftrapv
, die alle vorzeichenbehafteten Überläufe, aber nicht alle vorzeichenlosen Überläufe abfangen können. Die Auswirkungen auf die Leistung sind nicht allzu schlecht, daher kann es-ftrapv
in einigen Szenarien sinnvoll sein, damit zu kompilieren .That's about the age of the universe measured in nanoseconds.
Das bezweifle ich. Das Universum ist ungefähr13.7*10^9 years
alt, was ist4.32*10^17 s
oder4.32*10^26 ns
. Um4.32*10^26
als int darzustellen , benötigen Sie mindestens90 bits
.9,223,372,036,854,775,807 ns
wäre nur etwa292.5 years
.Wie bereits erwähnt, Mischen
unsigned
undsigned
könnte zu einem unerwarteten Verhalten führt (auch wenn sie gut definiert).Angenommen, Sie möchten alle Elemente des Vektors mit Ausnahme der letzten fünf durchlaufen, dann schreiben Sie möglicherweise falsch:
for (int i = 0; i < v.size() - 5; ++i) { foo(v[i]); } // Incorrect // for (int i = 0; i + 5 < v.size(); ++i) { foo(v[i]); } // Correct
Nehmen wir
v.size() < 5
dann an, wie esv.size()
istunsigned
,s.size() - 5
wäre es eine sehr große Zahl, und soi < v.size() - 5
wäre estrue
für einen mehr erwarteten Wertebereich voni
. Und UB passiert dann schnell (einmal ohne gebundenen Zugriffi >= v.size()
)Wenn ein
v.size()
vorzeichenbehafteter Wert zurückgegeben worden wäre,s.size() - 5
wäre er negativ gewesen, und im obigen Fall wäre die Bedingung sofort falsch.Auf der anderen Seite sollte der Index zwischen liegen
[0; v.size()[
,unsigned
was Sinn macht. Signed hat auch ein eigenes Problem als UB mit Überlauf oder implementierungsdefiniertem Verhalten für die Rechtsverschiebung einer negativ signierten Zahl, aber weniger häufige Fehlerquelle für die Iteration.quelle
i<size()-X
sollte man schreibeni+X<size()
. Sicher, es ist eine Sache, an die man sich erinnert, aber meiner Meinung nach ist es nicht so schwer, sich daran zu gewöhnen.Eines der haarsträubendsten Beispiele für einen Fehler ist, wenn Sie signierte und nicht signierte MIX-Werte verwenden:
#include <iostream> int main() { auto qualifier = -1 < 1u ? "makes" : "does not make"; std::cout << "The world " << qualifier << " sense" << std::endl; }
Die Ausgabe:
Die Welt macht keinen Sinn
Wenn Sie keine triviale Anwendung haben, ist es unvermeidlich, dass Sie entweder gefährliche Mischungen zwischen vorzeichenbehafteten und nicht vorzeichenbehafteten Werten erhalten (was zu Laufzeitfehlern führt), oder wenn Sie Warnungen auslösen und sie zur Kompilierungszeit fehlerhaft machen, erhalten Sie eine Menge static_casts in Ihrem Code. Aus diesem Grund ist es am besten, vorzeichenbehaftete Ganzzahlen für Typen für mathematische oder logische Vergleiche zu verwenden. Verwenden Sie nur vorzeichenlose Bitmasken und Typen, die Bits darstellen.
Das Modellieren eines Typs ohne Vorzeichen basierend auf der erwarteten Domäne der Werte Ihrer Zahlen ist eine schlechte Idee. Die meisten Zahlen liegen näher bei 0 als bei 2 Milliarden. Bei vorzeichenlosen Typen liegen viele Ihrer Werte näher am Rand des gültigen Bereichs. Um alles noch schlimmer zu machen, das letzte kann Wert in einem bekannten positiven Bereich, aber während Auswerten von Ausdrücken können Zwischenwerte unterlaufen, und wenn sie in Zwischenform verwendet werden können sehr falsche Werte. Schließlich, auch wenn Ihre Werte immer positiv zu erwarten sind, die nicht das bedeutet sie werden nicht interact mit anderen Variablen, kann negativ sein, und so können Sie mit einer Zwangssituation mit und ohne Vorzeichen Typen am Ende Mischen, das ist der schlechteste Ort zu sein.
quelle
Die Verwendung eines nicht signierten Typs führt nicht häufiger zu Fehlern als die Verwendung eines signierten Typs mit bestimmten Aufgabenklassen.
Verwenden Sie das richtige Werkzeug für den Job.
Wenn die Aufgabe gut übereinstimmt: nichts falsch. Nein, nicht wahrscheinlicher.
Sicherheits-, Verschlüsselungs- und Authentifizierungsalgorithmen setzen auf vorzeichenlose modulare Mathematik.
Auch Komprimierungs- / Dekomprimierungsalgorithmen sowie verschiedene Grafikformate profitieren und sind mit vorzeichenloser Mathematik weniger fehlerhaft.
Jedes Mal, wenn bitweise Operatoren und Verschiebungen verwendet werden, werden die vorzeichenlosen Operationen nicht mit den Vorzeichenerweiterungsproblemen der vorzeichenbehafteten Mathematik durcheinander gebracht .
Vorzeichenbehaftete Ganzzahlmathematik hat ein intuitives Erscheinungsbild, das von allen, einschließlich der Lernenden für das Codieren, leicht verstanden wird. C / C ++ war ursprünglich nicht als Ziel gedacht und sollte jetzt keine Intro-Sprache sein. Für eine schnelle Codierung, bei der Sicherheitsnetze für den Überlauf verwendet werden, sind andere Sprachen besser geeignet. Für schlanken, schnellen Code geht C davon aus, dass Codierer wissen, was sie tun (sie sind erfahren).
Eine Falle der signierten Mathematik ist heute das allgegenwärtige 32-Bit
int
, das bei so vielen Problemen für die allgemeinen Aufgaben ohne Bereichsprüfung gut genug ist. Dies führt zu Selbstzufriedenheit, gegen die der Überlauf nicht codiert ist. Stattdessenfor (int i=0; i < n; i++)
int len = strlen(s);
wird als OK angesehen , dan
wird angenommen , <INT_MAX
und Streicher werden nie zu lang sein, und nicht voll im ersten Fall oder Verwendung geschützt wird reichtesize_t
,unsigned
oder sogarlong long
in den zweiten.C / C ++ wurde in einer Zeit entwickelt, die sowohl 16-Bit als auch 32-Bit umfasste,
int
und das zusätzliche Bit, das ein nicht signiertes 16-Bitsize_t
bietet, war signifikant. In Bezug auf Überlaufprobleme war Aufmerksamkeit erforderlich, sei esint
oderunsigned
.Mit 32-Bit- (oder breiteren) Anwendungen von Google auf Nicht-16-Bit-
int/unsigned
Plattformen wird aufgrund derint
großen Reichweite der +/- Überlauf nicht berücksichtigt . Dies macht Sinn , für solche Anwendungen zu fördern ,int
überunsigned
. Dennoch istint
Mathe nicht gut geschützt.Die engen 16-Bit-
int/unsigned
Bedenken gelten heute für ausgewählte eingebettete Anwendungen.Die Richtlinien von Google gelten gut für Code, den sie heute schreiben. Dies ist keine endgültige Richtlinie für den größeren Bereich von C / C ++ - Code.
In C / C ++ ist ein vorzeichenbehafteter int math-Überlauf ein undefiniertes Verhalten und daher sicherlich nicht einfacher zu erkennen als das definierte Verhalten vorzeichenloser Mathematik.
Wie @Chris Uzdavinis gut kommentierte, wird das Mischen von signierten und nicht signierten Dateien am besten von allen (insbesondere Anfängern) vermieden und bei Bedarf sorgfältig codiert.
quelle
int
das Verhalten einer "tatsächlichen" Ganzzahl auch nicht modelliert. Undefiniertes Verhalten beim Überlauf ist nicht die Meinung eines Mathematikers zu ganzen Zahlen: Es gibt keine Möglichkeit eines "Überlaufs" mit einer abstrakten ganzen Zahl. Aber das sind Maschinenspeichereinheiten, keine Zahlen eines Mathematikers.signed int
Überlauf leicht zu erkennen (mit-ftrapv
), während ein nicht signierter "Überlauf" schwer zu erkennen ist.Ich habe einige Erfahrungen mit Googles Styleguide, AKA, dem Per Anhalter durch verrückte Programmierer, die vor langer Zeit in das Unternehmen eingestiegen sind. Diese spezielle Richtlinie ist nur ein Beispiel für die Dutzende verrückter Regeln in diesem Buch.
Fehler treten nur bei vorzeichenlosen Typen auf, wenn Sie versuchen, mit ihnen zu rechnen (siehe Beispiel von Chris Uzdavinis oben), dh wenn Sie sie als Zahlen verwenden. Vorzeichenlose Typen sind nicht zum Speichern numerischer Mengen vorgesehen, sondern zum Speichern von Zählwerten wie der Größe von Behältern, die niemals negativ sein können, und sie können und sollten für diesen Zweck verwendet werden.
Die Idee, arithmetische Typen (wie vorzeichenbehaftete Ganzzahlen) zum Speichern von Containergrößen zu verwenden, ist idiotisch. Würden Sie auch ein Double verwenden, um die Größe einer Liste zu speichern? Dass es bei Google Mitarbeiter gibt, die Containergrößen mit arithmetischen Typen speichern und von anderen verlangen, dass sie dasselbe tun, sagt etwas über das Unternehmen aus. Eine Sache, die ich an solchen Diktaten bemerke, ist, dass je dümmer sie sind, desto mehr müssen sie strenge Do-it-or-you-are-entlassen-Regeln sein, da sonst Menschen mit gesundem Menschenverstand die Regel ignorieren würden.
quelle
unsigned
Typen nur Zählungen enthalten könnten und nicht in der Arithmetik verwendet werden könnten. Daher ist der Teil "Wahnsinnige Anweisungen von schlechten Programmierern" sinnvoller.int
mit 16 Bit üblich, heute jedoch weitaus weniger), ist es besser, Zählungen zu haben, die sich wie Zahlen verhalten.Verwenden von vorzeichenlosen Typen zur Darstellung nicht negativer Werte ...
In den Google Coding Guidelines wird der Schwerpunkt auf die erste Art der Überlegung gelegt. Andere Richtliniensätze, wie die C ++ - Kernrichtlinien , legen mehr Wert auf den zweiten Punkt. Betrachten Sie beispielsweise die Kernrichtlinie I.12 :
Natürlich könnten Sie für einen
non_negative
Wrapper für Ganzzahlen argumentieren , der beide Fehlerkategorien vermeidet, aber das hätte seine eigenen Probleme ...quelle
In der Google-Anweisung geht es darum, unsigned als Größentyp für Container zu verwenden . Im Gegensatz dazu scheint die Frage allgemeiner zu sein. Bitte denken Sie daran, während Sie weiterlesen.
Da die meisten Antworten bisher auf die Google-Aussage reagierten, weniger auf die größere Frage, werde ich meine Antwort zu negativen Containergrößen beginnen und anschließend versuchen, jeden (hoffnungslos, ich weiß ...) davon zu überzeugen, dass unsigniert gut ist.
Signierte Behältergrößen
Nehmen wir an, jemand hat einen Fehler codiert, der zu einem negativen Containerindex führt. Das Ergebnis ist entweder undefiniertes Verhalten oder eine Ausnahme- / Zugriffsverletzung. Ist das wirklich besser, als undefiniertes Verhalten oder eine Ausnahme- / Zugriffsverletzung zu erhalten, wenn der Indextyp nicht signiert war? Ich denke nicht.
Nun gibt es eine Klasse von Menschen, die gerne über Mathematik und das, was in diesem Zusammenhang "natürlich" ist, sprechen. Wie kann ein integraler Typ mit negativer Zahl natürlich sein, um etwas zu beschreiben, das von Natur aus> = 0 ist? Verwenden Sie Arrays mit negativen Größen viel? Meiner Meinung nach würden besonders mathematisch veranlagte Personen diese Nichtübereinstimmung der Semantik (Größe / Indextyp sagt, dass negativ möglich ist, während ein Array mit negativer Größe schwer vorstellbar ist) als irritierend empfinden.
Die einzige Frage, die in dieser Angelegenheit noch offen ist, ist, ob ein Compiler - wie im Google-Kommentar angegeben - tatsächlich aktiv bei der Suche nach solchen Fehlern helfen kann. Und noch besser als die Alternative, bei der es sich um unterlaufgeschützte Ganzzahlen ohne Vorzeichen handelt (x86-64-Assembly und wahrscheinlich andere Architekturen haben Mittel, um dies zu erreichen, nur C / C ++ verwendet diese Mittel nicht). Die einzige Möglichkeit, die ich mir vorstellen kann, besteht darin, dass der Compiler automatisch Laufzeitprüfungen (
if (index < 0) throwOrWhatever
) hinzufügt oder im Falle von Aktionen zur Kompilierungszeit viele potenziell falsch positive Warnungen / Fehler erzeugt. "Der Index für diesen Array-Zugriff könnte negativ sein." Ich habe meine Zweifel, das wäre hilfreich.Außerdem ist es für Leute, die tatsächlich Laufzeitprüfungen für ihre Array- / Containerindizes schreiben, mehr Arbeit, sich mit vorzeichenbehafteten Ganzzahlen zu befassen. Anstatt zu schreiben, müssen
if (index < container.size()) { ... }
Sie jetzt schreiben :if (index >= 0 && index < container.size()) { ... }
. Sieht für mich nach Zwangsarbeit aus und nicht nach einer Verbesserung ...Sprachen ohne vorzeichenlose Typen saugen ...
Ja, das ist ein Stich in Java. Jetzt komme ich aus dem Hintergrund der eingebetteten Programmierung und wir haben viel mit Feldbussen gearbeitet, bei denen binäre Operationen (und oder oder xor, ...) und die bitweise Zusammensetzung von Werten buchstäblich das A und O sind. Für eines unserer Produkte wollten wir - oder besser gesagt ein Kunde - einen Java-Port ... und ich saß dem glücklicherweise sehr kompetenten Mann gegenüber, der den Port gemacht hat (ich lehnte ab ...). Er versuchte, gelassen zu bleiben ... und in der Stille zu leiden ... aber der Schmerz war da, er konnte nicht aufhören zu fluchen, nachdem er sich einige Tage lang ständig mit vorzeichenbehafteten Integralwerten befasst hatte, die nicht vorzeichenlos sein SOLLTEN ... Selbst wenn er Unit-Tests für schrieb Diese Szenarien sind schmerzhaft und ich persönlich denke, Java wäre besser dran gewesen, wenn sie vorzeichenbehaftete Ganzzahlen weggelassen und nur vorzeichenlose angeboten hätten ... zumindest dann müssen Sie sich nicht um Zeichenerweiterungen usw. kümmern.
Das sind meine 5 Cent in dieser Angelegenheit.
quelle