Ich möchte gemischte Datentypen in einem Array speichern. Wie könnte man das machen?
c
arrays
variant
mixed-type
chanzerre
quelle
quelle
Antworten:
Sie können die Array-Elemente zu einer diskriminierten Vereinigung machen, auch bekannt als getaggte Vereinigung .
Das Element
type
wird verwendet, um die Auswahl zu halten, welches Mitglied desunion
is für jedes Array-Element verwendet werden soll. Wenn Sie also einint
Element im ersten Element speichern möchten , gehen Sie wie folgt vor:Wenn Sie auf ein Element des Arrays zugreifen möchten, müssen Sie zuerst den Typ überprüfen und dann das entsprechende Mitglied der Union verwenden. Eine
switch
Aussage ist nützlich:Es bleibt dem Programmierer überlassen, sicherzustellen, dass das
type
Mitglied immer dem zuletzt imunion
.quelle
Verwenden Sie eine Gewerkschaft:
Sie müssen jedoch den Typ jedes Elements verfolgen.
quelle
Array-Elemente müssen dieselbe Größe haben, deshalb ist dies nicht möglich. Sie können dies umgehen, indem Sie einen Variantentyp erstellen :
Die Größe des Elements der Vereinigung entspricht der Größe des größten Elements, 4.
quelle
Es gibt einen anderen Stil die Tag-Vereinigung definieren (gleich welchen Namen) , die IMO es viel schöner machen Gebrauch von der internen Vereinigung zu entfernen. Dies ist der Stil, der im X Window System für Dinge wie Ereignisse verwendet wird.
Das Beispiel in Barmars Antwort gibt
val
der internen Vereinigung den Namen . Das Beispiel in der Antwort von Sp. Verwendet eine anonyme Vereinigung, um zu vermeiden, dass bei.val.
jedem Zugriff auf den Variantendatensatz angegeben werden muss. Leider sind "anonyme" interne Strukturen und Gewerkschaften in C89 oder C99 nicht verfügbar. Es ist eine Compiler-Erweiterung und daher von Natur aus nicht portierbar.Eine bessere Möglichkeit für IMO besteht darin, die gesamte Definition umzukehren. Machen Sie jeden Datentyp zu einer eigenen Struktur und fügen Sie das Tag (Typspezifizierer) in jede Struktur ein.
Dann wickeln Sie diese in eine Union der obersten Ebene ein.
Jetzt scheint es, dass wir uns wiederholen, und wir sind es . Bedenken Sie jedoch, dass diese Definition wahrscheinlich auf eine einzelne Datei beschränkt ist. Wir haben jedoch das Rauschen bei der Angabe des Zwischenprodukts beseitigt
.val.
bevor Sie zu den Daten gelangen.Stattdessen geht es am Ende, wo es weniger widerlich ist. : D.
Eine andere Sache, die dies erlaubt, ist eine Form der Vererbung. Bearbeiten: Dieser Teil ist nicht Standard C, sondern verwendet eine GNU-Erweiterung.
Up- und Downcasting.
Bearbeiten: Ein Problem, das Sie beachten sollten, ist, wenn Sie eines davon mit C99-Initialisierern erstellen. Alle Mitgliederinitialisierer sollten über dasselbe Gewerkschaftsmitglied erfolgen.
Die
.tag
Initialisierung kann durch einen optimierenden Compiler, weil die ignoriert werden.int_
initializer die folgt Aliase der gleichen Datenbereich. Auch wenn wir das Layout (!) Kennen, und es sollte in Ordnung sein. Nein, das ist es nicht. Verwenden Sie stattdessen das "interne" Tag (es überlagert das äußere Tag, genau wie wir es wollen, verwirrt aber den Compiler nicht).quelle
.int_.val
Alias nicht derselbe Bereich, da der Compiler weiß, dass der.val
Versatz größer ist als.tag
. Haben Sie einen Link zur weiteren Diskussion über dieses angebliche Problem?Sie können ein
void *
Array mit einem getrennten Array vonsize_t.
erstellen. Sie verlieren jedoch den Informationstyp.Wenn Sie den Informationstyp auf irgendeine Weise beibehalten müssen, behalten Sie ein drittes Array von int bei (wobei int ein Aufzählungswert ist). Codieren Sie dann die Funktion, die abhängig vom
enum
Wert umgewandelt wird.quelle
Union ist der Standardweg. Sie haben aber auch andere Lösungen. Einer davon ist der markierte Zeiger , bei dem mehr Informationen im "freien" Speicher gespeichert werden. Bits eines Zeigers .
Abhängig von den Architekturen können Sie die niedrigen oder hohen Bits verwenden. Am sichersten und portabelsten ist es jedoch, die nicht verwendeten niedrigen Bits zu verwenden, indem Sie den Vorteil des ausgerichteten Speichers nutzen. In 32-Bit- und 64-Bit-Systemen müssen die Zeiger auf
int
ein Vielfaches von 4 sein (vorausgesetzt, esint
handelt sich um einen 32-Bit-Typ), und die 2 niedrigstwertigen Bits müssen 0 sein. Daher können Sie sie zum Speichern des Typs Ihrer Werte verwenden . Natürlich müssen Sie die Tag-Bits löschen, bevor Sie den Zeiger dereferenzieren. Wenn Ihr Datentyp beispielsweise auf 4 verschiedene Typen beschränkt ist, können Sie ihn wie folgt verwendenWenn Sie sicherstellen können, dass die Daten 8-Byte-ausgerichtet sind (wie bei Zeigern in 64-Bit-Systemen oder
long long
unduint64_t
...), haben Sie ein weiteres Bit für das Tag.Dies hat den Nachteil, dass Sie mehr Speicher benötigen, wenn die Daten nicht an anderer Stelle in einer Variablen gespeichert wurden. Wenn daher der Typ und der Bereich Ihrer Daten begrenzt sind, können Sie die Werte direkt im Zeiger speichern. Diese Technik wurde in der 32-Bit-Version der V8-Engine von Chrome verwendet , bei der das niedrigstwertige Bit der Adresse überprüft wird, um festzustellen, ob es sich um einen Zeiger auf ein anderes Objekt (z. B. doppelte, große Ganzzahlen, Zeichenfolge oder ein Objekt) oder ein 31 handelt -bit vorzeichenbehafteter Wert (aufgerufen
smi
- kleine Ganzzahl ). Wenn dies derint
Fall ist, führt Chrome einfach eine arithmetische Rechtsverschiebung um 1 Bit durch, um den Wert zu erhalten. Andernfalls wird der Zeiger dereferenziert.Auf den meisten aktuellen 64-Bit-Systemen ist der virtuelle Adressraum immer noch viel schmaler als 64 Bit, daher können die höchstwertigen Bits auch als Tags verwendet werden . Abhängig von der Architektur haben Sie verschiedene Möglichkeiten, diese als Tags zu verwenden. ARM , 68k und viele andere können so konfiguriert werden, dass die oberen Bits ignoriert werden , sodass Sie sie frei verwenden können, ohne sich um Segfault oder ähnliches kümmern zu müssen. Aus dem oben verlinkten Wikipedia-Artikel:
Auf x86_64 können Sie die hohen Bits weiterhin vorsichtig als Tags verwenden . Natürlich müssen Sie nicht alle diese 16 Bits verwenden und können einige Bits für die Zukunftssicherheit weglassen
In früheren Versionen von Mozilla Firefox wurden auch kleine Ganzzahloptimierungen wie V8 verwendet, wobei die 3 niedrigen Bits zum Speichern des Typs (int, string, object ... usw.) verwendet wurden. Aber seit JägerMonkey haben sie einen anderen Weg eingeschlagen ( Mozillas neue JavaScript-Wertrepräsentation , Backup-Link ). Der Wert wird jetzt immer in einer 64-Bit-Variablen mit doppelter Genauigkeit gespeichert. Wenn die
double
a normalisiert ein, kann es direkt in Berechnungen verwendet werden. Wenn jedoch die hohen 16 Bits alle 1s sind, die ein NaN bezeichnen , speichern die niedrigen 32 Bits die Adresse (in einem 32-Bit-Computer) auf den Wert oder den Wert direkt, die verbleibenden 16 Bits werden verwendet um den Typ zu speichern. Diese Technik heißt NaN-Boxenoder Nonnenboxen. Es wird auch in JavaScriptCore von 64-Bit-WebKit und SpiderMonkey von Mozilla verwendet, wobei der Zeiger in den niedrigen 48 Bit gespeichert wird. Wenn Ihr Hauptdatentyp Gleitkomma ist, ist dies die beste Lösung und liefert eine sehr gute Leistung.Lesen Sie mehr über die oben genannten Techniken: https://wingolog.org/archives/2011/05/18/value-representation-in-javascript-implementations
quelle