2013-08-09 5 views
6

ich eine Struktur wie unten haben zu werfen:Wie eine Struktur von 2 uint in ein Doppel

struct pts_t 
{ 
    uint32_t lsb; 
    uint32_t msb; 
}; 

Ich möchte werfen, dass sich in ein Doppel. Ist es schreibt sicher direkt:

pts_t t; 

double timestamp = t; 

und komplexer werden, wenn der Strukturtyp Teil eines C-DLL API ist, ohne „Verpackung“ Attributs (für alle Compiler) in diesem Fall ich die pts_t* kopieren erhalten durch die API zu pts_t instance ich erstelle, um das Struct Packing zu kontrollieren?

void f(pts_t* t) 
{ 
    pts_t myt; myt.lsb = t->lsb; myt.msb = t->msb; 

    double timestamp = *(double*)(&myt.lsb); 
} 
+4

'Doppelzeitstempel = (double) (* ((uint64_t *) &t));' – sgarizvi

+0

kann Ihnen ein Beispiel nennen? –

Antwort

3

Der erste Gedanke der schreiben wäre folgende:

double timestamp = *((double *) &(t.lsb)); 

durch diesen zu Schritt (vorausgesetzt, Sie sind in einer 32-Bit-Umgebung):

  1. Sie erhalten die Adresse des Bezeichners t.lsb, weil Sie die Speicheradresse des ersten Bytes in Ihrer Struktur finden müssen. Hinweis, alternativ können Sie auch &t tun.
  2. Sie werfen diese Speicheradresse dann als einen Zeiger auf ein Double (8 Bytes).
  3. Sie sind schließlich Dereferenzierung dieser Zeiger und Speicherung aller 8 Bytes in dem 8-Byte-Speicherblock, die die Kennung timestamp verwendet.

Bemerkung:

  1. Sie benötigen wenig/big Endian zu betrachten.
  2. Sie gehen davon aus, dass sowohl die Struktur als auch das Double korrekt ausgerichtet sind (wie unten erwähnt).
  3. Dies ist nicht portierbar, Sie gehen davon aus, dass ein Double 8 Bytes ist.

Jetzt sind die drei Punkte in der Anmerkung Klappentext viel zu sorgen. Es wird ein großer Schmerz, wenn dieser Code über mehrere Plattformen hinweg portiert wird. Wie unten erwähnt, ist die Verwendung von C unions eine viel bessere und korrekte Lösung, die tragbar ist.

Es wäre geschrieben werden, wie mit C folgt unions:

double timestamp = (union { double d; struct pts_t pts; }) { t } .d; 
+0

'pts_t' verwendet' uint32_t', also sollte es unabhängig von der Größe eines Standards 'int' funktionieren (obwohl der Compiler wahrscheinlich weniger Padding-Bits zwischen den Feldern einer 32-Bit- als einer 64-Bit-Umgebung einfügt)) – Virgile

+0

@Virgile, meine Annahme einer 32-Bit-Umgebung für meinen kurzen Klappentext war so, dass ich für ein Double 8 Byte angeben kann. Wenn ein Double mehr oder weniger als 8 Bytes wäre, würden Sie Probleme beim Zugriff auf Speicheradressen bekommen, die Sie nicht zugewiesen haben. –

+0

Dies ist ein undefiniertes Verhalten (was zu einem BUS-Fehler führt), wenn die Struktur und das Double unterschiedlich ausgerichtet sind. Tu das nicht. –

3

Sie haben

double timestamp = *((double*)(&t.lsb)); 

Sie schreiben sollte wie

struct __attribute__((__packed__)) pts_t { 
    ... 
}; 

verwenden, um sicherzustellen, dass Ihre Struktur verpackt ist (obwohl ich nicht, warum jeder Compiler würde Pad sehen etwas nach dem LSB in diesem Fall).

...

Eigentlich je nachdem, ob Ihre Plattform Big- oder littleEndian Sie könnten wechseln haben lsb und msb oder etwas tun, wie folgt aus:

double timestamp; 
double* p_timestamp = &timestamp; 
*((uint32_t*)p_timestamp) = t.msb; 
*(((uint32_t*)p_timestamp) + 1) = t.lsb; 
+0

Über struct Verpackung und wenn die pts_t als Zeiger durch eine C-DLL API übergeben wird, meine bearbeiten sehen. – alexbuisson

+0

Dies ist ein undefiniertes Verhalten (was zu einem BUS-Fehler führt), wenn die Struktur und das Double unterschiedlich ausgerichtet sind. Tu das nicht. –

+0

Dieser Code verstößt auch gegen Aliasing-Regeln. Wie in [Jens Gustedts Antwort] (http://stackoverflow.com/a/18142604/298225) eine Union verwendet wird, ist die korrekte Neuinterpretation von Bytes. –

4

Auch wenn Sie davon ausgehen, dass Ihre double 64 Bit breit sind, wenn Sie für tragbare Code suchen Sie wirklich damit vorsichtig sein sollten: Ihre Struktur und double haben möglicherweise andere Ausrichtungsbeschränkungen, und Ihr Compiler kann aufgrund von Aliasing-Regeln verwirrt werden. Ein Weg, um Probleme zu vermeiden mit diesem ist ein union

union { 
struct pts_t pts; 
double timestamp; 
} x = { .pts = t }; 

zu verwenden und dann x.timestamp zu verwenden.

Seien Sie auch vorsichtig, dass "Komponieren" double s wie das kann zu seltsamen Werten wie Unendlichkeiten führen, die Sie sonst nicht begegnen würden.

+0

@alexbuisson Dies ist die bessere Antwort – SheetJS

0

Sie schreiben: Das

struct pts_t 
{ 
    uint32_t lsb; 
    uint32_t msb; 
}; 

:

pts_t t; 
double timestamp = t; 

ist perfekt "sicher", in dem Sinne, dass es nicht kompiliert werden, so kann es nicht schaden. Sie haben keinen Typ pts_t definiert; Sie haben einen Typ struct pts_t definiert.

Dies:

struct pts_t t; 
double timestamp = t; 

auch nicht kompiliert werden, da Sie nicht (entweder explizit, mit einer Besetzung, oder implizit, mit einer Zuordnung) umwandeln können einen Wert eines Strukturtypen auf ein Objekt numerischer Typ

Ich schlage vor, dass Sie etwas Zeit gespart hätten, wenn Sie das vor dem Posten versucht hätten.

Wahrscheinlich der einfachste Ansatz ist memcpy() zu verwenden:

#include <assert.h> 
#include <string.h> 
/* ... */ 
struct pts_t t = { some_lsb_value, some_msbZ_value }; 
double timestamp; 
assert(sizeof t == sizeof timestamp); 
memcpy(&timestamp, &t, sizeof timestamp); 

Durch die Verwendung von memcpy() statt Zeiger wirft, Sie das Risiko eines falsch ausgerichteten Speicherzugriff vermeiden; Je nach System benötigt Ihre Struktur möglicherweise mehr oder weniger strenge Ausrichtung als double.

Es gibt auch keine Garantie, dass Ihre Struktur die gleiche Größe wie eine double hat. Die Struktur ist fast sicher 64 Bit, und double ist wahrscheinlich 64 Bit, aber keiner ist tatsächlich garantiert, und es tut nicht weh, Ihre Annahmen explizit zu machen.

, die offen bleibt die Frage, ob die Werte, die Sie in t.lsb und t.msb gespeichert haben, wenn sie zusammengenommen, eine Darstellung für einen gültigen double Wert bilden, insbesondere für den von Ihnen gewünschten Wert. Die Sprache sagt sehr wenig darüber aus, wie Fließkommatypen dargestellt werden. Insbesondere kann und kann die Endianz über verschiedene Systeme variieren. Es liegt an Ihnen, sicherzustellen, dass eine Neuinterpretation der Darstellung auf diese Weise sinnvoll ist - und Ihr Code ist wahrscheinlich nicht portabel.