Casting einer C-Struktur in eine andere

Lesezeit: 5 Minuten

Ich habe zwei identische (aber unterschiedlich benannte) C-Strukturen:

typedef struct {
      double x;
      double y;
      double z;
} CMAcceleration;


typedef struct {
    double x;
    double y;
    double z;   
} Vector3d;

Jetzt möchte ich einer Vector3d-Variablen eine CMAcceleration-Variable zuweisen (die gesamte Struktur kopieren). Wie kann ich das machen?

Ich habe Folgendes versucht, bekomme aber diese Compiler-Fehler:

vector = acceleration;           // "incompatible type"
vector = (Vector3d)acceleration; // "conversion to non-scalar type requested"

Natürlich kann ich darauf zurückgreifen, alle Mitglieder einzeln einzustellen:

vector.x = acceleration.x;
vector.y = acceleration.y;
vector.z = acceleration.z;

aber das scheint eher unpraktisch zu sein.

Was ist die beste Lösung?

  • Können Sie nicht einfach typedef (z. B. typedef struct CMAcceleration Vector3d) eingeben? Ups, da hatte ja schon jemand darauf hingewiesen…

    – Nyan

    22. Oktober 2010 um 11:37 Uhr


Benutzeravatar von Georg Schölly
Georg Scholly

Das ist Ihre einzige Lösung (abgesehen davon, es in eine Funktion zu packen):

vector.x = acceleration.x;
vector.y = acceleration.y;
vector.z = acceleration.z;

Sie könnten es tatsächlich so werfen (mit Zeigern)

Vector3d *vector = (Vector3d*) &acceleration;

aber das steht nicht in den Spezifikationen und daher hängt das Verhalten vom Compiler, der Laufzeit und dem großen Grünflächenmonster ab.

  • +1: Gute Antwort. Beschreibt sowohl die einzige Methode, die garantiert funktioniert, als auch die Methode, die normalerweise in der Praxis funktioniert, und den Grund, warum diese Methode technisch nicht definiert ist.

    – Oliver Charlesworth

    22. Oktober 2010 um 11:18 Uhr

  • +1 Ich würde nur hinzufügen, dass die Casting-Technik ziemlich verbreitet ist – es ist nicht so, als wäre es wirklich böse.

    – Prof. Falken

    22. Oktober 2010 um 11:41 Uhr

  • +1 zum Einpacken in eine Funktion. Selbst für so etwas Triviales lohnt es sich, eine Unterroutine zu erstellen.

    – Alesplin

    22. Oktober 2010 um 15:43 Uhr

  • Was passiert, wenn wir CMAcceleration deklarieren als struct { Vector3d vec; };? Dann haben CMAcceleration-Instanzen zuerst Vector3d sizeof(Vector3d) Byte. Würde das striktes Aliasing beim Pointer-Casting beseitigen?

    – holgac

    11. April 2015 um 9:03 Uhr

  • Dann müssten wir keine Zeiger mehr werfen. Wir konnten einfach direkt zuordnen vector = acc.vec;.

    – Hermann Döppes

    9. Januar 2016 um 10:40 Uhr

Sie könnten einen Zeiger verwenden, um die Typumwandlung durchzuführen;

vector = *((Vector3d *) &acceleration);

  • Es sei darauf hingewiesen, dass der Compiler nicht verpflichtet ist, dafür zu sorgen, dass beide Structs gleich gepackt und ausgerichtet sind.

    – Oliver Charlesworth

    22. Oktober 2010 um 11:10 Uhr

  • Dies ist aufgrund des strikten Aliasing ein undefiniertes Verhalten. cellperformance.beyond3d.com/articles/2006/06/…

    – Sicher

    22. Oktober 2010 um 11:12 Uhr

  • @Secure Das ist eine Schande, weil ich diese Technik verwenden möchte, um eine Struktur nicht zu kopieren, sondern tatsächlich zu aliasieren (den Typ zu ändern).

    – Michael

    22. Oktober 2014 um 2:40 Uhr

  • @Michael: Geben Sie an, dass bei Verwendung von gcc oder clang der Code mit kompiliert werden muss -fno-strict-aliasing flag (andere Compiler verwenden möglicherweise ein Flag mit demselben Namen oder sind weniger aggressiv in ihren Aliasing-Optimierungen als gcc oder clang).

    – Superkatze

    12. September 2017 um 14:58 Uhr

Benutzeravatar von groovingandi
groovingandi

memcpy(&vector, &acceleration, sizeof(Vector3d));

Bitte beachten Sie, dass dies nur funktioniert, wenn das physikalische Layout der Strukturen im Speicher identisch ist. Wie @Oli darauf hingewiesen hat, ist der Compiler jedoch nicht verpflichtet, dies sicherzustellen!

  • Es sei darauf hingewiesen, dass der Compiler nicht verpflichtet ist, dafür zu sorgen, dass beide Structs gleich gepackt und ausgerichtet sind.

    – Oliver Charlesworth

    22. Oktober 2010 um 11:17 Uhr

  • @Oli Charlesworth: Du hast Recht und ich habe die Antwort entsprechend aktualisiert

    – groovingandi

    22. Oktober 2010 um 11:33 Uhr

  • @OliverCharlesworth: Es würde einen pathologisch perversen Compiler brauchen, um diese Annahme zu brechen, besonders. unter Berücksichtigung der akzeptierten Antwort auf diese Frage: stackoverflow.com/questions/19804655/…

    – chqrlie

    12. September 2017 um 19:46 Uhr

  • @chqrlie Die von Ihnen verlinkte akzeptierte Antwort besagt nur, dass ein Compiler sicherstellen muss, dass das Layout gleich ist, wenn beide Strukturen als Teil derselben Vereinigung verwendet werden. Aber es sagt nichts darüber aus, dass Strukturen außerhalb eines Union-Kontexts verwendet werden. So kann ein Compiler in einem Union-Fall ein gleiches Layout erzwingen, da er gemäß Standard, aber in anderen Fällen aufgrund einer seltsamen plattformspezifischen Optimierung beispielsweise unterschiedliche Layouts auswählen muss.

    – Mecki

    5. Juni 2018 um 14:26 Uhr

Benutzeravatar von sharptooth
scharfer Zahn

Sie verwenden dafür eine Hilfsfunktion:

void AccelerationToVector( struct CMAcceleration* from, struct Vector3d* to )
{
     to->x = from->x;
     to->y = from->y;
     to->z = from->z;
}

Warum benutzt du nicht.

typedef CMAcceleration Vector3d;

(anstatt eine ganz neue Struktur zu erstellen)

In diesem Fall vector = acceleration; lässt sich prima kompilieren.

  • Ich bekomme ein warning: 'typedef struct Vector3d Vector3d' does not refer to the unqualified type, so it is not used for linkage. Auch in diesem Fall CMAcceleration befindet sich in einem schwach verknüpften Framework, daher verzichte ich darauf, es in meiner .h-Datei zu verwenden.

    – Ortwin Gentz

    22. Oktober 2010 um 11:50 Uhr

  • Wenn die CMAcceleration struct aus einem separaten Framework stammt, sollten Sie am besten die Feld-für-Feld-Kopie anstelle der Memcpy- oder Tippspiel-Tricks durchführen, um Ihren Code für den Fall zukünftiger Änderungen im anderen Framework robust zu machen. (Auch wenn Sie wissen, dass die Strukturlayouts heute identisch sind, werden sie in späteren Versionen möglicherweise nicht so bleiben.)

    – David Gelhar

    22. Oktober 2010 um 13:03 Uhr

Dies wird leicht erreicht durch a Union:

typedef struct {
      double x;
      double y;
      double z;
} CMAcceleration;

typedef struct {
    double x;
    double y;
    double z;
} Vector3d;

typedef union {
    CMAcceleration acceleration;
    Vector3d vector;
} view;

int main() {
    view v = (view) (Vector3d) {1.0, 2.0, 3.0};
    CMAcceleration accel = v.acceleration;
    printf("proof: %g %g %g\n", accel.x, accel.y, accel.z);
}

  • Ich bekomme ein warning: 'typedef struct Vector3d Vector3d' does not refer to the unqualified type, so it is not used for linkage. Auch in diesem Fall CMAcceleration befindet sich in einem schwach verknüpften Framework, daher verzichte ich darauf, es in meiner .h-Datei zu verwenden.

    – Ortwin Gentz

    22. Oktober 2010 um 11:50 Uhr

  • Wenn die CMAcceleration struct aus einem separaten Framework stammt, sollten Sie am besten die Feld-für-Feld-Kopie anstelle der Memcpy- oder Tippspiel-Tricks durchführen, um Ihren Code für den Fall zukünftiger Änderungen im anderen Framework robust zu machen. (Auch wenn Sie wissen, dass die Strukturlayouts heute identisch sind, werden sie in späteren Versionen möglicherweise nicht so bleiben.)

    – David Gelhar

    22. Oktober 2010 um 13:03 Uhr

Benutzeravatar von lundblade
Lundklinge

Eine andere Version der Hilfsfunktion, die C99 verwendet:

static inline struct Vector3d AccelerationToVector(struct CMAcceleration In)
{
    return (struct Vector3d){In.x, In.y, In.z};
}

Wenn die Compiler-Optimierung aufgedreht ist (z. B. -Os), sollte dies beim Aufrufen zu absolut keinem Objektcode werden.

1410490cookie-checkCasting einer C-Struktur in eine andere

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy