Als ich die C ++ - Sprache zum ersten Mal lernte, stellte ich fest, dass neben int, float usw. kleinere oder größere Versionen dieser Datentypen in der Sprache vorhanden waren. Zum Beispiel könnte ich eine Variable x aufrufen
int x;
or
short int x;
Der Hauptunterschied besteht darin, dass short int 2 Bytes Speicher benötigt, während int 4 Bytes benötigt und short int einen geringeren Wert hat.
int x;
short int x;
unsigned short int x;
das ist noch restriktiver.
Meine Frage ist hier, ob es eine gute Praxis ist, separate Datentypen zu verwenden, je nachdem, welche Werte Ihre Variable im Programm annimmt. Ist es eine gute Idee, Variablen immer nach diesen Datentypen zu deklarieren?
c++
data-structures
Bugster
quelle
quelle
unsigned
einer Ganzzahl weniger Platz belegt wird, was natürlich falsch ist. Die Anzahl der diskreten darstellbaren Werte ist identisch (Geben oder Nehmen 1, je nachdem, wie das Vorzeichen dargestellt wird), es wird jedoch ausschließlich ins Positive verschoben.Antworten:
In den meisten Fällen sind die Platzkosten vernachlässigbar und Sie sollten sich keine Gedanken darüber machen. Sie sollten sich jedoch Gedanken über die zusätzlichen Informationen machen, die Sie durch die Angabe eines Typs geben. Zum Beispiel, wenn Sie:
Sie geben einem anderen Entwickler nützliche Informationen: Das Gehalt darf nicht negativ sein.
Der Unterschied zwischen short, int und long führt in Ihrer Anwendung selten zu Speicherplatzproblemen. Es ist wahrscheinlicher, dass Sie versehentlich die falsche Annahme treffen, dass eine Zahl immer in einen Datentyp passt. Es ist wahrscheinlich sicherer, immer int zu verwenden, es sei denn, Sie sind zu 100% sicher, dass Ihre Zahlen immer sehr klein sind. Selbst dann ist es unwahrscheinlich, dass Sie merklich Platz sparen.
quelle
unsigned
in diesem Fall ist eine schlechte Idee: Nicht nur das Gehalt kann nicht negativ sein, sondern auch die Differenz zwischen zwei Gehältern kann nicht negativ sein. (Im Allgemeinen ist die Verwendung von unsigned für alles andere als Bit-Twiddling und das Definieren des Verhaltens beim Überlauf eine schlechte Idee.)Das OP sagte nichts über die Art des Systems aus, für das sie Programme schreiben, aber ich nehme an, dass das OP an einen typischen PC mit GB Speicher gedacht hat, da C ++ erwähnt wird. Wie einer der Kommentare besagt, kann die Größe der Variablen selbst bei dieser Art von Speicher einen Unterschied ausmachen, wenn Sie mehrere Millionen Elemente eines Typs haben - beispielsweise ein Array.
Wenn Sie in die Welt der eingebetteten Systeme einsteigen - was nicht wirklich im Rahmen der Frage liegt, da das OP es nicht auf PCs beschränkt - dann ist die Größe der Datentypen sehr wichtig. Ich habe gerade ein kurzes Projekt auf einem 8-Bit-Mikrocontroller abgeschlossen, der nur 8 KByte Programmspeicher und 368 Byte RAM hat. Dort zählt natürlich jedes Byte. Man verwendet niemals eine Variable, die größer ist als sie benötigt (sowohl vom Standpunkt des Raums als auch der Codegröße - 8-Bit-Prozessoren verwenden viele Anweisungen, um 16- und 32-Bit-Daten zu bearbeiten). Warum eine CPU mit so begrenzten Ressourcen verwenden? In großen Mengen können sie nur ein Viertel kosten.
Derzeit arbeite ich an einem weiteren Embedded-Projekt mit einem 32-Bit-MIPS-basierten Mikrocontroller, der 512 KByte Flash und 128 KByte RAM enthält (und etwa 6 USD kostet). Wie bei einem PC beträgt die "natürliche" Datengröße 32 Bit. Jetzt wird es effizienter, Ints für die meisten Variablen anstelle von Zeichen oder Kurzzeichen zu verwenden. Aber auch hier muss jeder Typ von Array oder Struktur berücksichtigt werden, ob kleinere Datentypen gerechtfertigt sind. Im Gegensatz zu Compilern für größere Systeme ist es wahrscheinlicher, dass Variablen in einer Struktur in ein eingebettetes System gepackt werden. Ich achte darauf, immer zuerst alle 32-Bit-Variablen, dann 16-Bit und dann 8-Bit zu setzen, um "Löcher" zu vermeiden.
quelle
Die Antwort hängt von Ihrem System ab. Im Allgemeinen sind hier die Vor- und Nachteile der Verwendung kleinerer Typen:
Vorteile
Nachteile
Mein Rat ist, dies zu mögen:
Alternativ können Sie das
int_leastn_t
oderint_fastn_t
aus stdint.h verwenden, wobei n die Zahl 8, 16, 32 oder 64 ist. Derint_leastn_t
Typ bedeutet "Ich möchte, dass dies mindestens n Bytes sind, aber es ist mir egal, ob der Compiler es als zuweist ein größerer Typ für die Ausrichtung ".int_fastn_t
bedeutet "Ich möchte, dass dies n Byte lang ist, aber wenn mein Code dadurch schneller ausgeführt wird, sollte der Compiler einen größeren als den angegebenen Typ verwenden".Im Allgemeinen sind die verschiedenen stdint.h-Typen viel besser als normale
int
usw., da sie portabel sind. Esint
sollte nicht nur eine bestimmte Breite angegeben werden, um es tragbar zu machen. In Wirklichkeit ist es jedoch schwierig zu portieren, da Sie nie wissen, wie groß es auf einem bestimmten System sein wird.quelle
Abhängig von der Funktionsweise des jeweiligen Betriebssystems erwarten Sie im Allgemeinen, dass der Speicher nicht optimiert zugewiesen wird, sodass beim Aufrufen eines Bytes oder eines Wortes oder eines anderen kleinen Datentyps der Wert ein gesamtes Register belegt, das nur sehr stark belegt ist besitzen. Wie Ihr Compiler oder Interpreter dies interpretiert, ist jedoch etwas anderes. Wenn Sie beispielsweise ein Programm in C # kompilieren, kann der Wert ein Register für sich selbst physisch belegen, der Wert wird jedoch einer Grenzüberprüfung unterzogen, um sicherzustellen, dass Sie dies nicht tun Versuchen Sie, einen Wert zu speichern, der die Grenzen des beabsichtigten Datentyps überschreitet.
In Bezug auf die Leistung und wenn Sie solche Dinge sehr umständlich angehen, ist es wahrscheinlich schneller, einfach den Datentyp zu verwenden, der der Zielregistergröße am ehesten entspricht, aber dann verpassen Sie all den netten syntaktischen Zucker, der das Arbeiten mit Variablen so einfach macht .
Wie hilft dir das? Nun, es liegt wirklich an Ihnen, zu entscheiden, für welche Art von Situation Sie programmieren. Für fast jedes Programm, das ich jemals geschrieben habe, reicht es aus, einfach Ihrem Compiler zu vertrauen, um die Dinge zu optimieren und den Datentyp zu verwenden, der für Sie am nützlichsten ist. Wenn Sie eine hohe Genauigkeit benötigen, verwenden Sie die größeren Gleitkommadatentypen. Wenn Sie nur mit positiven Werten arbeiten, können Sie wahrscheinlich eine Ganzzahl ohne Vorzeichen verwenden. Meistens ist es jedoch ausreichend, den Datentyp int zu verwenden.
Wenn Sie jedoch sehr strenge Datenanforderungen haben, wie z. B. das Schreiben eines Kommunikationsprotokolls oder eine Art Verschlüsselungsalgorithmus, kann die Verwendung von Datentypen mit Bereichsprüfung sehr nützlich sein, insbesondere wenn Sie versuchen, Probleme im Zusammenhang mit Datenüberschreitungen / -unterläufen zu vermeiden oder ungültige Datenwerte.
Der einzige andere Grund, warum ich mir spontan vorstellen kann, bestimmte Datentypen zu verwenden, besteht darin, dass Sie versuchen, die Absicht in Ihrem Code zu kommunizieren. Wenn Sie beispielsweise eine Abkürzung verwenden, teilen Sie anderen Entwicklern mit, dass Sie positive und negative Zahlen in einem sehr kleinen Wertebereich zulassen.
quelle
Wie Scarfridge kommentierte, ist dies ein
Der Versuch , zu optimieren für die Speichernutzung könnte in anderen Bereichen der Leistung auswirken, und die goldenen Regeln der Optimierung sind:
Um zu wissen, ob es jetzt an der Zeit ist, zu optimieren, müssen Benchmarking und Tests durchgeführt werden. Sie müssen wissen, wo Ihr Code ineffizient ist, damit Sie Ihre Optimierungen gezielt durchführen können.
Um herauszufinden, ob die bestimmen optimierte Version des Codes ist sie Seite an Seite mit den gleichen Daten tatsächlich besser als die naive Implementierung zu einem bestimmten Zeitpunkt, müssen Sie Benchmark.
Denken Sie auch daran, dass eine bestimmte Implementierung, die für die aktuelle Generation von CPUs effizienter ist, nicht bedeutet, dass dies immer der Fall sein wird. Meine Antwort auf die Frage Ist die Mikrooptimierung beim Codieren wichtig? beschreibt ein Beispiel aus eigener Erfahrung, bei dem eine veraltete Optimierung zu einer Verlangsamung um eine Größenordnung führte.
Auf vielen Prozessoren sind nicht ausgerichtete Speicherzugriffe erheblich teurer als ausgerichtete Speicherzugriffe. Das Packen einiger Shorts in Ihre Struktur kann bedeuten, dass Ihr Programm jedes Mal, wenn Sie einen der beiden Werte berühren , einen Pack- / Entpack-Vorgang ausführen muss.
Aus diesem Grund ignorieren moderne Compiler Ihre Vorschläge. Wie Nikie kommentiert:
Errate deinen Compiler als Zweites auf deine Gefahr.
Es gibt einen Platz für solche Optimierungen, wenn mit Terabyte-Datensätzen oder eingebetteten Mikrocontrollern gearbeitet wird, aber für die meisten von uns ist dies kein wirkliches Problem.
quelle
Das ist falsch. Sie können nicht davon ausgehen, wie viele Bytes jeder Typ enthält, außer
char
einem Byte und mindestens 8 Bits pro Byte, wobei die Größe jedes Typs größer oder gleich der vorherigen ist.Die Performance-Vorteile sind für Stack-Variablen unglaublich gering - sie werden wahrscheinlich trotzdem ausgerichtet / aufgefüllt.
Aus diesem Grund ,
short
undlong
haben praktisch keine heutzutage verwenden, und Sie sind fast immer besser mitint
.Natürlich gibt es auch
stdint.h
welche, die vollkommen in Ordnung sind, wennint
sie nicht geschnitten werden. Wenn Sie jemals riesige Arrays von Ganzzahlen / Strukturen zuweisen,intX_t
ist dies sinnvoll, da Sie effizient sein und sich auf die Größe des Typs verlassen können. Dies ist keineswegs verfrüht, da Sie Megabyte Speicher einsparen können.quelle
long
anders seinint
. Wenn Ihr Compiler LP64int
ist, 32 Bit undlong
64 Bit, und Sie werden feststellen, dassint
s möglicherweise noch 4 Byte ausgerichtet ist (mein Compiler zum Beispiel).int64_t
int32_t
,int_fast32_t
Undlong
sind alle gute Möglichkeiten,long long
ist nur verschwenderisch undint
nicht tragbar.Dies wird von einer Art OOP- und / oder Unternehmer- / Anwendungsgesichtspunkt sein und ist möglicherweise in bestimmten Bereichen / Domänen nicht anwendbar, aber ich möchte das Konzept der primitiven Besessenheit aufgreifen .
Es ist eine gute Idee, unterschiedliche Datentypen für unterschiedliche Arten von Informationen in Ihrer Anwendung zu verwenden. Es ist jedoch wahrscheinlich NICHT ratsam, die integrierten Typen für diesen Zweck zu verwenden, es sei denn, Sie haben ernsthafte Leistungsprobleme (die gemessen und überprüft wurden usw.).
Wenn wir in unserer Anwendung Temperaturen in Kelvin modellieren möchten, KÖNNEN wir ein
ushort
oderuint
ähnliches verwenden, um zu bezeichnen, dass "der Begriff negativer Grad Kelvin absurd und ein Domänenlogikfehler ist". Die Idee dahinter ist Ton, aber Sie werden nicht den ganzen Weg gehen. Was wir erkannt haben, ist, dass wir keine negativen Werte haben können. Es ist daher praktisch, wenn wir den Compiler dazu bringen, sicherzustellen, dass niemand einer Kelvin-Temperatur einen negativen Wert zuweist. Es ist AUCH wahr, dass Sie bei Temperaturen keine bitweisen Operationen ausführen können. Und Sie können einer Temperatur (K) kein Maß für das Gewicht (kg) hinzufügen. Aber wenn Sie sowohl Temperatur als auch Masse alsuint
s modellieren , können wir genau das tun.Die Verwendung von integrierten Typen zur Modellierung unserer DOMAIN-Entitäten führt zwangsläufig zu unordentlichem Code und einigen fehlenden Prüfungen und kaputten Invarianten. Selbst wenn ein Typ EINEN Teil der Entität erfasst (kann nicht negativ sein), kann er andere zwangsläufig übersehen (kann nicht in willkürlichen arithmetischen Ausdrücken verwendet werden, kann nicht als Array von Bits behandelt werden usw.).
Die Lösung besteht darin, neue Typen zu definieren, die kapseln die die Invarianten . Auf diese Weise können Sie sicherstellen, dass Geld Geld ist und Entfernungen Entfernungen sind, und Sie können sie nicht addieren, und Sie können keine negative Entfernung erstellen, aber Sie können einen negativen Geldbetrag (oder eine Verschuldung) erstellen. Natürlich werden diese Typen die eingebauten Typen intern verwenden, aber dies ist vor Clients verborgen . In Bezug auf Ihre Frage zu Leistung / Speicherverbrauch können Sie auf diese Weise die Art und Weise ändern, wie Dinge intern gespeichert werden, ohne die Oberfläche Ihrer Funktionen zu ändern, die auf Ihren Domain-Entitäten ausgeführt werden, falls Sie feststellen, dass a
short
einfach zu verdammt ist groß.quelle
Ja natürlich. Es ist eine gute Idee zu verwenden
uint_least8_t
Wörterbücher, Arrays mit großen Konstanten, Puffer usw. zu verwenden. Es ist besser, sieuint_fast8_t
für Verarbeitungszwecke zu verwenden.uint8_least_t
(lagerung) ->uint8_fast_t
(Verarbeitung) ->uint8_least_t
(Lagerung).Zum Beispiel nehmen Sie 8-Bit-Symbole von
source
, 16-Bit-Codes vondictionaries
und einige 32-Bit-constants
. Dann verarbeiten Sie 10-15-Bit-Operationen mit ihnen und geben 8-Bit ausdestination
.Stellen wir uns vor, Sie müssen 2 Gigabyte verarbeiten
source
. Die Anzahl der Bitoperationen ist sehr groß. Sie erhalten einen hervorragenden Leistungsbonus, wenn Sie während der Verarbeitung auf schnelle Typen umsteigen. Schnelle Typen können für jede CPU-Familie unterschiedlich sein. Sie können einschließenstdint.h
und verwendenuint_fast8_t
,uint_fast16_t
,uint_fast32_t
etc.Sie könnten
uint_least8_t
stattuint8_t
für die Portabilität verwenden. Aber niemand weiß tatsächlich, welche moderne CPU diese Funktion verwenden wird. VAC-Maschine ist ein Museumsstück. Vielleicht ist es ein Overkill.quelle