Warum brauchen wir C-Gewerkschaften?

236

Wann sollten Gewerkschaften eingesetzt werden? Warum brauchen wir sie?

Timrau
quelle

Antworten:

251

Gewerkschaften werden häufig verwendet, um zwischen den binären Darstellungen von Ganzzahlen und Gleitkommazahlen zu konvertieren:

union
{
  int i;
  float f;
} u;

// Convert floating-point bits to integer:
u.f = 3.14159f;
printf("As integer: %08x\n", u.i);

Obwohl dies ein technisch undefiniertes Verhalten gemäß dem C-Standard ist (Sie sollten nur das zuletzt geschriebene Feld lesen), funktioniert es in praktisch jedem Compiler genau definiert.

Manchmal werden auch Gewerkschaften verwendet, um Pseudo-Polymorphismus in C zu implementieren, indem einer Struktur ein Tag zugewiesen wird, das angibt, welchen Objekttyp sie enthält, und dann die möglichen Typen zusammengeführt werden:

enum Type { INTS, FLOATS, DOUBLE };
struct S
{
  Type s_type;
  union
  {
    int s_ints[2];
    float s_floats[2];
    double s_double;
  };
};

void do_something(struct S *s)
{
  switch(s->s_type)
  {
    case INTS:  // do something with s->s_ints
      break;

    case FLOATS:  // do something with s->s_floats
      break;

    case DOUBLE:  // do something with s->s_double
      break;
  }
}

Dies ermöglicht eine Größe von struct Snur 12 Bytes anstelle von 28.

Adam Rosenfield
quelle
es sollte uy statt uf geben
Amit Singh Tomar
1
Funktioniert das Beispiel, in dem float in eine Ganzzahl konvertiert werden soll? Ich glaube nicht, da int und float in verschiedenen Formaten gespeichert sind. Können Sie Ihr Beispiel erklären?
Spin_eight
3
@spin_eight: Es wird nicht von float nach int "konvertiert". Eher wie "die binäre Darstellung eines Floats neu interpretieren, als wäre es ein int". Die Ausgabe ist nicht 3: ideone.com/MKjwon Ich bin mir nicht sicher, warum Adam als Hex druckt .
Endolith
@ Adam Rosenfield Ich habe die Konvertierung nicht wirklich verstanden. Ich bekomme keine ganze Zahl in der Ausgabe: p
The Beast
2
Ich bin der Meinung, dass der Haftungsausschluss für undefiniertes Verhalten entfernt werden sollte. Es ist in der Tat definiertes Verhalten. Siehe Fußnote 82 des C99-Standards: Wenn das Element, mit dem auf den Inhalt eines Vereinigungsobjekts zugegriffen wird, nicht mit dem Element identisch ist, das zuletzt zum Speichern eines Werts im Objekt verwendet wurde, wird der entsprechende Teil der Objektdarstellung des Werts als neu interpretiert eine Objektdarstellung im neuen Typ, wie in 6.2.6 beschrieben (ein Prozess, der manchmal als "Typ Punning" bezeichnet wird). Dies könnte eine Trap-Darstellung sein.
Christian Gibbons
135

Gewerkschaften sind besonders nützlich bei der eingebetteten Programmierung oder in Situationen, in denen ein direkter Zugriff auf die Hardware / den Speicher erforderlich ist. Hier ist ein triviales Beispiel:

typedef union
{
    struct {
        unsigned char byte1;
        unsigned char byte2;
        unsigned char byte3;
        unsigned char byte4;
    } bytes;
    unsigned int dword;
} HW_Register;
HW_Register reg;

Dann können Sie wie folgt auf die Registrierung zugreifen:

reg.dword = 0x12345678;
reg.bytes.byte3 = 4;

Endianness (Bytereihenfolge) und Prozessorarchitektur sind natürlich wichtig.

Ein weiteres nützliches Feature ist der Bitmodifikator:

typedef union
{
    struct {
        unsigned char b1:1;
        unsigned char b2:1;
        unsigned char b3:1;
        unsigned char b4:1;
        unsigned char reserved:4;
    } bits;
    unsigned char byte;
} HW_RegisterB;
HW_RegisterB reg;

Mit diesem Code können Sie direkt auf ein einzelnes Bit in der Register- / Speicheradresse zugreifen:

x = reg.bits.b2;
kgiannakakis
quelle
3
Ihre Antwort hier in Verbindung mit der obigen Antwort von @Adam Rosenfield ergibt das perfekte komplementäre Paar: Sie demonstrieren die Verwendung einer Struktur innerhalb einer Union , und er demonstriert die Verwendung einer Union innerhalb einer Struktur . Es stellt sich heraus, dass ich beides gleichzeitig brauche: eine Struktur innerhalb einer Union innerhalb einer Struktur , um einen ausgefallenen Polymorphismus für die Nachrichtenübermittlung in C zwischen Threads auf einem eingebetteten System zu implementieren, und ich hätte nicht bemerkt, dass ich nicht beide Antworten zusammen gesehen hätte .
Gabriel Staples
1
Ich habe mich geirrt: Es ist eine Vereinigung innerhalb einer Struktur innerhalb einer Vereinigung innerhalb einer Struktur, die verschachtelt bleibt, um zu schreiben, wie ich das geschrieben habe, von der innersten Verschachtelung bis zur äußersten Ebene. Ich musste eine weitere Vereinigung auf der innersten Ebene hinzufügen, um Werte verschiedener Datentypen zuzulassen.
Gabriel Staples
64

Low-Level-Systemprogrammierung ist ein vernünftiges Beispiel.

IIRC, ich habe Gewerkschaften verwendet, um Hardwareregister in die Komponentenbits aufzuteilen. Sie können also auf ein 8-Bit-Register (wie es an dem Tag war, als ich dies getan habe ;-) in die Komponentenbits zugreifen.

(Ich vergesse die genaue Syntax, aber ...) Diese Struktur würde den Zugriff auf ein Steuerregister als Steuerbyte oder über die einzelnen Bits ermöglichen. Es wäre wichtig sicherzustellen, dass die Bits für eine gegebene Endianness auf die richtigen Registerbits abgebildet werden.

typedef union {
    unsigned char control_byte;
    struct {
        unsigned int nibble  : 4;
        unsigned int nmi     : 1;
        unsigned int enabled : 1;
        unsigned int fired   : 1;
        unsigned int control : 1;
    };
} ControlRegister;
Snips
quelle
3
Dies ist ein hervorragendes Beispiel! Hier ist ein Beispiel, wie Sie diese Technik in eingebetteter Software verwenden können: edn.com/design/integrated-circuit-design/4394915/…
rzetterberg
34

Ich habe es in einigen Bibliotheken als Ersatz für objektorientierte Vererbung gesehen.

Z.B

        Connection
     /       |       \
  Network   USB     VirtualConnection

Wenn Sie möchten, dass die Verbindungsklasse eine der oben genannten Klassen ist, können Sie Folgendes schreiben:

struct Connection
{
    int type;
    union
    {
        struct Network network;
        struct USB usb;
        struct Virtual virtual;
    }
};

Beispiel für die Verwendung in libinfinity: http://git.0x539.de/?p=infinote.git;a=blob;f=libinfinity/common/inf-session.c;h=3e887f0d63bd754c6b5ec232948027cbbf4d61fc;hb=HEAD#l74

BB-Generation
quelle
33

Gewerkschaften ermöglichen es Datenmitgliedern, die sich gegenseitig ausschließen, denselben Speicher gemeinsam zu nutzen. Dies ist sehr wichtig, wenn der Speicher knapp ist, z. B. in eingebetteten Systemen.

Im folgenden Beispiel:

union {
   int a;
   int b;
   int c;
} myUnion;

Diese Vereinigung nimmt den Raum eines einzelnen int ein und nicht von drei separaten int-Werten. Wenn der Benutzer den Wert von a und dann den Wert von b festlegt, wird der Wert von a überschrieben, da beide denselben Speicherort gemeinsam nutzen.

LeopardSkinPillBoxHat
quelle
29

Viele Verwendungen. Einfach machen grep union /usr/include/*oder in ähnlichen Verzeichnissen. In den meisten Fällen unionwird das in ein structund ein Mitglied der Struktur eingeschlossen, das angibt, auf welches Element in der Union zugegriffen werden soll. Zum Beispiel zur Kasseman elf für reale Implementierungen.

Dies ist das Grundprinzip:

struct _mydata {
    int which_one;
    union _data {
            int a;
            float b;
            char c;
    } foo;
} bar;

switch (bar.which_one)
{
   case INTEGER  :  /* access bar.foo.a;*/ break;
   case FLOATING :  /* access bar.foo.b;*/ break;
   case CHARACTER:  /* access bar.foo.c;*/ break;
}
Phoxis
quelle
Genau das, wonach ich gesucht habe! Sehr nützlich, um einige Auslassungsparameter zu ersetzen :)
Nicolas Voron
17

Hier ist ein Beispiel für eine Vereinigung aus meiner eigenen Codebasis (aus dem Speicher und umschrieben, damit sie möglicherweise nicht genau ist). Es wurde verwendet, um Sprachelemente in einem von mir erstellten Interpreter zu speichern. Zum Beispiel der folgende Code:

set a to b times 7.

besteht aus folgenden Sprachelementen:

  • Symbol [setzen]
  • Variable [a]
  • Symbol [zu]
  • Variable [b]
  • Symbol [Zeiten]
  • konstant [7]
  • Symbol[.]

Sprachelemente wurden wie folgt definiert #define:

#define ELEM_SYM_SET        0
#define ELEM_SYM_TO         1
#define ELEM_SYM_TIMES      2
#define ELEM_SYM_FULLSTOP   3
#define ELEM_VARIABLE     100
#define ELEM_CONSTANT     101

und die folgende Struktur wurde verwendet, um jedes Element zu speichern:

typedef struct {
    int typ;
    union {
        char *str;
        int   val;
    }
} tElem;

dann war die Größe jedes Elements die Größe der maximalen Vereinigung (4 Bytes für den Typ und 4 Bytes für die Vereinigung, obwohl dies typische Werte sind, die tatsächlichen hängen Größen von der Implementierung ab).

Um ein "Set" -Element zu erstellen, würden Sie Folgendes verwenden:

tElem e;
e.typ = ELEM_SYM_SET;

Um ein "variable [b]" - Element zu erstellen, verwenden Sie:

tElem e;
e.typ = ELEM_VARIABLE;
e.str = strdup ("b");   // make sure you free this later

Um ein "Konstante [7]" - Element zu erstellen, verwenden Sie:

tElem e;
e.typ = ELEM_CONSTANT;
e.val = 7;

und Sie können es leicht erweitern, um float ( float flt) oder rationals ( struct ratnl {int num; int denom;}) und andere Typen einzuschließen.

Die Grundvoraussetzung ist, dass die strund valnicht zusammenhängend im Speicher sind, sondern sich tatsächlich überlappen. Dies ist eine Möglichkeit, eine andere Ansicht auf denselben Speicherblock zu erhalten, wie hier dargestellt, wobei die Struktur auf dem Speicherort basiert 0x1010und Ganzzahlen und Zeiger beide sind 4 Bytes:

       +-----------+
0x1010 |           |
0x1011 |    typ    |
0x1012 |           |
0x1013 |           |
       +-----+-----+
0x1014 |     |     |
0x1015 | str | val |
0x1016 |     |     |
0x1017 |     |     |
       +-----+-----+

Wenn es nur in einer Struktur wäre, würde es so aussehen:

       +-------+
0x1010 |       |
0x1011 |  typ  |
0x1012 |       |
0x1013 |       |
       +-------+
0x1014 |       |
0x1015 |  str  |
0x1016 |       |
0x1017 |       |
       +-------+
0x1018 |       |
0x1019 |  val  |
0x101A |       |
0x101B |       |
       +-------+
paxdiablo
quelle
Sollte der make sure you free this laterKommentar aus dem konstanten Element entfernt werden?
Trevor
Ja, @Trevor, obwohl ich nicht glauben kann, dass du die erste Person bist, die es in den letzten 4+ Jahren gesehen hat :-) Behoben, und danke dafür.
Paxdiablo
7

Ich würde sagen, es macht es einfacher, Speicher wiederzuverwenden, der auf unterschiedliche Weise verwendet werden kann, dh Speicher zu sparen. Sie möchten beispielsweise eine "Varianten" -Struktur erstellen, mit der sowohl eine kurze Zeichenfolge als auch eine Zahl gespeichert werden können:

struct variant {
    int type;
    double number;
    char *string;
};

In einem 32-Bit-System würde dies dazu führen, dass für jede Instanz von mindestens 96 Bit oder 12 Bytes verwendet werden variant.

Mit einer Union können Sie die Größe auf 64 Bit oder 8 Byte reduzieren:

struct variant {
    int type;
    union {
        double number;
        char *string;
    } value;
};

Sie können noch mehr sparen, wenn Sie mehr verschiedene Variablentypen usw. hinzufügen möchten. Es kann sein, dass Sie ähnliche Dinge tun können, indem Sie einen ungültigen Zeiger werfen - aber die Vereinigung macht ihn sowohl zugänglicher als auch typischer sicher. Solche Einsparungen klingen nicht massiv, aber Sie sparen ein Drittel des für alle Instanzen dieser Struktur verwendeten Speichers.

Mario
quelle
5

Es ist schwierig, sich einen bestimmten Anlass vorzustellen, bei dem Sie diese Art von flexibler Struktur benötigen, möglicherweise in einem Nachrichtenprotokoll, in dem Sie Nachrichten unterschiedlicher Größe senden würden, aber selbst dann gibt es wahrscheinlich bessere und programmiererfreundlichere Alternativen.

Gewerkschaften sind ein bisschen wie Variantentypen in anderen Sprachen - sie können jeweils nur eine Sache enthalten, aber diese Sache kann ein Int, ein Float usw. sein, je nachdem, wie Sie es deklarieren.

Beispielsweise:

typedef union MyUnion MYUNION;
union MyUnion
{
   int MyInt;
   float MyFloat;
};

MyUnion enthält nur ein int ODER ein float, je nachdem, welches Sie zuletzt festgelegt haben . Also mach das:

MYUNION u;
u.MyInt = 10;

u hält jetzt ein int gleich 10;

u.MyFloat = 1.0;

u hält jetzt einen Float von 1,0. Es enthält kein int mehr. Offensichtlich jetzt, wenn Sie versuchen, printf zu machen ("MyInt =% d", u.MyInt); dann werden Sie wahrscheinlich eine Fehlermeldung erhalten, obwohl ich mir über das spezifische Verhalten nicht sicher bin.

Die Größe der Union wird durch die Größe ihres größten Feldes bestimmt, in diesem Fall des Schwimmers.

Xiaofu
quelle
1
sizeof(int) == sizeof(float)( == 32) normalerweise.
Nick T
1
Für den Datensatz führt das Zuweisen zum Float und das Drucken des Int nicht zu einem Fehler, da weder der Compiler noch die Laufzeitumgebung wissen, welcher Wert gültig ist. Das int, das gedruckt wird, ist natürlich für die meisten Zwecke bedeutungslos. Es wird nur die Speicherdarstellung des Floats sein, interpretiert als int.
Jerry B
4

Gewerkschaften werden verwendet, wenn Sie Strukturen modellieren möchten, die durch Hardware, Geräte oder Netzwerkprotokolle definiert sind, oder wenn Sie eine große Anzahl von Objekten erstellen und Platz sparen möchten. Sie brauchen sie jedoch in 95% der Fälle wirklich nicht. Halten Sie sich an den einfach zu debuggenden Code.

Ana Betts
quelle
4

Viele dieser Antworten befassen sich mit dem Casting von einem Typ zum anderen. Gewerkschaften mit denselben Typen nutzen mich am meisten, nur mehr (dh beim Parsen eines seriellen Datenstroms). Sie ermöglichen es, das Parsen / Erstellen eines gerahmten Pakets trivial zu machen.

typedef union
{
    UINT8 buffer[PACKET_SIZE]; // Where the packet size is large enough for
                               // the entire set of fields (including the payload)

    struct
    {
        UINT8 size;
        UINT8 cmd;
        UINT8 payload[PAYLOAD_SIZE];
        UINT8 crc;
    } fields;

}PACKET_T;

// This should be called every time a new byte of data is ready 
// and point to the packet's buffer:
// packet_builder(packet.buffer, new_data);

void packet_builder(UINT8* buffer, UINT8 data)
{
    static UINT8 received_bytes = 0;

    // All range checking etc removed for brevity

    buffer[received_bytes] = data;
    received_bytes++;

    // Using the struc only way adds lots of logic that relates "byte 0" to size
    // "byte 1" to cmd, etc...
}

void packet_handler(PACKET_T* packet)
{
    // Process the fields in a readable manner
    if(packet->fields.size > TOO_BIG)
    {
        // handle error...
    }

    if(packet->fields.cmd == CMD_X)
    {
        // do stuff..
    }
}

Bearbeiten Der Kommentar zu Endianness und Strukturauffüllung ist ein berechtigtes und großes Anliegen. Ich habe diesen Code fast ausschließlich in eingebetteter Software verwendet, von denen die meisten die Kontrolle über beide Enden der Pipe hatten.

Adam Lewis
quelle
1
Dieser Code funktioniert (meistens) nicht, wenn Daten aus folgenden Gründen über zwei unterschiedliche Plattformen ausgetauscht werden: 1) Endianness kann unterschiedlich sein. 2) Polsterung in Strukturen.
Mahori
@ Ravi Ich stimme den Bedenken hinsichtlich Endianness und Polsterung zu. Es sollte jedoch bekannt sein, dass ich dies ausschließlich in eingebetteten Projekten verwendet habe. Die meisten davon kontrollierte ich beide Enden der Rohre.
Adam Lewis
1

Gewerkschaften sind großartig. Eine clevere Verwendung von Gewerkschaften, die ich gesehen habe, besteht darin, sie bei der Definition eines Ereignisses zu verwenden. Sie können beispielsweise festlegen, dass ein Ereignis 32 Bit umfasst.

Innerhalb dieser 32 Bits möchten Sie möglicherweise die ersten 8 Bits als Kennung des Absenders des Ereignisses festlegen ... Manchmal behandeln Sie das Ereignis als Ganzes, manchmal zerlegen Sie es und vergleichen seine Komponenten. Gewerkschaften geben Ihnen die Flexibilität, beides zu tun.

Gewerkschaftsereignis
{
  unsigned long eventCode;
  unsigned char eventParts [4];
};
Dicroce
quelle
1

Was VARIANTist damit in COM-Schnittstellen verwendet? Es hat zwei Felder - "Typ" und eine Vereinigung mit einem tatsächlichen Wert, der abhängig vom Feld "Typ" behandelt wird.

scharfer Zahn
quelle
1

In der Schule habe ich Gewerkschaften wie diese benutzt:

typedef union
{
  unsigned char color[4];
  int       new_color;
}       u_color;

Ich habe es verwendet, um Farben einfacher zu handhaben, anstatt die Operatoren >> und << zu verwenden, musste ich nur den unterschiedlichen Index meines char-Arrays durchgehen.

Zoneur
quelle
1

Ich habe Union verwendet, als ich für eingebettete Geräte codiert habe. Ich habe C int, das 16 Bit lang ist. Und ich muss die höheren 8 Bits und die niedrigeren 8 Bits abrufen, wenn ich aus / speichern in das EEPROM lesen muss. Also habe ich diesen Weg benutzt:

union data {
    int data;
    struct {
        unsigned char higher;
        unsigned char lower;
    } parts;
};

Es muss nicht verschoben werden, damit der Code leichter zu lesen ist.

Auf der anderen Seite sah ich einen alten C ++ stl-Code, der union für den stl-Allokator verwendete. Wenn Sie interessiert sind, können Sie den sgi stl- Quellcode lesen . Hier ist ein Stück davon:

union _Obj {
    union _Obj* _M_free_list_link;
    char _M_client_data[1];    /* The client sees this.        */
};
Mu Qiao
quelle
1
Würdest du nicht eine Gruppierung structum dein higher/ brauchen lower? Im Moment sollten beide nur auf das erste Byte zeigen.
Mario
@Mario ah richtig, ich schreibe es einfach von Hand und vergesse es, danke
Mu Qiao
1
  • Eine Datei mit verschiedenen Datensatztypen.
  • Eine Netzwerkschnittstelle mit verschiedenen Anforderungstypen.

Sehen Sie sich Folgendes an: X.25-Pufferbefehlsbehandlung

Einer der vielen möglichen X.25-Befehle wird in einem Puffer empfangen und mithilfe einer UNION aller möglichen Strukturen an Ort und Stelle verarbeitet.

James Anderson
quelle
Könnten Sie bitte diese beiden Beispiele erklären? Ich meine, wie diese mit der Gewerkschaft zusammenhängen
Amit Singh Tomar
1

In früheren Versionen von C würden alle Strukturdeklarationen einen gemeinsamen Satz von Feldern verwenden. Gegeben:

struct x {int x_mode; int q; float x_f};
struct y {int y_mode; int q; int y_l};
struct z {int z_mode; char name[20];};

Ein Compiler würde im Wesentlichen eine Tabelle mit den Größen (und möglicherweise Ausrichtungen) der Strukturen und eine separate Tabelle mit den Namen, Typen und Offsets der Mitglieder der Strukturen erstellen. Der Compiler verfolgte nicht, welche Mitglieder zu welchen Strukturen gehörten, und erlaubte zwei Strukturen, nur dann ein Element mit demselben Namen zu haben, wenn Typ und Versatz übereinstimmten (wie bei Mitglied qvon struct xundstruct y ). Wenn p ein Zeiger auf einen Strukturtyp wäre, würde p-> q den Versatz von "q" zum Zeiger p hinzufügen und ein "int" von der resultierenden Adresse abrufen.

Angesichts der obigen Semantik war es möglich, eine Funktion zu schreiben, die einige nützliche Operationen für mehrere Arten von Strukturen austauschbar ausführen konnte, vorausgesetzt, dass alle von der Funktion verwendeten Felder mit nützlichen Feldern innerhalb der fraglichen Strukturen ausgerichtet waren. Dies war eine nützliche Funktion, und das Ändern von C zur Validierung der für den Strukturzugriff verwendeten Elemente anhand der Typen der betreffenden Strukturen hätte den Verlust bedeutet, wenn keine Struktur vorhanden wäre, die mehrere benannte Felder an derselben Adresse enthalten kann. Das Hinzufügen von "Union" -Typen zu C hat dazu beigetragen, diese Lücke etwas zu schließen (allerdings nicht, IMHO, so wie es hätte sein sollen).

Ein wesentlicher Teil der Fähigkeit der Gewerkschaften, diese Lücke zu schließen, war die Tatsache, dass ein Zeiger auf ein Gewerkschaftsmitglied in einen Zeiger auf eine Gewerkschaft umgewandelt werden konnte, die dieses Mitglied enthält, und ein Zeiger auf eine Gewerkschaft in einen Zeiger auf ein Mitglied umgewandelt werden konnte. Während die Standard - C89 nicht ausdrücklich sagte , dass ein Casting T*direkt an ein U*entsprach es auf einen Zeiger auf jede Vereinigung Typ Gießen beide enthält Tund U, und dann Gießen dass U*kein definiertes Verhalten der letzteren Guss Sequenz durch die betroffen wären Der verwendete Unionstyp und der Standard haben keine entgegengesetzte Semantik für eine direkte Umwandlung von Tbis angegeben U. In Fällen, in denen eine Funktion einen Zeiger unbekannter Herkunft empfangen hat, ist das Verhalten des Schreibens eines Objekts überT* Konvertieren desT*zu a U*, und dann das Lesen des Objekts über U*wäre gleichbedeutend mit dem Schreiben einer Vereinigung über ein Mitglied vom Typ Tund dem Lesen als Typ U, was in einigen Fällen standarddefiniert (z. B. beim Zugriff auf Mitglieder der allgemeinen Anfangssequenz) und implementierungsdefiniert (eher) wäre als undefiniert) für den Rest. Während es für Programme selten war, die GUS-Garantien mit tatsächlichen Objekten vom Typ Union zu nutzen, war es weitaus üblicher, die Tatsache auszunutzen, dass Zeiger auf Objekte unbekannter Herkunft sich wie Zeiger auf Gewerkschaftsmitglieder verhalten mussten und die damit verbundenen Verhaltensgarantien hatten.

Superkatze
quelle
Können Sie ein Beispiel dafür geben: "Es war möglich, eine Funktion zu schreiben, die einige nützliche Operationen für mehrere Arten von Strukturen austauschbar ausführen konnte." Wie können mehrere Strukturelemente mit demselben Namen verwendet werden? Wenn zwei Strukturen dieselbe Datenausrichtung und damit ein Element mit demselben Namen und demselben Versatz wie in Ihrem Beispiel haben, aus welcher Struktur würde ich dann die tatsächlichen Daten liefern? (Wert). Zwei Strukturen haben dieselbe Ausrichtung und dieselben Elemente, aber unterschiedliche Werte. Können Sie es bitte
näher
@Herdsman: In früheren Versionen von C hat ein Strukturelementname einen Typ und einen Offset gekapselt. Zwei Mitglieder unterschiedlicher Strukturen könnten genau dann denselben Namen haben, wenn ihre Typen und Offsets übereinstimmen. Wenn struct member fooein intOffset mit 8 ist, anyPointer->foo = 1234;bedeutet dies "Nehmen Sie die Adresse in anyPointer, verschieben Sie sie um 8 Bytes und führen Sie einen Integer-Speicher mit dem Wert 1234 für die resultierende Adresse durch. Der Compiler muss nicht wissen oder sich darum kümmern, ob er anyPointeridentifiziert wird." jeder Strukturtyp, der foounter seinen Mitgliedern aufgeführt war.
Supercat
Mit dem Zeiger können Sie jede Adresse unabhängig vom 'Ursprung' des Zeigers dereferenzieren, das ist wahr, aber was bringt der Compiler dann dazu, Tabellen mit Strukturelementen und deren Namen (wie Sie in Ihrem Beitrag gesagt haben) zu speichern, wenn ich Daten abrufen kann mit einem Zeiger, der nur die Adresse eines Mitglieds in einer bestimmten Struktur kennt? Und wenn der Compiler nicht weiß, ob er sich anyPointermit einem Strukturelement identifiziert, wie überprüft der Compiler dann diesen Zustand to have a member with the same name only if the type and offset matchedIhres Beitrags?
Hirte
@Herdsman: Der Compiler würde die Liste der Namen der Strukturmitglieder behalten, da das genaue Verhalten von p->foovom Typ und Offset von abhängen würde foo. Im Wesentlichen p->foowar Abkürzung für *(typeOfFoo*)((unsigned char*)p + offsetOfFoo). Wenn ein Compiler auf eine Strukturelementdefinition stößt, muss für Ihre letztere Frage entweder kein Mitglied mit diesem Namen vorhanden sein oder das Mitglied mit diesem Namen muss denselben Typ und Versatz haben. Ich würde vermuten, dass das gequietscht hätte, wenn eine nicht übereinstimmende Strukturelementdefinition existiert hätte, aber ich weiß nicht, wie es mit Fehlern umgegangen ist.
Superkatze
0

Ein einfaches und sehr nützliches Beispiel ist ....

Vorstellen:

Sie haben ein uint32_t array[2]und möchten auf das 3. und 4. Byte der Bytekette zugreifen. du könntest tun *((uint16_t*) &array[1]). Dies verstößt jedoch leider gegen die strengen Aliasing-Regeln!

Mit bekannten Compilern können Sie jedoch Folgendes tun:

union un
{
    uint16_t array16[4];
    uint32_t array32[2];
}

technisch ist dies immer noch ein Verstoß gegen die Regeln. Alle bekannten Standards unterstützen diese Verwendung.

Dhein
quelle