mit uint vs int [geschlossen]

83

Ich habe eine Weile beobachtet, dass C # -Programmierer dazu neigen, int überall zu verwenden und selten auf uint zurückzugreifen. Aber ich habe nie eine zufriedenstellende Antwort gefunden, warum.

Wenn Interoperabilität Ihr Ziel ist, sollte uint nicht in öffentlichen APIs angezeigt werden, da nicht alle CLI-Sprachen vorzeichenlose Ganzzahlen unterstützen. Das erklärt aber nicht, warum int selbst in internen Klassen so verbreitet ist. Ich vermute, dies ist der Grund, warum uint in der BCL sparsam verwendet wird.

Wenn Sie in C ++ eine Ganzzahl haben, für die negative Werte keinen Sinn ergeben, wählen Sie eine Ganzzahl ohne Vorzeichen.

Dies bedeutet eindeutig, dass negative Zahlen nicht zulässig sind oder erwartet werden, und der Compiler wird einige Überprüfungen für Sie durchführen. Ich vermute auch im Fall von Array-Indizes, dass die JIT die Prüfung der unteren Grenzen leicht fallen lassen kann.

Beim Mischen von Int- und Unit-Typen sind jedoch besondere Sorgfalt und Abdrücke erforderlich.

Sollte uint mehr verwendet werden? Warum?

Eloff
quelle
Ich dachte nur, ich hätte ein Deja Vu: D, fast genau die gleiche Frage wurde vor kurzem gestellt.
KroaX
Wie für die untere Grenze Kontrollen (im Fall müssen Sie Ihre eigenen schreiben) , können Sie ersetzen if (i < 0 || i >= length)mit if (unchecked((uint)i) >= length). Die resultierende IL hat insgesamt einen (Verzweigungs-) Befehl weniger und liefert ungefähr die gleiche Leistung (unendlich schneller). Persönlich liebe ich es einfach, weil es meinen Juckreiz gegen das Überprüfen der unteren Grenzen kratzt. Andere argumentieren wahrscheinlich aufgrund von "ungeprüft" dagegen, aber ich behaupte, dass dies eine sehr gute Linie ist, um ihre Bedeutung zu lernen, da es einfach und sofort aus dem Kontext klar ist, was der Zweck ist = hilft dem Leser zu lernen.
AnorZaken
Ich habe vergessen zu erwähnen, dass das Obige bei 64-Bit-Builds optimal ist, da es den Vergleich mit 64-Bit durchführt. Für 32-Bit-Builds if (unchecked((uint)i) >= unchecked((uint)length))ergibt sich eine bessere Leistung. Dies sieht jedoch sehr kompliziert aus, und der 64-Bit-Vergleich ist immer noch leistungsfähiger als die Standardprüfung der doppelt verzweigten Grenzen bei einem 32-Bit-Build, sodass ich dies in keiner vernünftigen Situation wirklich empfehlen kann. (Ich erwähne es meistens, um darauf hinzuweisen, dass ein 64-Bit-Vergleich ansonsten verwendet wird - was für einige nützliche Informationen sein könnte.)
AnorZaken
Meine Herren, ich behaupte, dass Ihre primäre Meinungsbasis nicht gut ist. Wenn eine objektive Antwort möglich ist, würde ich sie gerne hören. Ich bin alle dafür, meine Praktiken zum Nutzen zu ändern.
Joshua

Antworten:

50

uintIch vermute, dass Ihre Beobachtung, warum in der BCL nicht verwendet wird, der Hauptgrund ist.

UInt32 ist nicht CLS-konform, was bedeutet, dass es für die Verwendung in öffentlichen APIs völlig ungeeignet ist. Wenn Sie uint in Ihrer privaten API verwenden, bedeutet dies, dass Sie Konvertierungen in andere Typen durchführen - und es ist normalerweise einfacher und sicherer, den Typ einfach gleich zu halten.

Ich vermute auch, dass dies in der C # -Entwicklung nicht so häufig vorkommt, selbst wenn C # die einzige verwendete Sprache ist, vor allem, weil es in der BCL nicht häufig vorkommt. Entwickler versuchen im Allgemeinen, (dankenswerterweise) den Stil des Frameworks nachzuahmen, auf dem sie aufbauen. In C # bedeutet dies, dass Sie versuchen, Ihre öffentlichen und internen APIs so ähnlich wie möglich der .NET Framework BCL zu machen. Dies würde bedeuten, uint sparsam zu verwenden.

Reed Copsey
quelle
1
stackoverflow.com/questions/2013116/… ist eine Frage, die sich mit einem ähnlichen Thema befasst
Stephan
77

intist kürzer zu tippen als uint.

jjnguy
quelle
2
Ich vermute, das ist ziemlich nah an der Wahrheit. Warum verwenden, uintwenn 99% der Zeit (meiner Erfahrung nach) intausreichen?
Matthew Jones
12
@ Justin: "Magische Zahlen" wie -1 sind im Allgemeinen keine gute Idee. Wenn Sie auf Long umschalten, müssen Sie auch ohne Grund 2x Speicher verwenden ... "unit" ist auf jeden Fall wertvoll, vorausgesetzt, Sie müssen nicht mit anderen APIs interagieren.
Reed Copsey
28
Ich fühle mich nie wohl int, wenn ich ein Array indiziere, weil ich nie einen negativen Index haben werde. Scheint blind offensichtlich, dass uintin diesem Fall a verwendet werden sollte.
Mark H
2
Nicht zu vergessen, besser lesbar. Wenn Sie Ihren Code / Ihre Algorithmen jemals an eine andere Person weitergeben, die möglicherweise weniger erfahren ist als Sie, kann die Verwendung vieler Codes uintsie ein wenig aufhängen. intist in allen Situationen, in denen Sie die Reichweite steuern, durchaus akzeptabel.
Drharris
3
@MarkH Völlig einverstanden, aber bei einer umgekehrten Iteration kann dies nützlich sein in Form von : for (int i = arr.Length - 1; i >= 0; i--) { }. Wenn Sie dies mit einem Uint tun, führt dies zu einer Überlaufausnahme oder schlimmer noch zu einer Endlosschleife.
Aidiakapi
19

Normalerweise reicht intes aus. Wenn Sie alle folgenden Bedingungen erfüllen können, können Sie Folgendes verwenden uint:

  • Es ist nicht für eine öffentliche API (da uintes nicht CLS-kompatibel ist).
  • Sie brauchen keine negativen Zahlen.
  • Sie benötigen (möglicherweise) den zusätzlichen Bereich.
  • Sie verwenden es nicht im Vergleich mit < 0, da dies niemals der Fall ist true.
  • Sie verwenden es nicht im Vergleich mit >= 0, da dies niemals der Fall ist false.

Die letzte Anforderung wird oft vergessen und führt zu Fehlern:

static void Main(string[] args)
{
    if (args.Length == 0) return;
    uint last = (uint)(args.Length - 1);

    // This will eventually throw an IndexOutOfRangeException:
    for (uint i = last; i >= 0; i--)
    {
        Console.WriteLine(args[i]);
    }
}
Daniel AA Pelsmaeker
quelle
13

1) Schlechte Angewohnheit. Ernsthaft. Auch in C / C ++.

Denken Sie an das gemeinsame forMuster:

for( int i=0; i<3; i++ )
    foo(i);

Es gibt absolut keinen Grund, dort eine Ganzzahl zu verwenden. Sie werden niemals negative Werte haben. Aber fast jeder wird auf diese Weise eine einfache Schleife ausführen, selbst wenn sie (mindestens) zwei andere "Stil" -Fehler enthält.

2) intwird als nativer Maschinentyp wahrgenommen.

lornova
quelle
4

Ich bevorzuge es uint, es intsei denn, eine negative Zahl liegt tatsächlich im Bereich akzeptabler Werte. Insbesondere ist es einfach albern , einen intParameter zu akzeptieren, aber einen zu werfen, ArgumentExceptionwenn die Zahl kleiner als Null ist - verwenden Sie a uint!

Ich bin damit einverstanden, dass dies nicht uintausreichend genutzt wird, und ich ermutige alle anderen, es stärker zu nutzen.

JSB ձոգչ
quelle
7
Es ist sehr gefährlich, nur Uint zu akzeptieren und die Grenzen nicht zu überprüfen. Wenn jemand einen negativen Wert übergibt, interpretiert die CLR ihn als großes int, was bedeutet, dass Sie für -1 uint.maxvalue erhalten. Dies ist kein gewünschtes Verhalten.
Henri
19
@Henri: C # hat keine implizite Konvertierung von int nach uint, daher gibt es kein "Wenn jemand einen negativen Wert übergibt". Natürlich ist eine Grenzüberprüfung der Obergrenze immer noch angemessen (aber jetzt benötigen Sie nur noch eine Prüfung anstelle von zwei).
Ben Voigt
1

Ich programmiere auf einer untergeordneten Anwendungsebene, in der Ints selten über 100 liegen, sodass negative Werte kein Problem darstellen (z. B. für Sachen vom Typ i <myname.length ()). Es ist nur eine alte C-Gewohnheit - und kürzer zu tippen, wie oben erwähnt. In einigen Fällen ist jedoch bei der Anbindung an Hardware, bei der es sich um Ereignisflags von Geräten handelt, die Uint wichtig, wenn ein Flag möglicherweise das am weitesten links liegende (höchste) Bit verwendet.

Ehrlich gesagt, für 99,9% meiner Arbeit könnte ich leicht ushort verwenden, aber int, wissen Sie, klingt viel besser als ushort.

ddm
quelle
1

Ich habe einen Direct3D 10-Wrapper in C # erstellt und muss uint verwenden, wenn ich sehr große Vertex-Puffer erstellen möchte. Große Puffer in der Grafikkarte können nicht mit einem signierten Int dargestellt werden.

UINT ist sehr nützlich und es ist albern, etwas anderes zu sagen. Wenn jemand denkt, nur weil er nie gebraucht hat, wird es niemand anderes tun, dann liegen Sie falsch.

zezba9000
quelle
Dies ist ein guter Fall, in dem Sie die größere Reichweite nutzen können.
Leo Gurdian
0

Ich denke es ist nur Faulheit. C # ist von Natur aus eine Wahl für die Entwicklung auf Desktops und anderen Computern mit relativ vielen Ressourcen.

C und C ++ haben jedoch tiefe Wurzeln in alten Systemen und eingebetteten Systemen, in denen der Speicher knapp ist. Daher werden Programmierer verwendet, um sorgfältig zu überlegen, welcher Datentyp verwendet werden soll. C # -Programmierer sind faul und da im Allgemeinen genügend Ressourcen vorhanden sind, optimiert niemand die Speichernutzung wirklich (im Allgemeinen natürlich nicht immer). Wenn ein Byte ausreichen würde, verwenden viele C # -Programmierer, einschließlich mir, der Einfachheit halber nur int. Darüber hinaus akzeptieren viele API-Funktionen Ints, sodass das Casting verhindert wird.

Ich stimme zu, dass die Auswahl des richtigen Datentyps eine gute Praxis ist, aber ich denke, die Hauptmotivation ist Faulheit.

Schließlich ist die Auswahl einer Ganzzahl mathematisch korrekter. Vorzeichenlose Ints existieren in der Mathematik nicht (nur natürliche Zahlen). Und da die meisten Programmierer einen mathematischen Hintergrund haben, ist die Verwendung einer Ganzzahl natürlicher.

Henri
quelle
Ich würde nicht sagen, dass es Faulheit ist, obwohl Faulheit ihre Vorzüge hat. Es ist mehr als die meiste Zeit, ich kümmere mich einfach nicht genug um die int / uint-Sache, um meine Gehirnzyklen für eine solche Entscheidung zu verschwenden und einfach mit der int zu gehen. Hardware ist billig, Programmierer können teuer sein.
SWeko
Programmierer sind faul. Das ist eine schlechte Sache. Raymond würde sagen, dass Programmierer es hassen, ihre Steuern zu zahlen!
Lornova
Ich würde als erster dem Administrator mitteilen, dass wir C # -Programmierer faul sind, aber das ist nicht unbedingt eine schlechte Sache.
ChaosPandion
@ Lorenzo, ich habe in der Universität einen Artikel geschrieben, in dem es heißt, dass ein fauler Programmierer ein guter Programmierer ist. Meist ging es darum, die Programmiererzeit anstelle der Maschinenzeit zu optimieren.
Eloff
1
Hmm, die meisten Programmierer-zurechenbaren Fehler, die ich jemals gesehen (oder gemacht) habe, werden durch Faulheit verursacht ...
Lornova
0

Ich denke, ein großer Teil des Grundes ist, dass, als C zum ersten Mal herauskam, die meisten Beispiele der intKürze halber verwendet wurden. Wir freuten integeruns, nicht wie bei Fortran und Pascal schreiben zu müssen , und in jenen Tagen verwendeten wir sie routinemäßig für alltägliche Dinge wie Array-Indizes und Loop-Zähler. Ganzzahlen ohne Vorzeichen waren Sonderfälle für große Zahlen, die das letzte zusätzliche Bit benötigten. Ich denke, es ist eine natürliche Entwicklung, dass C-Gewohnheiten in C # und anderen neuen Sprachen wie Python fortgesetzt wurden.

Ed Power
quelle
0

Einige Sprachen (z. B. viele Versionen von Pascal) betrachten vorzeichenlose Typen als numerische Größen. Eine Operation zwischen einem vorzeichenlosen Typ und einem vorzeichenbehafteten Typ derselben Größe wird im Allgemeinen so ausgeführt, als ob die Operanden zum nächstgrößeren Typ heraufgestuft worden wären (in einigen solchen Sprachen hat der größte Typ kein vorzeichenloses Äquivalent, sodass eine solche Heraufstufung immer möglich ist ).

Andere Sprachen (z. B. C) betrachten vorzeichenlose N-Bit-Typen als eine Gruppe, die sich um Modulo 2 ^ N dreht. Beachten Sie, dass das Subtrahieren von N von einem Mitglied einer solchen Gruppe keine numerische Subtraktion darstellt, sondern das Gruppenmitglied ergibt, das, wenn N hinzugefügt wird, das Original ergibt. Bestimmte Operationen, die eine Mischung aus vorzeichenbehafteten und vorzeichenlosen Werten beinhalten, sind wahrscheinlich nicht wirklich sinnvoll und hätten vielleicht verboten werden sollen, aber selbst Code, der mit seinen Spezifikationen für Dinge wie numerische Literale schlampig ist, funktioniert normalerweise, und es wurde Code geschrieben, der vorzeichenbehaftete Werte mischt und vorzeichenlose Typen und obwohl sie schlampig sind, funktionieren sie, so dass sich die Spezifikation nicht so schnell ändern kann.

Es ist viel einfacher, ausschließlich mit signierten Typen zu arbeiten, als alle Feinheiten der Interaktion zwischen signierten und nicht signierten Typen zu ermitteln. Vorzeichenlose Typen sind nützlich, wenn Sie große Zahlen aus kleineren Teilen zerlegen (z. B. zur Serialisierung) oder solche Zahlen wiederherstellen möchten. Im Allgemeinen ist es jedoch besser, einfach vorzeichenbehaftete Zahlen für Dinge zu verwenden, die tatsächlich Mengen darstellen

Superkatze
quelle
0

Ich weiß, dass dies wahrscheinlich ein alter Thread ist, aber ich wollte etwas Klarheit geben.

Nehmen wir ein int8, das Sie zwischen –128 und 127 speichern können, und es verwendet 1 Byte, was insgesamt 127 positiven Zahlen entspricht.
Wenn Sie ein int8 verwenden, wird eines der Bits für die negativen Zahlen -128 verwendet.
Wenn Sie ein Uint8 verwenden, geben Sie die negativen Zahlen an die positiven an, sodass Sie 255 positive Zahlen mit der gleichen Speichermenge von 1 Byte verwenden können.
Der einzige Nachteil ist, dass Sie jetzt nicht mehr in der Lage sind, negative Werte zu verwenden.
Ein weiteres Problem dabei ist, dass nicht alle Programmiersprachen und Datenbanken dies unterstützen.
Der einzige Grund, warum Sie dies meiner Meinung nach verwenden würden, ist, wenn Sie effizient in der Gaming-Programmierung sein müssen und große, nicht negative Zahlen speichern müssen. Dies ist der Grund, warum nicht viele Programme dies verwenden.

Der Hauptgrund ist, dass Speicher kein Problem darstellt und Sie ihn nicht flexibel mit anderer Software, Plugins, Datenbanken oder APIs verwenden können. Auch zum Beispiel würde eine Bank negative Zahlen benötigen, um Geld usw. zu speichern.

Ich hoffe das wird jemandem helfen.

Pieter de Vries
quelle