Eine Sache, die mich immer intuitiv als positives Merkmal von C empfunden hat (nun ja, tatsächlich von seinen Implementierungen wie gcc, clang, ...), ist die Tatsache, dass es zur Laufzeit keine versteckten Informationen neben Ihren eigenen Variablen speichert. Damit meine ich, wenn Sie zum Beispiel eine Variable "x" vom Typ "uint16_t" wollten, könnten Sie sicher sein, dass "x" nur 2 Bytes Speicherplatz belegt (und keine versteckten Informationen wie den Typ usw. Enthält .). Wenn Sie ein Array mit 100 Ganzzahlen wünschen, können Sie sicher sein, dass es 100 Ganzzahlen groß ist.
Je mehr ich jedoch versuche, konkrete Anwendungsfälle für diese Funktion zu entwickeln, desto mehr frage ich mich, ob sie überhaupt praktische Vorteile hat. Das einzige, was ich mir bisher einfallen lassen konnte, ist, dass es offensichtlich weniger RAM benötigt. Für begrenzte Umgebungen wie AVR-Chips usw. ist dies definitiv ein großes Plus, aber für alltägliche Desktop- / Server-Anwendungsfälle scheint es ziemlich irrelevant zu sein. Eine andere Möglichkeit , die ich denke, dass es vielleicht hilfreich sein / entscheidend für den Zugriff auf Hardware, oder vielleicht Speicherbereiche abbildet (zB für VGA - Ausgang und dergleichen) ...?
Meine Frage: Gibt es konkrete Domänen, die ohne diese Funktion nicht oder nur sehr umständlich implementiert werden können?
PS Bitte sag mir, ob du einen besseren Namen dafür hast! ;)
quelle
virtual
Member-Funktion verfügen . RTTI vergrößert also niemals Objekte, sondern vergrößert die Binärdatei nur um eine Konstante.T *
ist immer gleich groß undT
kann ein verstecktes Feld enthalten, das auf die vtable zeigt. Und kein C ++ - Compiler hat jemals vtables in Objekte eingefügt, die sie nicht benötigen.Antworten:
Es gibt mehrere Vorteile, der offensichtliche liegt bei der Kompilierung, um sicherzustellen, dass Dinge wie Funktionsparameter mit den übergebenen Werten übereinstimmen.
Aber ich denke, Sie fragen, was zur Laufzeit passiert.
Beachten Sie, dass der Compiler eine Laufzeit erstellt, die das Wissen über die Datentypen in die von ihm ausgeführten Vorgänge einbettet. Jeder Datenblock im Speicher ist möglicherweise nicht selbstbeschreibend, aber der Code weiß von Natur aus, was diese Daten sind (wenn Sie Ihre Arbeit korrekt ausgeführt haben).
Zur Laufzeit sind die Dinge etwas anders als Sie denken.
Nehmen Sie beispielsweise nicht an, dass nur zwei Bytes verwendet werden, wenn Sie uint16_t deklarieren. Je nach Prozessor und Wortausrichtung kann es 16, 32 oder 64 Bit auf dem Stapel belegen. Möglicherweise verbraucht Ihr Shorts-Array viel mehr Speicher als erwartet.
Dies kann in bestimmten Situationen problematisch sein, in denen Sie Daten an bestimmten Offsets referenzieren müssen. Dies geschieht bei der Kommunikation zwischen zwei Systemen mit unterschiedlichen Prozessorarchitekturen, entweder über eine drahtlose Verbindung oder über Dateien.
Mit C können Sie Strukturen mit Granularität auf Bitebene angeben:
Diese Struktur ist drei Byte lang, wobei ein Kurzschluss definiert ist, um mit einem ungeraden Versatz zu beginnen. Es muss auch verpackt werden, um genau so zu sein, wie Sie es definiert haben. Andernfalls richtet der Compiler die Elemente in Wortausrichtung aus.
Der Compiler generiert Code hinter den Kulissen, um diese Daten zu extrahieren und in ein Register zu kopieren, damit Sie nützliche Dinge damit tun können.
Jetzt können Sie sehen, dass mein Programm jedes Mal, wenn es auf ein Mitglied der myMessage-Struktur zugreift, weiß, wie es genau extrahiert und bearbeitet wird.
Dies kann problematisch und schwierig zu handhaben sein, wenn zwischen verschiedenen Systemen mit verschiedenen Softwareversionen kommuniziert wird. Sie müssen das System und den Code sorgfältig entwerfen, um sicherzustellen, dass beide Seiten genau die gleiche Definition der Datentypen haben. Dies kann in einigen Umgebungen eine große Herausforderung sein. Hier benötigen Sie ein besseres Protokoll, das selbstbeschreibende Daten wie die Protokollpuffer von Google enthält .
Zuletzt sollten Sie sich fragen, wie wichtig dies in der Desktop- / Serverumgebung ist. Es hängt wirklich davon ab, wie viel Speicher Sie verwenden möchten. Wenn Sie beispielsweise eine Bildverarbeitung ausführen, wird möglicherweise viel Speicher benötigt, was sich auf die Leistung Ihrer Anwendung auswirken kann. Dies ist definitiv immer ein Problem in der eingebetteten Umgebung, in der der Speicher eingeschränkt ist und kein virtueller Speicher vorhanden ist.
quelle
short
. Dies ist jedoch eine einmalige Voraussetzung für den Start des Arrays. Der Rest wird automatisch korrekt ausgerichtet, da er aufeinanderfolgend ist.uint8_t padding: 6;
, genau wie bei den ersten beiden Bits. Oder klarer nur der Kommentar//6 bits of padding inserted by the compiler
. Die Struktur hat, wie Sie sie geschrieben haben, eine Größe von mindestens neun Bytes, nicht drei.Sie treffen auf einen der einzigen Gründe, warum dies nützlich ist: die Zuordnung externer Datenstrukturen. Dazu gehören speicherabgebildete Videopuffer, Hardwareregister usw. Dazu gehören auch Daten, die intakt außerhalb des Programms übertragen werden, wie SSL-Zertifikate, IP-Pakete, JPEG-Bilder und so ziemlich jede andere Datenstruktur, die außerhalb des Programms eine dauerhafte Lebensdauer hat.
quelle
C ist eine einfache Sprache, fast ein portabler Assembler, daher befinden sich die Datenstrukturen und Sprachkonstrukte in der Nähe des Metalls (Datenstrukturen verursachen keine versteckten Kosten - mit Ausnahme der durch Hardware und ABI auferlegten Einschränkungen hinsichtlich Auffüllen, Ausrichtung und Größe ). C hat also in der Tat keine dynamische Typisierung von Haus aus. Wenn Sie es jedoch benötigen, können Sie eine Konvention festlegen, dass alle Ihre Werte Aggregate sind, beginnend mit bestimmten Typinformationen (z. B. einigen ...). Verwendung -s und (für arrayartige Dinge) flexible Anordnungs - Element in sich auch die Größe des Arrays enthält.
enum
union
struct
(Wenn Sie in C programmieren, liegt es in Ihrer Verantwortung, nützliche Konventionen zu definieren, zu dokumentieren und zu befolgen - insbesondere Vor- und Nachbedingungen und Invarianten. Auch die dynamische Speicherzuweisung von C erfordert explizite Konventionen darüber, wer
free
eine gehäuftemalloc
Speicherzone verwenden soll.)Also, Werte zu repräsentieren , die boxed ganze Zahlen sind, oder Strings, oder irgendeine Art von Schema -ähnlichen Symbol oder Vektoren von Werten, werden Sie das Konzept eine verwenden getaggten Vereinigung (als Vereinigung von Zeigern implementiert) -Immer vom Typ Art Start -, z.B:
Um den dynamischen Typ eines Wertes zu erhalten
Hier ist eine "dynamische Umwandlung" in Vektoren:
und ein "sicherer Accessor" innerhalb von Vektoren:
Normalerweise definieren Sie die meisten der oben genannten
static inline
Kurzfunktionen wie in einer Header-Datei.Übrigens, wenn Sie den Garbage Collector von Boehm verwenden können, können Sie ganz einfach in einem übergeordneten (aber unsicheren) Stil codieren, und mehrere Scheme-Interpreter werden auf diese Weise ausgeführt. Ein variadischer Vektorkonstruktor könnte sein
und wenn Sie drei Variablen haben
Sie können mit ihnen einen Vektor erstellen
make_vector(3,v1,v2,v3)
Wenn Sie Böhms Garbage Collector nicht verwenden (oder Ihren eigenen entwerfen) möchten, sollten Sie sehr vorsichtig sein, um Destruktoren zu definieren und zu dokumentieren, wer, wie und wann Speicher
free
-d sein sollte. siehe dieses Beispiel. Sie könnten alsomalloc
anstelle vonGC_MALLOC
oben verwenden (aber dann auf seinen Fehler testen), aber Sie müssen einige Destruktorfunktionen sorgfältig definieren und verwendenvoid destroy_value(value_t)
Die Stärke von C besteht darin, niedrig genug zu sein, um Code wie oben zu ermöglichen und Ihre eigenen Konventionen (insbesondere für Ihre Software) zu definieren.
quelle