Warum sind die Typen immer eine bestimmte Größe haben, unabhängig von Ihrem Wert?
Implementierungen unterscheiden, die die tatsächliche Größe der Arten, aber auf den meisten, Typen wie unsigned int und float immer 4 Byte. Aber warum hat der Typ immer besetzt bestimmte Menge an Speicher, egal, der Wert? Wenn ich zum Beispiel erstellt der folgende ganze Zahl mit dem Wert 255
int myInt = 255;
Dann myInt
belegen würde 4 bytes mit meinem compiler. Allerdings ist der tatsächliche Wert, 255
dargestellt werden kann, mit nur 1 byte, also warum sollte myInt
nicht nur belegen 1 byte an Speicher? Oder die allgemeinere Art zu Fragen: Warum macht ein Typ nur eine Größe zugeordnet, wenn der Platz benötigt, um den Wert möglicherweise kleiner als die Größe?
- 1) "Jedoch den tatsächlichen Wert, 256 dargestellt werden kann, mit nur 1 byte" Falsch, der größte
unsinged
Wert, der dargestellt werden kann mit 1-byte255
. 2) Betrachten Sie den overhead der Berechnung der optimalen Speichergröße und verkleinern/erweitern der Speicher-Bereich, der eine variable, so verändert sich der Wert. - Gut, wenn die Zeit kommt, Lesen Sie den Wert aus dem Speicher, wie Sie vorschlagen, die Maschine bestimmt, wie viele bytes zu Lesen? Wie wird die Maschine wissen, wo Sie aufhören zu Lesen das Wert? Dies erfordert zusätzliche Einrichtungen. Und im Allgemeinen Fall wird die Speicher-und performance-overhead für diese zusätzlichen Einrichtungen wird viel höher sein, als im Falle der einfach mit festen 4 bytes für
unsigned int
Wert. - Warum macht ein Typ nur eine Größe zugeordnet, wenn der Platz benötigt, um den Wert möglicherweise kleiner als die Größe? Weil es vielleicht nicht immer kleiner werden.
- Ich mag diese Frage. Obwohl es scheinen mag einfach zu beantworten, ich denke, dass die eine präzise Erklärung erfordert eine gute understandning, wie computer und computer-Architekturen, die wirklich funktionieren. Die meisten Leute werden wahrscheinlich nur nehmen es für selbstverständlich, ohne dass Sie eine umfassende Erklärung für Sie.
- FYI - Auf Ubuntu 17.10, sizeof (std::string) Berichte 32 bytes automatische Speicher, unabhängig davon, wie viele chars sind es. (Alle Daten, die chars sind in dynamischer Speicher!!!) Aber dies ist eine Implementierung detail. Ähnliche Angaben existieren für std::vector und viele andere Behälter.
- 1) Ahh ja, ich meinte, dass 1 byte kann 256 verschiedene Werte darstellen. Lassen Sie mich Bearbeiten Sie die Frage, um genauer zu sein 2) ich sehe, Sie könnten aber auch etwas Speicher sparen, so die Nachteile und Vorteile von dynamischer Größe, können gleichwertig sein, die vor-und Nachteile von statischen Größen. Also, die Art der Lagerung abhängig wäre von der situation in denen der eine wichtiger als der andere.
- 1) die Lagerung ist nur die eine Seite der Gleichung. Berechnung der Geschwindigkeit ist eine andere. In einem typischen Fall -, Rechen-Geschwindigkeit wichtiger ist als die Speicherkapazität. Also, warum sollte man dafür bezahlen, was er nicht braucht? 2) Die Arten der
char
,short
usw. existieren aus einem Grund: wenn Sie wissen, dass die zahlen, die Sie arbeiten auf klein-genug zahlen, können Sie mit kleineren Datentyp. 3) Lesen Sie weitere Kommentare/Antworten. In einem typischen Fall: Es ist einfach nicht der Mühe Wert. - Betrachten Sie, was passieren, wenn Sie 1, um den Wert der Variablen, so dass es 256, also würde es brauchen, um zu erweitern. Wo kommt es zu erweitern? Bewegen Sie den rest der Erinnerung Platz zu machen? Nicht die variable selbst bewegen? Wenn ja, wo bewegt es sich und wie finden Sie die Hinweise, die Sie brauchen, um zu aktualisieren?
- Typen, die im Allgemeinen nicht Konstante Größe. int, float, etc. haben. viele andere haben Konstante Größe in c++, im Gegensatz zu einigen anderen Sprachen, aus performance-Gründen. Andere Arten haben variable Größe, auch in c++, weil Sie es brauchen, z.B.: std::vector
- Nein, Sie sind falsch.
std::vector<X>
hat immer die gleiche Größe, d.h.sizeof(std::vector<X>)
ist eine Compilezeit-Konstante. - Ich vermisse die Erklärung: es ist gespeichert als 4 bytes, weil "int" hat den expliziten Auftrag, dies zu tun.
- Protokoll-Puffer ist Varints sind ein Beispiel einer Implementierung eines variable-Länge-Menge, wo "Kleinere zahlen nehmen mit einer kleineren Anzahl von bytes.", wie du es beschreibst.
- Wenn Sie kaufen eine acht-stelliger Rechner, es wird ein drei-stelliger Rechner, wenn Sie geben Sie den Wert 255? Ich bezweifle es.
- Ich bin nicht einverstanden. Offensichtlich
sizeof(std::vector<X>)
ist eine compile-Zeit-Konstante, aber nur, weilsizeof
ist nicht genau zu sagen, Sie die Menge des Speichers, die der Typ belegt. Das ist mehr wiesizeof(vec) + vec.capacity()*(sizeof(vec.front())) + vec.capacity() ? dynamic_memory_overhead : 0
- Sie sind frei, anderer Meinung als Sie, aber in C++ Begriffe, die Größe des Typs ist Wert, der zurückgegeben wird, durch
sizeof
Betreiber. Es ist eine definition von Standard. - 255 und die Sie verwenden möchten 2 nibbles für Sie. OK, das kann ich sehen. Wie viele Sie verwenden möchten, für 9? Wie viele für null?
- Daten-Typen und Ihre Zuordnung zu Speicher ist sehr relevant zu Programmieren; es ist kaum ein Thema der "Allgemeine Computer-hardware und-software." Das schließen Grund ist, für die Menschen zu Fragen, wie die Arbeit Ihre Tabellenkalkulation, zum Beispiel. Die Abstimmung erneut zu öffnen.
- Ich Stimme mit @WayneConrad - ich sehe nicht, wie die engen Grund gilt auch hier. Es scheint wie eine ganz angemessene Frage für mich.
- Nur um zu unterdrücken jede mögliche streiten... beide SergeyA und Martin Bonner korrekt sind.
std:vector<T>
kapselt eine dynamisch zugewiesene Arrays, wie zum Beispiel erzeugt durchnew T[N]
, in der Regel durch die Speicherung ein handle auf das besagte array.std::vector
's Größe ist somit konstant, und genau gemessensizeof
. Jedoch, da die eigentliche Datenspeicherung verwaltetvector
gar nicht in dievector
selbst, es wird nicht reflektiert, indem das Ergebnissizeof
. - Selbst wenn man die Daten speichern in 8-bits, die auf den meisten Systemen, Sie müssen nicht die option zum Lesen von Daten auf 8 bits zu einer Zeit, als Prozessoren haben in der Regel eine Feste Breite Datenbus (z.B. 32 bit). Sie werden am Ende Lesung 32 bits aus dem Speicher und einfach "ignorieren" 24-bits mit Ihrer Regelung, dass die ganze "Optimierung" sinnlos.
Du musst angemeldet sein, um einen Kommentar abzugeben.
Der compiler soll zu produzieren assembler (und letztlich auch der Maschinen-code) für eine Maschine, und in der Regel C++ versucht, sein Verständnis für die Maschine.
Sein Verständnis für die zugrunde liegenden Maschine bedeutet in etwa: Sie machen es einfach zu schreiben C++ - code, die Karte effizient auf die Operationen, die der Rechner ausführen kann, schnell. So, wir wollen geben Ihnen Zugang zu den Datentypen und Operationen, die schnell sind und "natürlich" auf unserer hardware-Plattform.
Konkret, betrachten Sie einen bestimmten Computer Architektur. Nehmen wir die aktuellen Intel-x86-Familie.
Die Intel® 64 und IA-32 Architectures Software Developer ' s Manual vol 1 (link), Abschnitt 3.4.1, sagt:
So, wir wollen die compiler benutzen diese EAX, EBX, etc. Register, wenn es kompiliert einfach C++ integer-Arithmetik. Dies bedeutet, dass, wenn ich erklären
int
es sollte etwas sein, das kompatibel mit diesen Registern, so dass ich Sie verwenden kann, effizient.Die Register sind immer gleich groß (hier 32 bit), so dass meine
int
Variablen werden immer 32-bit-als auch. Ich werde mit dem gleichen layout (little-endian), so dass ich nicht zu tun haben, eine Konvertierung jedes mal, wenn ich laden Sie eine variable mit dem Wert in ein register oder Speicher, ein register zurück, in eine variable.Mit godbolt wir genau sehen können, was der compiler tut, für einige trivial-code:
kompiliert (mit GCC 8.1 und
-fomit-frame-pointer -O3
für Einfachheit) zu:bedeutet dies:
int num
parameter übergeben wurde, im register "EDI", was bedeutet, es ist genau die Größe und das layout von Intel erwarten, die im einheitlichen register. Die Funktion muss nicht konvertieren etwasimul
), die sehr schnellEdit: wir können hinzufügen, einen relevanten Vergleich, um den Unterschied zu zeigen mit einem nicht-native-layout macht. Der einfachste Fall ist die Speicherung von Werten, die in etwas anderem als der nativen Breite.
Mit godbolt wieder, die wir vergleichen können eine einfache native Multiplikation
mit dem entsprechenden code für eine nicht-standard-Breite
Alle extra-Anweisungen beschäftigen sich mit der Umwandlung der input-format (zwei 31-bit unsigned Integer) in das format der Prozessor intern behandeln können. Wenn wir wollen, speichern Sie das Ergebnis zurück in einen 31-bit-Wert, es würde noch ein oder zwei Anweisungen, dies zu tun.
Diese zusätzliche Komplexität bedeutet, Sie würde nur diese Mühe machen, wenn der Raum zu sparen ist sehr wichtig. In diesem Fall haben wir nur das speichern von zwei bits im Vergleich zu der nativen
unsigned
oderuint32_t
Art, die entstanden wären viel einfacher-code.Ein Hinweis auf dynamische Größen:
Beispiel oben ist immer noch Feste Werte für die Breite, anstatt mit variabler Breite, aber die Breite (und Ausrichtung) nicht mehr mit der nativen registriert.
Die x86-Plattform hat mehrere einheimische Größen, einschließlich 8-bit und 16-bit-zusätzlich zu den Haupt-32-bit (ich bin gern auf 64-bit-Modus und verschiedene andere Dinge, für die Einfachheit).
Diese Typen (char, int8_t, uint8_t, int16_t, etc.) sind auch direkt unterstützt durch die Architektur, die teilweise für die Abwärtskompatibilität mit älteren 8086/286/386/etc. etc. instruction sets.
Es ist sicherlich der Fall, dass die Wahl der kleinsten Natürliche Feste Größe Typ, der ausreichend, kann eine gute Praxis - Sie sind immer noch schnell, single Anweisungen lädt und speichert, erhalten Sie noch full-speed-native-Arithmetik, und Sie können sogar verbessern die Leistung durch die Reduzierung von cache misses.
Dies ist sehr unterschiedlich zu variable-Länge-Kodierung - ich habe mit einigen von diesen, und Sie sind schrecklich. Jede Belastung wird zu einer Schleife statt einer einzelnen Anweisung. Jeder Shop ist auch eine Schleife. Jede Struktur der variable-Länge, so dass Sie nicht verwenden können, arrays natürlich.
Ein weiterer Hinweis auf die Effizienz
In nachfolgenden Kommentare, Sie habe mit dem Wort "effizient" ist, soweit ich das sagen kann, mit Bezug auf Speichergröße. Wir haben manchmal wählen, zu minimieren Speichergröße - es kann wichtig sein, wenn wir sparen sehr große Anzahl von Werten in Dateien, oder senden Sie Sie über ein Netzwerk. Die trade-off ist, dass wir laden müssen diese Werte in Registern zu tun nichts mit Ihnen, und die Durchführung der Umwandlung ist nicht frei.
Wenn wir über Effizienz, müssen wir wissen, was wir optimieren, und was die trade-offs sind. Mit non-native storage-Typen ist ein Weg, um den Handel Verarbeitungsgeschwindigkeit für Raum, und das macht manchmal Sinn. Mit variabler Länge Speicherplatz (für arithmetische Typen zumindest), trades mehr Verarbeitungsgeschwindigkeit (und code-Komplexität und Entwicklungszeit) für ein oft minimaler weitere Platzersparnis.
Die Geschwindigkeit Strafe zahlen Sie für diese bedeutet, es ist nur sinnvoll, wenn Sie brauchen, um absolut minimieren, Bandbreite oder langfristige Lagerung, und für diese Fälle gibt es in der Regel einfacher zu verwenden, eine einfache und Natürliche format - und dann einfach komprimieren Sie es mit einem general-purpose-system (wie z.B. zip -, gzip -, bzip2 -, xy-oder was auch immer).
tl;dr
Jede Plattform hat eine Architektur, aber Sie können sich mit einer im wesentlichen unbegrenzten Anzahl von verschiedenen Möglichkeiten, um Daten darzustellen. Ist es nicht sinnvoll, für jede Sprache, um eine unbegrenzte Anzahl von built-in-Datentypen. Also, C++ impliziten Zugriff auf die Plattform von einheimischen, natürlichen Satz von Datentypen, und Sie können code jede andere (nicht-native) Darstellung selbst.
array[7]
, weil ich nicht so berechnen Sie den offset, ohne dass die gespeicherten Werte inarray[0]..array[6]
).git
's Metadaten) oder Sie sind tatsächlich halten es im Speicher eine gelegentlich müssen zufällig Zugriff auf oder ändern Sie ein paar, aber nicht die meisten Werte (wie in HTML - +CSS-rendering-engines), und damit nur shunken mit so etwas wie VLQ in-place.Weil Arten grundsätzlich dar-Speicher, und Sie sind definiert in Bezug auf maximale Wert, den Sie halten kann, nicht den aktuellen Wert.
Die sehr einfache Analogie wäre ein Haus - ein Haus, das hat eine Feste Größe, unabhängig davon, wie viele Menschen dort Leben, und es ist auch ein Gebäude-code legt die maximale Anzahl von Menschen, die Leben in einem Haus in einer bestimmten Größe.
Jedoch, auch wenn eine einzelne person in einem Haus zu Leben, die Platz für 10, die Größe des Hauses ist nicht betroffen von der aktuellen Anzahl der Bewohner.
Es ist eine Optimierung und Vereinfachung.
Können Sie entweder Feste Größe der Objekte. So speichern Sie den Wert.
Oder Sie können mit variabler Größe objets. Aber die Speicherung von Wert und Größe.
Feste Größe von Objekten
Den code, der manipuliert die Nummer nicht brauchen, um sorgen über die Größe. Sie vermuten, dass Sie immer 4 bytes und machen den code sehr einfach.
Dynamische Größe von Objekten
Code manipuliert-Nummer muss verstehen, beim Lesen einer variable, die es Lesen müssen, den Wert und die Größe. Verwenden Sie die Größe, um sicherzustellen, dass alle high bits sind null in das register.
Wenn der Wert wieder in Erinnerung, wenn der Wert nicht überschritten wird, wird die aktuelle Größe dann einfach den Wert wieder in Erinnerung. Aber wenn der Wert geschrumpft oder gewachsen, müssen Sie den Speicherort für das Objekt an einen anderen Speicherort im Arbeitsspeicher, um sicherzustellen, dass Sie nicht überlaufen. Jetzt haben Sie, um zu verfolgen die position dieser Zahl (wie Sie sich bewegen kann, wenn es wächst zu groß für seine Größe). Sie auch brauchen, um zu verfolgen alle unbenutzten Variablen Standorten, so dass Sie potenziell wiederverwendet werden.
Zusammenfassung
Den generierten code für die Feste Größe der Objekte ist viel einfacher.
Hinweis
Kompression nutzt die Tatsache, dass 255 passen in ein byte. Es gibt Kompressionsverfahren für die Speicherung von großen Daten-sets, die aktiv die Nutzung unterschiedlicher Größe-Werte für verschiedene zahlen. Aber da dieses nicht ist, live-Daten, haben Sie nicht die Komplexität wie oben beschrieben. Sie verwenden weniger Speicherplatz zum speichern der Daten auf Kosten der Kompression/de-komprimieren der Daten zur Speicherung.
int
speichert die Anzahl der Elemente im array. Dassint
selbst eine Feste Größe wieder.Weil in einer Sprache wie C++, ein design-Ziel ist, dass einfache Operationen kompilieren auf einfache Maschinenbefehle.
Alle mainstream-CPU-Befehlssätzen arbeiten mit fester Breite Arten, und wenn Sie wollen, zu tun variabler Breite Arten Sie tun müssen, mehrere Maschinenbefehle, um diese zu behandeln.
Als für warum die zugrunde liegenden computer-hardware ist das auch so: Es ist, weil es einfacher und effizienter für viele Fällen (aber nicht alle).
Sich vorstellen, den computer als ein Stück Klebeband:
Wenn Sie einfach sagen, den computer zu schauen, das erste byte auf das Band
xx
, wie funktioniert es wissen, ob oder nicht der Typ hält dort, oder fährt weiter zum nächsten byte? Wenn Sie eine Zahl wie255
(hexadezimalFF
) oder eine Zahl wie65535
(hexadezimalFFFF
) das erste byte ist immerFF
.So, wie wissen Sie? Sie haben, um zusätzliche Logik, und "überlast" die Bedeutung von mindestens einem bit-oder byte-Wert, um anzugeben, dass der Wert weiter auf das nächste byte. Diese Logik ist nie "frei", entweder emulieren Sie es in software oder das hinzufügen von ein paar zusätzliche transistoren auf der CPU, um es zu tun.
Die fixed-width Datentypen von Programmiersprachen wie C und C++ widerspiegeln.
Es nicht haben so sein, und mehr abstrakte Sprachen, die sich weniger um die Zuordnung zu maximal effizienten code zur freien Verwendung variabler Breite Codierungen (auch bekannt als "Variable Length Quantities" oder VLQ) für numerische Datentypen.
Weiter Lesen: Wenn Sie nach "variabler Länge Menge" finden Sie einige Beispiele, wo diese Art der Codierung ist tatsächlich effizient und lohnt sich die zusätzliche Logik. Es ist in der Regel, wenn Sie brauchen, um zu speichern eine große Menge von Werten, die könnte überall sein, in einem großen Bereich, aber die meisten Werte tendieren in Richtung einer kleinen sub-Bereich.
Beachten Sie, dass, wenn ein compiler kann beweisen, dass Sie Weg erhalten können mit speichern der Werte in einer kleineren Menge an Speicherplatz, ohne zu brechen keine code (zum Beispiel eine variable nur intern sichtbar innerhalb einer einzelnen übersetzungseinheit), und Optimierung Heuristiken schlagen vor, dass es effizienter auf die Ziel-hardware, ist es völlig dürfen optimieren Sie entsprechend und speichern Sie es in einer kleineren Menge an Speicherplatz, so lange wie der rest des Codes funktioniert, "als ob" es hat die standard-Sache.
Aber, wenn der code inter-operate mit anderem code, der kompiliert werden könnte separat, Größen müssen konsistent bleiben, oder dafür sorgen, dass jedes Stück code folgt demselben übereinkommen.
Weil wenn es nicht konsistent ist, gibt es diese Komplikation: Was, wenn ich
int x = 255;
aber dann später in den code, den ich tunx = y
? Wennint
könnte variabler Breite, der compiler müsste wissen, vor der Zeit, zu pre-reservieren Sie die maximale Menge an Speicherplatz Sie benötigen. Das ist nicht immer möglich, denn was ist, wenny
ist ein argument übergeben, aus einem anderen Stück code, das getrennt kompiliert?Benutzt Java-Klassen aufgerufen, "BigInteger" und "BigDecimal", genau das zu tun wie funktioniert der C++'s GMP-Klasse in C++ - Schnittstelle offenbar (Dank Digital-Trauma). Sie können leicht machen es sich in so ziemlich jeder Sprache, wenn Sie wollen.
CPUs haben immer die Fähigkeit hatten, zu verwenden BCD (Binary Coded Decimal), die ausgelegt ist zur Unterstützung von Operationen von beliebiger Länge (aber Sie neigen dazu, manuell zu betreiben, der auf ein byte zu einer Zeit, die wäre LANGSAM durch die heutigen GPU-standards.)
Der Grund, warum wir nicht verwenden, diese oder ähnliche Lösungen? Leistung. Ihre höchst performant Sprachen kann es sich nicht leisten zu gehen, erweitern Sie eine variable in der Mitte ein paar engen loop-Betrieb-es wäre sehr nicht-deterministisch.
In der Masse Lager-und transport-Situationen, verpackt-Werte sind oft die EINZIGE Art von Wert, den Sie verwenden würde. Zum Beispiel, ein Musik/video-Paket gestreamt werden, um Ihren computer verbringen könnten ein bisschen angeben, wenn der nächste Wert 2 Byte oder 4 Byte als Größe Optimierung.
Sobald es auf Ihrem computer, wo kann es verwendet werden, obwohl, Speicher ist Billig, aber die Geschwindigkeit und die Komplikation der veränderbare Variablen ist es nicht.. das ist wirklich der einzige Grund.
Weil es wäre sehr kompliziert, und die Berechnung schwerer haben einfach Typen, die mit der dynamischen Größen. Ich bin nicht sicher, es wäre dies auch möglich.
Computer haben würde, um zu überprüfen, wie viele bits die Anzahl nimmt nach jeder änderung seines Wertes. Es wäre eine ganze Menge zusätzlicher Operationen.
Und es wäre wesentlich schwieriger, die Berechnungen ausführen, wenn Sie nicht wissen, die Größen der Variablen während der Kompilierung.
Zur Unterstützung der dynamischen Größen der Variablen, die computer eigentlich müsste daran erinnern, wie viele bytes eine variable hat jetzt welche ... würde zusätzlichen Speicher zum speichern dieser Informationen. Und diese Informationen müssen analysiert werden, bevor jede operation auf der Variablen wählen Sie den richtigen Prozessor-Anweisung.
Besser versteht, wie computer funktioniert und warum Variablen Konstante Größen, lernen die Grundlagen der assembler-Sprache.
Obwohl, ich vermute, es wäre möglich, etwas zu erreichen, wie das mit constexpr-Werte. Dies würde jedoch den code weniger vorhersehbar, für einen Programmierer. Ich nehme an, dass einige compiler-Optimierungen könnten etwas tun, aber Sie verstecken es von einem Programmierer um Dinge einfach zu halten.
Beschrieb ich hier nur die Probleme, die Bedenken, die performance eines Programms. Ich ausgelassen, alle Probleme müssten gelöst werden, um Speicher zu sparen durch die Verringerung der Größen von Variablen. Ehrlich gesagt, ich glaube nicht, dass es überhaupt möglich ist.
Abschließend mit kleineren Variablen als deklariert hat nur Sinn, wenn die Werte bekannt sind, während der Kompilierung. Es ist ziemlich wahrscheinlich, dass moderne Compiler tun das. In anderen Fällen würde es dazu führen, dass zu viele schwere oder sogar unlösbare Probleme.
56
und wir Multiplikation mit 2-byte-variable. Auf einigen Architekturen 64-bit-Betrieb wäre mehr Rechenzeit schwere compiler so optimieren könnte, dass führen nur 16-bit-Multiplikation.Dies ist bekannt als variable-Länge-Kodierung, gibt es verschiedene Codierungen definiert, zum Beispiel VLQ. Eines der bekanntesten, jedoch vermutlich UTF-8: UTF-8-kodiert code-Punkte, die auf einer Variablen Anzahl von bytes, von 1 bis 4.
Wie immer in der Technik, es geht um trade-offs. Es gibt keine Lösung, die nur Vorteile, also Sie müssen das Gleichgewicht zwischen Vorteilen und trade-offs beim entwerfen Ihrer Lösung.
Dem design, das besiedelt wurde, war die Verwendung fester Größe Basistypen, und die hardware/Sprachen flogen nur so herunter.
Also, was ist die die grundlegende Schwäche der variable Codierung, die verursacht es zu sein, abgelehnt zu Gunsten von mehr Speicher-hungrig-Schemata? Ohne Random-Adressierung.
Was ist der index der byte an, die der 4. code-point beginnt in einer UTF-8-string?
Kommt es auf die Werte des vorherigen code-Punkte, ein linear-scan erforderlich ist.
Sicherlich gibt es variable-Länge-Codierung-Schemata, die besser auf die random-Adressierung?
Ja, aber Sie sind auch komplizierter. Wenn es ein ideal ein, ich habe noch nie gesehen, noch.
Nicht Zufällige Adressierung es wirklich ankommt, eh?
Oh JA!
Die Sache ist, jede Art von Aggregat/array stützt sich auf fixed-size-Typen:
struct
? Zufällige Adressierung!Was bedeutet, dass Sie im wesentlichen die folgenden trade-off:
Feste Größe von Typen ODER Linear Speicher-scans
Computer-Speicher ist unterteilt in aufeinanderfolgend adressierten Blöcken einer bestimmten Größe (oft 8 bits, und bezeichnet als bytes), und die meisten Computer sind entworfen, um effizienter Zugriff auf die Sequenzen von bytes, die haben aufeinander folgende Adressen.
Wenn ein Objekt-Adresse ändert sich nie im Objekt Leben, dann code gegeben und seine Adresse schnell auf das Objekt in Frage. Eine wesentliche Einschränkung dieses Ansatzes ist jedoch, dass wenn eine Adresse zugewiesen ist, für die Adresse X, und dann die andere Adresse ist zugewiesen für Adresse Y, die N-bytes entfernt, dann ist X nicht in der Lage zu wachsen, die größer als N bytes innerhalb der Lebensdauer von Y, wenn entweder X oder Y verschoben wird. Um für X zu bewegen, wäre es notwendig, dass alles im Universum, hält die X-Adresse werden aktualisiert, um die neuen, und ebenso für Y zu bewegen. Es ist zwar möglich, ein system zu konzipieren, zu erleichtern, solche updates (sowohl Java als auch .NET verwalten es ziemlich gut) es ist viel effektiver, arbeiten mit Objekten, die den Aufenthalt in der gleichen Position während der gesamten Lebensdauer, die wiederum in der Regel verlangen, dass Ihre Größe konstant bleiben muss.
Die kurze Antwort ist: Weil der C++ standard sagt so.
Ist die lange Antwort: Was Sie tun können, auf einem computer ist letztlich begrenzt durch die hardware. Es ist natürlich möglich, Kodieren für eine ganze Zahl in eine variable Anzahl von bytes für die Speicherung, aber dann Lesen es würde entweder erfordern spezielle CPU-Befehle zu schnell, oder Sie könnte die Implementierung in software, aber dann wäre es schrecklich langsam. Feste Größe-Operationen in der CPU für die be-Werte der vordefinierten Breite, es gibt keine für variable breiten.
Ein weiterer Punkt zu beachten ist, wie computer-Speicher funktioniert. Lassen Sie uns sagen, dass Ihre integer-Typ nehmen könnte irgendwo zwischen 1 bis 4 Byte Speicherplatz. Angenommen, Sie speichern den Wert 42 in Ihre integer: es nimmt 1 byte, und Sie legen Sie es auf den memory-Adresse X. Dann speichern Sie Ihre nächste variable an Position X+1 (ich bin nicht unter Berücksichtigung der Ausrichtung an dieser Stelle) und so weiter. Später Sie sich entscheiden, ändern Sie Ihren Wert zu 6424.
Aber das passt nicht in ein byte! Also, was tun Sie? Wo steckst du den rest? Haben Sie bereits etwas bei X+1, so kann nicht platzieren Sie es. Irgendwo anders? Wie werden Sie später wissen, wo? Computer-Speicher unterstützt keine insert-Semantik: Sie können nicht nur etwas an einer Stelle und schieben Sie alles, nachdem Sie Sie beiseite, um Platz zu machen!
Beiseite: Was du redest, ist eigentlich das Gebiet der Datenkompression. Kompressions-algorithmen existieren, um alles packen enger, so dass mindestens einige von Ihnen werden in Betracht gezogen, mit mehr Platz für Ihre ganze Zahl, als es nötig ist. Allerdings komprimierten Daten ist nicht einfach zu verändern (falls überhaupt möglich) und nur am Ende stärker komprimiert jedes mal, wenn Sie änderungen vornehmen, um es.
Gibt es ziemlich umfangreiche Laufzeit-performance profitiert, dies zu tun. Wenn Sie wurden zu arbeiten auf variable-size-Typen, würden Sie entschlüsseln müssen jede Zahl, bevor Sie den Betrieb (Maschinen-code Instruktionen sind in der Regel mit fester Breite), die operation, dann suchen Sie sich einen Platz im Speicher groß genug ist um das Ergebnis aufzunehmen. Das sind sehr schwierige Operationen. Es ist viel einfacher, einfach speichern Sie alle Daten leicht ineffizient.
Dies ist nicht immer wie es gemacht wird. Betrachten Sie Google Protobuf-Protokoll. Protobufs sind entworfen, um Daten zu übertragen sehr effizient. Die Verringerung der Anzahl der übertragenen bytes Wert ist, die Kosten für zusätzliche Anweisungen, die beim Betrieb auf die Daten. Dementsprechend protobufs verwenden eine Verschlüsselung, die verschlüsselt ganzen zahlen 1, 2, 3, 4, oder 5 bytes, und kleinere Ganzzahlen werden weniger bytes. Sobald die Nachricht empfangen wird, aber es wird ausgepackt, in einen eher traditionellen Feste Größe-integer-format, die leichter zu bedienen ist auf. Es ist nur während der übertragung über das Netzwerk, die Sie verwenden, wie ein space-efficient variable length integer.
Ich mag Sergej ' s Haus-Analogie, aber ich denke, eine Auto-Analogie wäre besser.
Vorstellen-Variablen-Typen als Typen von Autos und Leute wie Daten. Wenn wir auf der Suche nach einem neuen Auto, wählen wir die eine, die passt unsere Zwecke am besten. Wollen wir eine kleine smart-Auto, das kann nur passen ein oder zwei Personen? Oder eine limousine mehr Menschen tragen? Beide haben Ihre Vorteile und Nachteile, wie Geschwindigkeit und Benzinverbrauch (denken Sie an Geschwindigkeit und Speichernutzung).
Wenn Sie einen Limousinen-und Sie fahren allein, es wird nicht schrumpfen zu fit nur Sie. Zu tun, würden Sie haben, das Auto zu verkaufen (gelesen: deallocate) und kaufen eine neue kleinere für sich selbst.
Fortsetzung der Analogie kann man sich den Speicher als einen riesigen Parkplatz mit Autos gefüllt, und wenn Sie gehen, um zu Lesen, einen spezialisierten chauffeur ausgebildet, die ausschließlich für Ihre Art von Auto geht um es zu Holen für Sie. Wenn Sie Ihr Auto ändern könnte-Typen je auf die Menschen im inneren, würden Sie brauchen, um eine ganze Reihe von Fahrern jedes mal, wenn Sie wollte, um Ihr Auto, da Sie nie wissen, welche Art von Auto werden sitzen in den Ort.
In anderen Worten, zu versuchen, um zu bestimmen, wie viel Speicher Sie benötigen zum Lesen zur Laufzeit wäre äußerst ineffizient und überwiegen die Tatsache, dass Sie könnte vielleicht passen ein paar mehr Autos in Ihrem Parkplatz.
Gibt es ein paar Gründe. Eine ist die zusätzliche Komplexität für das handling beliebiger Größe, die zahlen und die Leistung dieser Treffer gibt, weil der compiler kann nicht mehr optimieren, basierend auf der Annahme, dass jeder int wird genau X bytes lang.
Ein zweiter ist, dass die Speicherung von einfachen Datentypen bedeutet, Sie brauchen ein zusätzliches byte, um zu halten die Länge. Also, ein Wert von 255 oder weniger eigentlich braucht zwei bytes in diesem neuen system, nicht eine, und im schlimmsten Fall müssen Sie nun 5 bytes statt 4. Dies bedeutet, dass der performance-Gewinn in Bezug auf Speicher verwendet wird, weniger als Sie vielleicht denken und einige Grenzfälle vielleicht tatsächlich eine Netto-Verlust.
Ein Dritter Grund ist, dass computer-Speicher ist in der Regel adressierbar in Worte, nicht bytes. (Siehe aber Fußnote).
Worte sind ein Vielfaches von bytes, in der Regel 4 auf 32-bit-Systemen und 8 auf 64-bit-Systemen. Sie können in der Regel nicht Lesen eines einzelnen byte, Sie Lesen ein Wort, und extrahieren Sie die x-te byte aus, das Wort. Dies bedeutet, dass das extrahieren einzelner bytes in einem word-dauert ein bisschen mehr Aufwand als nur das Lesen des gesamten Wortes und, dass es sehr effizient, wenn der gesamte Speicher gleichmäßig aufgeteilt in Wort-Größe (dh 4-byte große) Stücke schneiden.
Da, wenn man beliebig große ganze zahlen im Umlauf, könnten Sie am Ende mit einem Teil der Ganzzahl wird in einem Wort, und ein weiteres in das nächste Wort, dass es zwei mal gelesen, um den vollen integer.
Fußnote: um genauer Zu sein, während Sie behandelt in bytes, den meisten Systemen ignoriert die 'ungleiche' - bytes. Ie, Adresse 0, 1, 2 und 3 alle Lesen das gleiche Wort, 4, 5, 6 und 7 Lesen Sie das nächste Wort und so weiter.
Auf eine unreleated beachten Sie, dies ist auch der Grund, warum 32-bit-Systeme können maximal 4 GB Speicher. Die Register, die verwendet werden, um Speicherstellen im Arbeitsspeicher sind in der Regel groß genug, um halten Sie ein Wort, ie 4 bytes, die einen max-Wert von (2^32)-1 = 4294967295. 4294967296 bytes 4 GB.
Objekte gibt, die in gewissem Sinne haben variable Größe, die im C++ standard-Bibliothek, wie
std::vector
. Jedoch, alle diese dynamisch zuweisen der zusätzlichen Speicher Sie benötigen. Wenn Siesizeof(std::vector<int>)
erhalten Sie eine Konstante, die hat nichts zu tun mit dem Speicher verwaltet, die von dem Objekt, und wenn Sie reservieren ein array oder eine Struktur mitstd::vector<int>
es wird reserve diese base-Größe, anstatt die extra-Speicher in der gleichen array oder eine Struktur. Es gibt ein paar Stücke von der C-syntax, die mit Unterstützung so etwas wie dieses, vor allem mit variabler Länge arrays und Strukturen, aber C++ nicht wählen, um Sie zu unterstützen.Der Sprache standard definiert die Größe des Objekts, die Art und Weise, so dass Compiler erzeugen kann, die effizienten code. Zum Beispiel, wenn
int
geschieht, werden 4 Byte lang auf einige Umsetzung, und erklären Siea
als ein Zeiger oder ein array vonint
Werte, danna[i]
übersetzt in die pseudocode, "dereferenzieren der Adresse a + 4×ich." Dies kann in konstanter Zeit, und ist eine so häufige und wichtige operation, die viele instruction-set-Architekturen, darunter x86 und DEC PDP Rechnern, auf denen C wurde ursprünglich entwickelt, können es in einem einzigen Maschinenbefehl.Einer gemeinsamen real-world Beispiel von gespeicherten Daten nacheinander als variable-Länge-Einheiten strings als UTF-8 kodiert. (Allerdings ist der zugrunde liegende Typ einer UTF-8-string an den compiler ist noch
char
und hat die Breite 1. Dies ermöglicht die Verwendung von ASCII-Zeichenfolgen interpretiert werden als gültig UTF-8, und eine Menge code für die Bibliothek wiestrlen()
undstrncpy()
um weiter zu arbeiten.) Die Codierung alle UTF-8-codepoint kann aus einem bis vier bytes lang ist, und daher, wenn Sie möchten, dass die fünfte UTF-8-Codepunkt in einen string, es könnte beginnen irgendwo ab dem fünften byte des siebzehnten byte der Daten. Der einzige Weg Sie zu finden ist scan aus dem Anfang des Strings und überprüfen Sie die Größe der einzelnen codepoint. Wenn Sie wollen, finden die fünften grapheme, Sie müssen auch prüfen, die Charakter-Klassen. Wenn Sie wollte, zu finden, der millionste UTF-8-Zeichen in einer Zeichenfolge, die Sie würde ausführen müssen, um diese Schleife eine million mal! Wenn Sie wissen, dass Sie arbeiten müssen, um mit Indizes oft können Sie durchqueren, einmal die saite und einen index zu erstellen—oder Sie können konvertieren in ein fixed-width-encoding, z.B. UCS-4. Das finden der millionste UCS-4-Zeichen in einer Zeichenfolge ist nur eine Frage der Zugabe von vier Millionen auf die Adresse des array.Anderen Komplikation mit variabler Länge Daten, die, wenn Sie es zuweisen, müssen Sie entweder zu reservieren so viel Arbeitsspeicher wie könnte es jemals vielleicht verwenden, oder aber dynamisch zuzuweisen, wie benötigt werden. Die Zuweisung für den ungünstigsten Fall könnte extrem kostspielig. Wenn Sie brauchen, einer fortlaufenden Speicherblock zuweisen könnte Sie zwingen, kopieren Sie alle Daten an einem anderen Speicherort, aber wenn die Speicher gespeichert werden, die in nicht-aufeinander folgende Stücke erschwert die Programm-Logik.
Also, es ist möglich, variable-Länge bignums statt fester Breite
short int
,int
,long int
undlong long int
, aber es wäre ineffizient, zu reservieren und zu nutzen. Darüber hinaus werden alle mainstream CPUs sind entworfen, um zu tun Arithmetik auf eine Feste Breite Register, und keine Anweisungen, die direkt zu betreiben, die auf irgendeine Art von variable-Länge-bignum. Diese müssten umgesetzt werden, in der software, viel mehr langsam.In der realen Welt, die meisten (aber nicht alle) haben die Programmierer beschlossen, dass die Vorteile von UTF-8-Kodierung vor allem die Kompatibilität wichtig sind, und dass wir so selten über Pflege etwas anderes ist als das Scannen eines string von vorne nach hinten oder kopieren von Blöcken von Speicher, der die Nachteile von variabler Breite sind akzeptabel. Wir konnten noch verpackt, mit variabler Breite Elemente ähnlich wie UTF-8 für andere Dinge. Aber wir sehr selten tun, und Sie sind nicht in der standard-Bibliothek.
Vor allem wegen der Ausrichtung Anforderungen.
Als pro basic.ausrichten/1:
Denken Sie an ein Gebäude mit vielen Etagen und jede Etage hat viele Zimmer.
Jedes Zimmer ist Ihr Größe (einen festen Platz) der Lage, der N-Menge von Personen oder Objekten.
Mit der Größe der Zimmer sind im Voraus bekannt, es macht die strukturellen Komponenten des Gebäudes gut strukturierte.
Wenn das Zimmer nicht ausgerichtet, dann wird das Gebäude Skelett nicht gut strukturiert.
Kann es weniger sein. Betrachte die Funktion:
es Assembler-code kompiliert (g++, x64, details beraubt)
Hier
bar
undbaz
am Ende mit null-bytes zu stellen.Weil Sie gesagt haben, es zu benutzen so viel. Bei Verwendung eines
unsigned int
einige Normen schreiben vor, dass 4 bytes verwendet wird und daß der zur Verfügung stehende Bereich, denn es werden von 0 bis 4,294,967,295. Wenn Sie auf einenunsigned char
stattdessen würden Sie wahrscheinlich nur über das 1 byte, das du suchst, ist (je nach standard und C++ verwendet in der Regel diese Normen).Wenn es nicht für diese standards, die Sie haben würden, dies zu Bedenken: wie ist der compiler oder die CPU auch wissen sollen, verwenden Sie nur 1 byte statt 4? Später in Ihrem Programm könnten Sie addieren oder zu multiplizieren, der Wert, die würde mehr Platz benötigen. Wenn Sie eine Speicher-Zuordnung, die OS hat, finden, anzeigen, und geben Sie diesen Raum, (potenziell swapping Speicher auf virtuellen RAM als gut); dies kann eine lange Zeit dauern. Wenn Sie den Speicher vor der hand, Sie müssen nicht warten, bis eine weitere Zuweisung abgeschlossen werden.
Als für der Grund, warum wir mit 8 bit pro byte, können Sie einen Blick auf diese:
Was ist der hintergrund, warum Byte sind acht bits?
On a side note, kann man die integer-überlauf; aber sollten Sie verwenden Sie eine Ganzzahl, die C\C++ - standards Stand, dass integer-überläufe führen zu undefiniertem Verhalten.
Integer-überlauf
Etwas einfach, die meisten Antworten scheinen zu verpassen:
, denn es passt die design-Ziele von C++.
Arbeiten zu können, sich ein geben Sie die Größe zur compile-Zeit ermöglicht, eine große Anzahl von vereinfachenden Annahmen gemacht werden, die durch den compiler und Programmierer, die bringen eine Menge Vorteile, besonders im Hinblick auf die Leistung. Natürlich, fixed-size-Typen haben, die damit einhergehenden Fallstricke wie "integer overflow". Dies ist der Grund, warum unterschiedliche Sprachen machen unterschiedliche design-Entscheidungen. (Zum Beispiel, Python-Ganzzahlen sind im wesentlichen variable Größe.)
Wahrscheinlich der Hauptgrund, C++ neigt sich so stark auf fixed-size-Typen ist das Ziel der C-Kompatibilität. Da jedoch C++ ist eine statisch typisierte Sprache, die versucht, Sie zu erzeugen sehr effizienten code, und vermeidet hinzufügen von Dingen, die nicht explizit durch den Programmierer, fixed-size-Typen noch sehr viel Sinn machen.
Warum also hat sich C entscheiden Sie sich für fixed-size-Typen in den ersten Platz? Einfach. Es wurde entworfen, um zu schreiben '70er-ära-Betriebssysteme, server-software und-Dienstprogramme; Dinge, die Infrastruktur (wie z.B. memory management) für andere software. Auf einem so niedrigen Niveau, die Leistung ist entscheidend, und so wird der compiler genau das tut, was Sie sagen, es zu.
Ändern Sie die Größe einer Variablen erfordern würde, die Umverteilung und dies ist in der Regel nicht lohnt sich die zusätzliche CPU-Zyklen im Vergleich zu vergeuden, ein paar bytes mehr Arbeitsspeicher.
Lokale Variablen gehen auf einen Stapel, die sehr schnell zu Bearbeiten, wenn Sie diese Variablen ändern sich nicht in der Größe. Wenn Sie sich entschieden, Sie wollen erweitern Sie die Größe einer Variablen von 1 byte auf 2 bytes um, dann Sie haben zu bewegen alles, was auf dem stack durch ein byte zu machen, die für ihn den Platz. Das kann potenziell die Kosten eine Menge CPU-Zyklen, je nachdem, wie viele Dinge, die bewegt werden müssen.
Andere Weise, die Sie es tun könnten, ist, indem jede variable einen Zeiger auf eine heap-Lage, aber Sie würden Abfall noch mehr CPU-Zyklen und Speicher diese Weise, eigentlich. Pointer sind 4 bytes (32-bit-Adressierung) oder 8 Byte (64 bit Adressierung), so dass Sie schon mit 4 oder 8 für die Zeiger, dann ist die tatsächliche Größe der Daten auf dem heap. Es gibt noch die Kosten für die Umschichtung in diesem Fall. Wenn Sie brauchen, um eine Neuverteilung Haufen Daten, könntest du Glück haben und mehr Platz haben, erweitern Sie ihn, aber manchmal müssen Sie, um es irgendwo anders auf dem heap, um die zusammenhängenden Speicherblock der Größe, die Sie wollen.
Es ist immer schneller, um zu entscheiden, wie viel Speicher vorher. Wenn Sie können, vermeiden Sie dynamische Größenanpassung du Leistung gewinnen. Speicher zu verschwenden ist in der Regel lohnt sich der performance-Gewinn. Das ist, warum Computer haben Tonnen von Speicher. 🙂
Dem compiler erlaubt, um eine Menge von änderungen an code, solange die Dinge noch funktionieren (der "als-ist" - Regel).
Wäre es möglich, einen 8-bit-literal move-Anweisung statt des mehr (32/64 bit) erforderlich, um eine vollständige
int
. Allerdings müssten Sie zwei Anleitungen, um die Belastung, da Sie sich würde stellen müssen, um das register auf null zuerst, bevor Sie den laden.Es ist einfach effektiver (zumindest nach den wichtigsten Compilern) zu handhaben, die den Wert als 32-bit. Tatsächlich, ich habe noch zu sehen ein x86/x86_64 compiler, der würde das machen 8-bit-Last ohne inline Assembler.
Liegen die Dinge jedoch anders, wenn es um die 64 bit. Bei der Gestaltung der bisherigen Erweiterungen (von 16 auf 32 bit) Ihrer Prozessoren, die Intel einen Fehler gemacht. Hier ist eine gute Darstellung, wie Sie Aussehen. Die wichtigsten mitnehmen hier ist, dass, wenn Sie schreiben, um AL oder AH, der andere ist davon nicht betroffen (fair genug, das war der Punkt, und es machte Sinn damals). Aber es wird interessant, wenn Sie es erweitert auf 32 bit. Wenn Sie schreiben, die unteren bits (AL, AH oder AX), passiert nix, die oberen 16 bits des EAX, was bedeutet, dass, wenn Sie heraufstufen möchten ein
char
in eineint
, müssen Sie klar, dass der Speicher zuerst, aber Sie haben keine Möglichkeit, tatsächlich mit nur diese top-16-bit, so dass dieses "feature" mehr ein Schmerz als alles andere.Nun mit 64 bit, AMD hat einen viel besseren job. Wenn Sie berühren etwas in den unteren 32 bits, die oberen 32 bit werden einfach auf 0 gesetzt. Dies führt zum eigentlichen Optimierungen, die Sie sehen können in diesem godbolt. Sie können sehen, dass das laden etwas von 8 bits oder 32 bits ist der gleiche Weg, aber wenn Sie 64-bit-Variablen, verwendet der compiler eine andere Weisung abhängig von der tatsächlichen Größe Ihrer literalen.
So können Sie hier sehen, können Compiler völlig ändern Sie die tatsächliche Größe der Ihre variable in die CPU, wenn es würde zum selben Ergebnis führen, aber es macht keinen Sinn, so zu tun für die kleineren Arten.