Daten aus einem struct kopieren, gibts da nen Trick?
-
Wenn zwischen den struct-doubles keine Paddingbytes vorkommen (was man portabel mit offsetof prüfen kann), geht einfach memcpy
oder struct-copy:typedef struct{double x[6];} Copyst; ... Type_Measured t; double a[6]; *(Copyst*)a=*(Copyst*)&t.Value1;
-
Den anderen Antworten nach zu urteilen, habe ich Dich wohl mißverstanden:
Du willst nicht statistic() aufrufen fuer
obj1.value1, obj2.value1, ..., objN.value1
,
sondern fuerobj1.value1, obj1.value2, ..., obj1.valueN
?
-
Hallo!
Um etwas Klarheit reinzubringen:
in dem Struct liegen eine Reihe von Meßwertreihen.
Die Funktion Statistics wertet jeweils immer nur eine solche Reihe aus.
Bisherige vorgehensweise:hingeschrieben, NICHT rauskopiert, daher eher Pseudocode...
typedef struct{ //... int Type; int UserCode; char Label[LABELLEN]; double Value1; double Value2; double Value3; double Value4; double Value5; double Value6; //... } Type_Measured; int NumOfTimeSteps =0; double* Values =NULL; /* hier die Speicherallozierung usw. */ for (i=0;i<NumOfTimesteps;i++){ Values[i] = Measured[i].Value1; } rc = Statistics(...,NumOfTimeSteps,Values,...); // Ausgabe der statistischen Werte for (i=0;i<NumOfTimesteps;i++){ Values[i] = Measured[i].Value2; } rc = Statistics(...,NumOfTimeSteps,Values,...); // Ausgabe der statistischen Werte //usw. für Value3, Value4,...
Sind eben viele for-Schleifen und beim testen sind die Datensätze ja eher klein...
Daher die Frage, ob es evt. schneller geht.
Am struct darf ich nicht mehr viel rumfummeln und an Statistics eher gar nicht!Ciao
OkkaPapa
-
Das mit dem "einfach die Adresse auf das erste
double
-Element als Zeiger verwenden" fällt dir spätestens dann auf die Füße, wenn du ein Alignment von 16 verwendest. Dann hast du nach jedemdouble
-Element ein leeres double-Element. Wenn du allerdings mit einem Alignment von 8 oder weniger kompilierst, bist du gerettet.
Falls aber irgendwann mal entschieden werden sollte, auflong double
umzusteigen, welches 10 Bytes benötigt, geht das wieder kaputt. Hast du dann 2 (4-Byte-Padding), oder 6 (8-Byte-Padding), oder überhaupt kein Byte (Padding deaktiviert), welches nachfolgend kommt und nicht verwendet wird?Ob Paddingoptionen für 32-Byte existieren, weiß ich nicht, hängt wahrscheinlich vom Compiler ab. Ist natürlich superschlau gewesen, die Elemente nicht direkt als Array zu deklarieren, da wären die Elemente hintereinander, oder der Compiler würde sich darum kümmern, dass der Zugriff trotzdem wie erwartet funktioniert.
Und noch eine kleine Bemerkung: mach bloß keine Funktion daraus, die die Adresse des Arrays zurückgibt, welches du befüllt hast. Sowas kannst du nur auf zwei Arten lösen: entweder über
malloc
, und das ist langsam und unnötig, oder über ein lokales Array auf dem Stack, und da ist es reines Glück, wenn das funzt.Lass dir den Speicherbereich als Parameter übergeben:
typedef struct { int Type; int UserCode; char Label[LABELLEN]; /*Warum kein Array? Ernsthaft ...*/ double Value1; double Value2; double Value3; double Value4; double Value5; double Value6; }Type_Measured; /*int ist hier im Grunde errno_t, ist aber nicht überall definiert. int passt **da schon.*/ inline int fillMyArray(Type_Measured*obj,double*arr) { if(!obj || !arr) return EFAULT; arr[0]=Value1; arr[1]=Value2; arr[2]=Value3; arr[3]=Value4; arr[4]=Value5; arr[5]=Value6; /*Alles in Ordnung.*/ return 0; } void calcMyStuff(Type_Measured*obj) { /*Speicherreservierung uebernimmt die aufrufende Funktion*/ double myValues[6]; if(fillMyArray(obj,myValues)) { /*Hat nicht so ganz geklappt - Fehlerbehandlung*/ } /*Ansonsten weiter im Text*/ }
Oer mach daraus direkt ein Marko.
-
OkkaPapa schrieb:
Sind eben viele for-Schleifen und beim testen sind die Datensätze ja eher klein...
Daher die Frage, ob es evt. schneller geht.
Am struct darf ich nicht mehr viel rumfummeln und an Statistics eher gar nicht!Ich verstehe.
Aber Du wirst ja wahrscheinlich eher selten einen Messpunkt wieder verändern. Eher einen Haufen Messpunkte speichern und danach da durchpflügen wollen...Deswegen nochmal schematisch mein Ansatz, der aus Deinem "Array of Struct" ein "Struct of Arrays" macht.
typedef struct{ // wie gehabt } Type_Measured; struct series{ int *type; int *usercode; char *label[LABELLEN]; double *value1; double *value2; int len; // Anzahl d. Eintraege }; struct series* get_series(){ /* Konstruktor */ } void free_series(struct series* s){ /* destruktor */ } void push_back(const Type_Measured* m, struct series* s){ /* auseinanderpfluecken & anfuegen */ } // wenn Du wirklich einen einzelnen Punkt brauchst... Type_Measured get(int i, const struct series *s){ assert(i<s->len); Type_Measured result = {s->type[i], s->usercode[i], {0}, ///... }; memcpy(result.Label, s->label[i], LABELLEN); return result; } int stat1(const struct series *s){ return Statistic(/*....*/, s->values1, s->len); /* Baa-am! */ }
-
Goddamn it!
Die Deklaration vonseries.label
ist falsch...
-
Kann das bitte jmd. etwas genauer erklären, was hier passiert?
typedef struct{double x[6];} Copyst; ... Type_Measured t; double a[6]; *(Copyst*)a=*(Copyst*)&t.Value1;
-
unsure110 schrieb:
Kann das bitte jmd. etwas genauer erklären, was hier passiert?
Sehr schmutzige Dinge!
-
unsure110 schrieb:
Kann das bitte jmd. etwas genauer erklären, was hier passiert?
typedef struct{double x[6];} Copyst; ... Type_Measured t; double a[6]; *(Copyst*)a=*(Copyst*)&t.Value1;
Kannst du genauer erklären, was du da dran nicht verstehst?
Das einzige, was da "passiert", ist das Gleichheitszeichen, also eine Zuweisung. Von t.Value1 auf a. Der Rest ist Syntaxgeschwurbel, damit der Compiler diese Sprachvergewaltigung ohne Murren akzeptiert.
-
... und anstatt zu erklären, was da passiert, wird nur gesagt, dass es schlecht ist ...
Also gut, ich versuch's mal:
a[6]
wird nicht gepadded. Die Elemente von Arrays werden grundsätzlich nicht gepadded. C99 verbietet das sogar. Der Grund dafür ist, dass wenn du die Elemente eines Arrays indizierst, im Grunde immer nur der Index * sizeof(Typ des Arrays) + Speicheradresse des ersten Arrayelementes gerechnet wird. Der Zugriff muss halt einheitlich erfolgen.Also hast du 6 * sizeof(double) (meistens
Bytes == 48 Bytes insgesamt, in die du schreiben kannst. Die Werte in dem Struct sind aber keine Arrays, sondern einzelne Werte - heißt, der Compiler darf und kann da Padding einfügen. Das Struct ist dann 6 * 8 Bytes + <Padding> == 48 + <Padding>. Und das "+ <Padding>" kann dir dann so richtig schön auf die Füße fallen. Und Programme willst du in der Regel so deterministisch wie möglich ablaufen lassen, weil sonst das Debugging einen Heidenspaß macht (nur nicht für dich).
Also ein wenig deutlicher:
struct a{char x[48];} a1; struct b{char x[48+padding];} b1; /*padding kann 0 sein, oder auch nicht.*/ a1=*(struct a*)&b1; /*Kann Überlauf verursachen oder auch nicht.*/
Kurz: Lass es.
-
Insgesamt ist es aber eine ziemlich gute Wette, dass ein Compiler kein Padding zwischen den Membern eines structs der Form
struct { Foo foo1, foo2, foo3, /* ... */ fooN; };
einführen wird. Egal wie abgefahren Foo ist. Und erst recht nicht, wenn es primitive Datentypen sind. Auch wenn dieses Verhalten streng genommen aus den Regeln der Sprache so nicht hervor geht.
-
Übrigens habe ich oben ein wenig scheiße erzählt - hätte mir auch vorher klar sein können:
#include <stdio.h> #include <stdint.h> struct a{char x[48];} a1; struct b{char x[56];} b1; int main(void) { size_t i; /*Speicher befuellen.*/ for(i=0;i<56;i++) b1.x[i]=i; /*Zuweisung.*/ a1=*(struct a*)&b1; /*WARNUNG: kann undefiniertes Verhalten hervorrufen. In der Regel ist **zwar genug Platz im globalen Datensegment, aber man weiss ja nie ... ***/ for(i=48;i<56;i++) printf("%i|",a1.x[i]); puts(""); return 0; }
Ausgabe:
0|0|0|0|0|0|0|0|
Heisst, der Compiler passt schon auf, dass es nicht zu einem Überlauf kommt. Sonst wäre an dieser Stelle die Zahlen 48 bis 55 erschienen. Aber das Problem, dass du plötzlich undefinierte Werte in deinem Array haben kannst, besteht natürlich immer noch.
-
Was soll da dran ungewöhnlich oder überraschend sein? Du kopierst ein struct a, natürlich wird da nur so viel kopiert, wie ein struct a groß ist.
-
Ja, das ist mir jetzt auch klar.
Hatte aber vorher die wirre Idee, dass durch den Zeiger-cast kein temporäres Objekt erstellt wird, sondern die Werte 1:1 geschrieben werden.