In der heutigen plattformübergreifende C ++ (oder C) Welt , die wir haben :
Data model | short | int | long | long long | pointers/size_t | Sample operating systems
...
LLP64/IL32P64 16 32 32 64 64 Microsoft Windows (x86-64 and IA-64)
LP64/I32LP64 16 32 64 64 64 Most Unix and Unix-like systems, e.g. Solaris, Linux, BSD, and OS X; z/OS
...
Was dies heute bedeutet, ist, dass für jede "gemeinsame" (vorzeichenbehaftete) Ganzzahl int
ausreicht und möglicherweise beim Schreiben von C ++ - Anwendungscode immer noch als ganzzahliger Standardtyp verwendet werden kann. Aus praktischen Gründen wird es auch eine einheitliche Größe für alle Plattformen haben.
Wenn ein Anwendungsfall mindestens 64 Bit erfordert, können wir ihn heute verwenden long long
, obwohl die Verwendung eines der bitnessspezifizierenden Typen oder des __int64
Typs möglicherweise sinnvoller ist.
Dies bleibt long
in der Mitte, und wir erwägen, die Verwendung von long
Code aus unserem Anwendungscode endgültig zu verbieten .
Wäre dies sinnvoll , oder gibt es einen Grund für die Verwendung long
in modernem C ++ - (oder C-) Code, der plattformübergreifend ausgeführt werden muss? (Plattform ist Desktop, mobile Geräte, aber keine Dinge wie Mikrocontroller, DSPs usw.)
Möglicherweise interessante Hintergrundlinks:
- Wie lautet der C ++ - Standard, dessen Größe int, long sein soll?
- Warum hat sich das Win64-Team für das LLP64-Modell entschieden?
- 64-Bit-Programmiermodelle: Warum LP64? (etwas gealtert)
- Ist
long
garantiert mindestens 32 Bits sein? (Hiermit wird die Kommentardiskussion unten angesprochen. Antwort .)
quelle
long
Nur so können 32 Bit garantiert werden.int
kann 16 Bit sein, für einige Anwendungen ist dies nicht ausreichend. Ja,int
ist manchmal 16 Bit auf modernen Compilern. Ja, Menschen schreiben Software auf Mikrocontrollern. Ich würde behaupten, dass mehr Leute Software schreiben, die mehr Benutzer auf Mikrocontrollern als auf PCs hat, mit dem Aufstieg von iPhone- und Android-Geräten, ganz zu schweigen vom Aufstieg von Arduinos usw.int
es immer noch 16 Bit gibt. Ich hasse es, es zu sagen, aber wenn Sie über die "heutige plattformübergreifende Welt" schreiben, können Sie den gesamten indischen Subkontinent nicht ignorieren.Antworten:
Der einzige Grund, den ich heute verwenden würde,
long
ist das Aufrufen oder Implementieren einer externen Schnittstelle, die diese verwendet.Wie Sie in Ihrem Posting sagen, weisen short und int heute auf allen wichtigen Desktop- / Server- / Mobilplattformen einigermaßen stabile Eigenschaften auf, und ich sehe keinen Grund, dass sich dies in absehbarer Zukunft ändern wird. Ich sehe daher wenig Grund, sie im Allgemeinen zu meiden.
long
auf der anderen Seite ist ein Durcheinander. Mir ist bekannt, dass alle 32-Bit-Systeme die folgenden Eigenschaften aufweisen.Auf der Grundlage eines oder mehrerer dieser Merkmale wurden große Codemengen geschrieben. Mit der Umstellung auf 64-Bit konnten jedoch nicht alle beibehalten werden. Unix-ähnliche Plattformen entschieden sich für LP64, das die Merkmale 2 und 3 zu Lasten von Merkmal 1 beibehielt. Win64 entschied sich für LLP64, das die Merkmale 1 zu Lasten von Merkmal 2 und 3 behielt. Sie können sich also nicht mehr auf eines dieser Merkmale verlassen und das IMO lässt wenig Grund zu verwenden
long
.Wenn Sie einen Typ möchten, der genau 32 Bit groß ist, sollten Sie verwenden
int32_t
.Wenn Sie einen Typ möchten, der die gleiche Größe wie ein Zeiger hat, sollten Sie
intptr_t
(oder besseruintptr_t
) verwenden.Wenn Sie einen Typ wünschen, der das größte Element ist, an dem in einem einzelnen Register / Befehl gearbeitet werden kann, dann glaube ich leider nicht, dass der Standard einen vorsieht.
size_t
sollte auf den meisten gängigen Plattformen stimmen, aber nicht auf x32 .PS
Ich würde mich nicht mit den Typen "schnell" oder "am wenigsten" beschäftigen. Die "kleinsten" Typen sind nur dann von Bedeutung, wenn Sie Portabilität bevorzugen, um Architekturen wirklich zu verschleiern
CHAR_BIT != 8
. Die Größe der "schnellen" Typen in der Praxis scheint ziemlich willkürlich zu sein. Linux scheint sie mindestens so groß wie Pointer zu machen, was auf 64-Bit-Plattformen mit schneller 32-Bit-Unterstützung wie x86-64 und arm64 albern ist. IIRC iOS macht sie so klein wie möglich. Ich bin nicht sicher, was andere Systeme tun.PPS
Ein Grund für die Verwendung
unsigned long
(aber nicht einfachlong
) ist, dass ein Modulo-Verhalten garantiert ist. Leider haben aufgrund der verkorksten Promotion-Regeln von C unsignierte Typen, die kleinerint
sind, kein Modulo-Verhalten.Heutzutage ist auf allen wichtigen Plattformen
uint32_t
die gleiche Größe oder größer als int und weist daher ein Modulo-Verhalten auf. Es gab jedoch in der Vergangenheit und es könnte theoretisch in Zukunft Plattformen geben, auf denenint
64-Bit verfügbar ist und daheruint32_t
kein Modulo-Verhalten vorliegt.Persönlich würde ich sagen, dass es besser ist, das Modulo-Verhalten durch die Verwendung von "1u *" oder "0u +" am Anfang Ihrer Gleichungen zu erzwingen, da dies für jede Größe von vorzeichenlosem Typ funktioniert.
quelle
Wie Sie in Ihrer Frage erwähnen, dreht sich bei moderner Software alles um die Interaktion zwischen Plattformen und Systemen im Internet. Die C- und C ++ - Standards geben Bereiche für ganzzahlige Schriftgrößen an, nicht für bestimmte Größen (im Gegensatz zu Sprachen wie Java und C #).
Um sicherzustellen, dass Ihre auf verschiedenen Plattformen kompilierte Software auf dieselbe Weise mit denselben Daten arbeitet und dass andere Software mit Ihrer Software in derselben Größe interagieren kann, sollten Sie Ganzzahlen mit fester Größe verwenden.
Geben Sie ein,
<cstdint>
welches genau das bereitstellt und ein Standardheader ist, den alle Compiler- und Standardbibliotheksplattformen bereitstellen müssen. Hinweis: Dieser Header wurde erst ab C ++ 11 benötigt, wurde jedoch von vielen älteren Bibliotheksimplementierungen trotzdem bereitgestellt.Willst du eine 64-Bit-Ganzzahl ohne Vorzeichen? Verwenden Sie
uint64_t
. Vorzeichenbehaftete 32-Bit-Ganzzahl? Verwenden Sieint32_t
. Während die Typen im Header optional sind, sollten moderne Plattformen alle in diesem Header definierten Typen unterstützen.Manchmal wird eine bestimmte Bitbreite benötigt, beispielsweise in einer Datenstruktur, die für die Kommunikation mit anderen Systemen verwendet wird. Ein anderes Mal ist es nicht. Stellt für weniger strenge Situationen
<cstdint>
Typen mit einer Mindestbreite bereit.Es gibt die wenigsten Varianten:
int_leastXX_t
Wird ein ganzzahliger Typ mit mindestens XX Bits sein. Es wird der kleinste Typ verwendet, der XX-Bits bereitstellt, der Typ darf jedoch größer als die angegebene Anzahl von Bits sein. In der Praxis sind dies typischerweise dieselben wie die oben beschriebenen Typen, die die genaue Anzahl von Bits angeben.Es gibt auch schnelle Varianten:
int_fastXX_t
mindestens XX Bits, sollte aber einen Typ verwenden, der auf einer bestimmten Plattform schnell arbeitet. Die Definition von "schnell" in diesem Zusammenhang ist nicht spezifiziert. In der Praxis bedeutet dies jedoch typischerweise, dass ein Typ, der kleiner als die Registergröße einer CPU ist, ein Alias für einen Typ der Registergröße der CPU sein kann. Beispielsweise gibt der Header von Visual C ++ 2015 an, dassint_fast16_t
es sich um eine 32-Bit-Ganzzahl handelt, da die 32-Bit-Arithmetik auf x86 insgesamt schneller ist als die 16-Bit-Arithmetik.Dies ist alles wichtig, da Sie Typen verwenden sollten, die die Ergebnisse von Berechnungen enthalten können, die Ihr Programm unabhängig von der Plattform ausführt. Wenn ein Programm auf einer Plattform aufgrund von Unterschieden beim Ganzzahlüberlauf korrekte Ergebnisse, auf einer anderen jedoch falsche Ergebnisse liefert, ist dies schlecht. Wenn Sie die Standard-Integer-Typen verwenden, stellen Sie sicher, dass die Ergebnisse auf verschiedenen Plattformen hinsichtlich der Größe der verwendeten Ganzzahlen gleich sind (natürlich kann es neben der Integer-Breite auch andere Unterschiede zwischen den Plattformen geben).
Also ja,
long
sollte aus modernem C ++ Code verbannt werden. So sollint
,short
undlong long
.quelle
std
Namespace einzufügen, wenn#include
d in einer C ++ - Kompilierungseinheit verwendet wird. In der Dokumentation, die ich verlinkt habe, wird dies jedoch nicht erwähnt, und es scheint Visual Studio egal zu sein, wie ich darauf zugreife.int
kann ... übertrieben sein? (Ich würde es in Betracht ziehen, wenn der Code extrem portabel auf allen undurchsichtigen (und nicht so undurchsichtigen) Plattformen sein muss. Ein Verbot für "App-Code" passt möglicherweise nicht sehr gut zu unseren Entwicklern.#include <cstdint>
ist erforderlich , die Arten in setzenstd::
und (leider) optional erlaubt auch sie in dem globalen Namensraum zu setzen.#include <stdint.h>
ist genau das Gegenteil. Gleiches gilt für jedes andere Paar von C-Headern. Siehe: stackoverflow.com/a/13643019/2757035. Ich wünschte, der Standard hätte verlangt, dass sich jeder nur auf den jeweils erforderlichen Namespace auswirkt, anstatt sich an die schlechten Konventionen zu knicken , die von einigen Implementierungen festgelegt wurden.Nein, es wäre absurd, die eingebauten Integer-Typen zu verbieten. Sie sollten jedoch auch nicht missbraucht werden.
Wenn Sie eine Ganzzahl benötigen, die genau N Bits breit ist, verwenden Sie (oder wenn Sie eine Version benötigen ). Es ist einfach falsch, sich eine 32-Bit-Ganzzahl und eine 64-Bit-Ganzzahl vorzustellen. Auf Ihren aktuellen Plattformen ist dies möglicherweise der Fall, dies setzt jedoch implementierungsspezifisches Verhalten voraus.
std::intN_t
std::uintN_t
unsigned
int
long long
Die Verwendung von Integer-Typen mit fester Breite ist auch für die Interaktion mit anderen Technologien nützlich. Wenn beispielsweise einige Teile Ihrer Anwendung in Java und andere in C ++ geschrieben sind, möchten Sie wahrscheinlich die Ganzzahltypen abgleichen, um konsistente Ergebnisse zu erzielen. (Beachten Sie jedoch, dass der Überlauf in Java eine genau definierte Semantik aufweist, während der
signed
Überlauf in C ++ undefiniertes Verhalten ist, sodass die Konsistenz ein hohes Ziel darstellt.) Sie sind auch beim Datenaustausch zwischen verschiedenen Computerhosts von unschätzbarem Wert.Wenn Sie nicht genau N Bits benötigen , sondern nur einen Typ, der breit genug ist , sollten Sie die Verwendung von (platzoptimiert) oder (geschwindigkeitsoptimiert) in Betracht ziehen . Auch hier haben beide Familien Kollegen.
std::int_leastN_t
std::int_fastN_t
unsigned
Wann sollten die eingebauten Typen verwendet werden? Nun, da der Standard ihre Breite nicht genau spezifiziert, verwenden Sie sie, wenn Sie sich nicht um die tatsächliche Bitbreite, sondern um andere Eigenschaften kümmern.
A
char
ist die kleinste Ganzzahl, die von der Hardware adressiert werden kann. Die Sprache zwingt Sie tatsächlich, sie zum Aliasing von beliebigem Speicher zu verwenden. Es ist auch der einzige realisierbare Typ für die Darstellung von (schmalen) Zeichenfolgen.Ein
int
Typ ist normalerweise der schnellste Typ, den die Maschine verarbeiten kann. Es ist breit genug, damit es mit einem einzigen Befehl geladen und gespeichert werden kann (ohne dass Bits maskiert oder verschoben werden müssen) und schmal genug, damit es mit (den effizientesten) Hardwareanweisungen bearbeitet werden kann. Daherint
ist es die perfekte Wahl für die Übergabe von Daten und das Rechnen, wenn ein Überlauf kein Problem darstellt. Der zugrunde liegende Standardtyp für Aufzählungen ist beispielsweiseint
. Ändern Sie es nicht in eine 32-Bit-Ganzzahl, nur weil Sie können. Wenn Sie einen Wert haben, der nur -1, 0 und 1 sein kann, ist einint
ist eine perfekte Wahl, es sei denn, Sie speichern riesige Arrays von ihnen. In diesem Fall möchten Sie möglicherweise einen kompakteren Datentyp verwenden, wenn Sie für den Zugriff auf einzelne Elemente einen höheren Preis zahlen müssen. Eine effizientere Zwischenspeicherung wird sich wahrscheinlich auszahlen. Viele Betriebssystemfunktionen sind auch in Bezug auf definiertint
. Es wäre dumm, ihre Argumente und Ergebnisse hin und her zu konvertieren. All dies möglicherweise tun könnten , ist einführen Überlauffehler.long
wird normalerweise der breiteste Typ sein, der mit Einzelmaschinenanweisungen gehandhabt werden kann. Dies ist besondersunsigned long
attraktiv für den Umgang mit Rohdaten und allem, was mit Bitmanipulation zu tun hat. Zum Beispiel würde ich erwarten,unsigned long
in der Implementierung eines Bitvektors zu sehen . Wenn der Code sorgfältig geschrieben wird, spielt es keine Rolle, wie breit der Typ tatsächlich ist (da sich der Code automatisch anpasst). Auf Plattformen, auf denen das native Maschinenwort 32-Bit ist, ist das Backing-Array des Bitvektors ein Array vonunsigned
32-Bit-Ganzzahlen sind am wünschenswertesten, da es dumm wäre, einen 64-Bit-Typ zu verwenden, der über teure Anweisungen geladen werden muss, um die nicht benötigten Bits ohnehin wieder zu verschieben und zu maskieren. Wenn die native Wortgröße der Plattform hingegen 64 Bit beträgt, möchte ich ein Array dieses Typs, da Vorgänge wie "find first set" möglicherweise doppelt so schnell ausgeführt werden. Das „Problem“ deslong
Datentyps, den Sie beschreiben, dass seine Größe von Plattform zu Plattform variiert, ist also ein Feature , das man gut nutzen kann. Es wird nur dann zum Problem, wenn Sie sich die eingebauten Typen als Typen mit einer bestimmten Bitbreite vorstellen, die sie einfach nicht sind.char
,int
Undlong
sind sehr nützlich Typen wie oben beschrieben.short
undlong long
sind bei weitem nicht so nützlich, weil ihre Semantik viel weniger klar ist.quelle
long
zwischen Windows und Unix hin. Ich mag missverstehen, aber Ihre Beschreibung des Unterschieds in der Größelong
, ein "Feature" statt eines "Problems" zu sein, macht für mich Sinn, wenn ich 32- und 64-Bit-Datenmodelle vergleiche, aber nicht für diesen speziellen Vergleich. In dem speziellen Fall, in dem diese Frage gestellt wurde, ist dies wirklich ein Merkmal? Oder ist es ein Merkmal in anderen Situationen (das heißt im Allgemeinen) und in diesem Fall harmlos?uint32_t
wie durchgeführt werden Werte unterzeichnet ,int
auf einer Plattform -width Arithmetik , woint
ist breiter alsuint32_t
. (Mit heutigen ABIs ist dies mit überwältigender Wahrscheinlichkeit ein Problem füruint16_t
.)long
Wird normalerweise der breiteste Typ sein, der mit Einzelmaschinenanweisungen verarbeitet werden kann. ..." - und das ist genau falsch . Schauen Sie sich das Windows-Datenmodell an. IMHO, dein ganzes folgendes Beispiel bricht zusammen, weil auf x64 Windows noch 32 bit lang ist.In einer anderen Antwort wird bereits auf die CSTDINT-Typen und weniger bekannte Variationen eingegangen.
Ich möchte noch hinzufügen:
Verwenden Sie domänenspezifische Typnamen
Das heißt, nicht erklären , Ihre Parameter und Variablen zu sein
uint32_t
(schon gar nichtlong
!), Aber Namen wiechannel_id_type
,room_count_type
usw.über Bibliotheken
Bibliotheken von Drittanbietern, die
long
oder was auch immer verwenden, können ärgerlich sein, insbesondere wenn sie als Verweise oder Zeiger auf diese verwendet werden.Das Beste ist, Wrapper zu machen.
Meine Strategie besteht im Allgemeinen darin, eine Reihe von cast-ähnlichen Funktionen zu erstellen, die verwendet werden. Sie sind überladen, um nur die Typen zu akzeptieren, die genau den entsprechenden Typen entsprechen, zusammen mit den von Ihnen benötigten Zeigervariationen usw. Sie werden spezifisch für das Betriebssystem / den Compiler / die Einstellungen definiert. Auf diese Weise können Sie Warnungen entfernen und dennoch sicherstellen, dass nur die "richtigen" Konvertierungen verwendet werden.
Insbesondere bei verschiedenen primitiven Typen, die 32 Bit erzeugen, stimmt Ihre Auswahl der
int32_t
Definition möglicherweise nicht mit dem Bibliotheksaufruf überein (z. B. int vs long unter Windows).Die cast-ähnliche Funktion dokumentiert den Konflikt, ermöglicht die Überprüfung des mit den Funktionsparametern übereinstimmenden Ergebnisses während der Kompilierung und beseitigt Warnungen oder Fehler nur dann, wenn der tatsächliche Typ mit der tatsächlichen Größe übereinstimmt. Das heißt, es ist überladen und definiert, wenn ich (unter Windows) ein
int*
oder a übergebelong*
und andernfalls einen Fehler bei der Kompilierung gebe.Wenn also die Bibliothek aktualisiert wird oder jemand ändert, was
channel_id_type
ist, wird dies weiterhin überprüft.quelle