Moin,
Nach meinem dafuerhalten:
1.) Am besten garnicht.
...
...
10001.) immernoch nicht.
10002.) memcpy(&ds, da, 3 * sizeof(int));
10003.) irgendwas mit ner union, die die beiden verschiedenen Typen
zusammenmauschelt.
Gruss
WK
Wenn es unbeding sein muss: Mach ds zu einem pointer und caste zumindest
ein mal über char *.
Ungefähr so:
1
structdat_struct
2
{
3
inta,b,c;
4
};
5
6
structdat_struct*ds;
7
typedefintdat_array[3];
8
dat_arrayda={1,2,3};
9
10
voidtest(void)
11
{
12
ds=(structdat_struct*)(char*)&da;
13
}
Der cast über char * sieht zwar nutzlos aus, sorgt aber dafür, das dem
Compiler das aliasing der Daten erklärt wird. Er darf zum Beispiel nicht
mehr davon ausgehen, das Änderungen in 'da' keine Auswirkungen auf *ds
haben.
Wenn Du es irgendwie anders lösen kannst, dann lös es anders. Es
funktioniert zwar, aber solcher Code ist heikel weil er die
Typensicherung komplett außer Kraft setzt und davon ausgeht, das die
struct member in dat_struct exakt zum Array passen.
Michael schrieb:> 10002 verwende ich auch. Eine Notlösung, die überflüssige Laufzeit> generiert, obwohl doch zum Zeitpunkt des Kompilierens bereits alles> bekannt ist.
Nicht auf vernünftigen Compilern. Die wissen was memcpy macht und
optimieren das entsprechend.
Michael schrieb:> Wie mache ich in C einen Typecast, mit dem ich ein Array in eine> Struktur umwandle?>>
1
>structdat_struct
2
>{
3
>inta,b,c;
4
>};
5
>
6
>structdat_structds;
7
>
8
>typedefintdat_array[3];
9
>
10
>dat_arrayda={1,2,3};
11
>
12
>ds=(???)da;
13
>
ds = *(struct dat_struct*)da;
da ist ja schon die Adresse des Feldes, und das Alignment ist identisch,
macht also keine Probleme.
oder
memcpy(&ds, da, sizeof(da));
memcpy mit konstanter Größe wird bei modernen Compilern mit -O2 auch
wegoptimiert.
Michael schrieb:> ds = (???) da;
Der Zuweisungsoperator deutet darauf hin, dass du eine Kopie von da
mit einem anderen Datentyp (nämlich (struct dat_struct)) anlegen
möchtest. Das geht am besten mit memcpy:
Dergute W. schrieb:> 10002.) memcpy(&ds, da, 3 * sizeof(int));
Ich habe mir mal angewöhnt, als drittes Argument von memcpy immer die
Größe des Zielobjekts (und nicht des Quellobjekts) anzugeben. Das
vermeidet das Beschreiben von Speicherbereichen außerhalb des
Zielobjekts, wenn das Quellobjekt versehentlich größer als das
Zielobjekt ist:
1
memcpy(&ds,da,sizeof(structdat_struct));
Max H. schrieb:> ds = (struct dat_struct*) (char *) &da;
Der C-Standard verbietet es, auf das int-Array über den L-Value des
nichtkompatiblen Typs (struct dat_struct) zuzugreifen. An dieser Regel
ändert auch die mehrfache Casterei nichts (Kapitel 6, Abschnitt 5,
Absatz 7). Für memcpy gilt diese Einschränkung nicht.
Yalu X. schrieb:> Ich habe mir mal angewöhnt, als drittes Argument von memcpy immer die> Größe des Zielobjekts (und nicht des Quellobjekts) anzugeben. Das> vermeidet das Beschreiben von Speicherbereichen außerhalb des> Zielobjekts, wenn das Quellobjekt versehentlich größer als das> Zielobjekt ist:
Dafür liest man halt über das Ende des Quellobjekts hinaus, falls es
versehentlich kleiner ist. Das ist auch nicht so viel besser.
> memcpy(&ds, da, sizeof (struct dat_struct));
Warum nicht gleich
Yalu X. schrieb:> Der C-Standard verbietet es, auf das int-Array über den L-Value des> nichtkompatiblen Typs (struct dat_struct) zuzugreifen. An dieser Regel> ändert auch die mehrfache Casterei nichts (Kapitel 6, Abschnitt 5,> Absatz 7). Für memcpy gilt diese Einschränkung nicht.
Auf welchen Standard beziehst du dich da genau, und bist du dir sicher?
Das ist gängige Praxis seitdem es C gibt, und ich sehe keinen
technischen Grund warum das ein Problem sein soll, solange das Alignment
passt.
Der Hack mit memcpy() funktioniert zwar, setzt aber einen modernen
Compiler mit aufgedrehten Optimierungen voraus und funktioniert in einer
Freestanding Umgebung nicht. Der ASM Code ist jedenfalls identisch.
Rolf M. schrieb:> Yalu X. schrieb:>> Ich habe mir mal angewöhnt, als drittes Argument von memcpy immer die>> Größe des Zielobjekts (und nicht des Quellobjekts) anzugeben. Das>> vermeidet das Beschreiben von Speicherbereichen außerhalb des>> Zielobjekts, wenn das Quellobjekt versehentlich größer als das>> Zielobjekt ist:>> Dafür liest man halt über das Ende des Quellobjekts hinaus, falls es> versehentlich kleiner ist. Das ist auch nicht so viel besser.
Nicht viel, aber vielleicht ein wenig, weil Lesen normalerweise
zerstörungsfrei ist.
>> memcpy(&ds, da, sizeof (struct dat_struct));>> Warum nicht gleich memcpy(&ds, da, sizeof ds);
Ja, natürlich. Ich habe mich durch das
1
3*sizeof(int)"
im zitierten Programmcode blenden lassen. Auch dort hätte man einfacher
1
sizeofda
schreiben können.
Udo K. schrieb:> Yalu X. schrieb:>> Der C-Standard verbietet es, auf das int-Array über den L-Value des>> nichtkompatiblen Typs (struct dat_struct) zuzugreifen. An dieser Regel>> ändert auch die mehrfache Casterei nichts (Kapitel 6, Abschnitt 5,>> Absatz 7). Für memcpy gilt diese Einschränkung nicht.>> Auf welchen Standard beziehst du dich da genau, und bist du dir sicher?
Auf jeden von ISO bzw. ANSI. In C90 stand die Regel allerdings noch in
Kapitel 6, Abschnitt 3, Absatz 6.
> Das ist gängige Praxis seitdem es C gibt, und ich sehe keinen> technischen Grund warum das ein Problem sein soll, solange das Alignment> passt.
Die Regel dient nicht nur dazu, Alignment-Probleme zu vermeiden, sondern
dem Compiler Annahmen zu möglichem bzw. unmöglichem Aliasing zu
erlauben, um damit den Code besser optimieren zu können.
Udo K. schrieb:> Das ist gängige Praxis seitdem es C gibt, und ich sehe keinen> technischen Grund warum das ein Problem sein soll, solange das Alignment> passt.> Der Hack mit memcpy() funktioniert zwar, setzt aber einen modernen> Compiler mit aufgedrehten Optimierungen voraus und funktioniert in einer> Freestanding Umgebung nicht. Der ASM Code ist jedenfalls identisch.
Jede der Lösungen ist ein Hack. memcpy() ist davon meiner Ansicht nach
noch der sauberste. So modern muss der Compiler dafür übrigens auch
nicht sein. gcc macht das schon seit mindesten 25 Jahren.
Rolf M. schrieb:> gcc macht das schon seit mindesten 25 Jahren.
Alles was nach C99 kam, ist hoch modern, nicht original, nicht
vertrauenswürdig und daher in ersthaften Projekten "verboten" - könnte
man meinen.
Ich stelle mir vor, wie diese Leute auf dem Trödel verzweifelt nach
Schellack Platten für ihr Grammophon suchen.
Wenn das mein Code für irgendein Projekt zu Hause wäre, würde ich wohl
den hack mit memcpy nehmen und einen Kommentar dazu schreiben, damit ich
mich in 3 Jahren noch daran erinnere, nicht blind eine der beiden
Datenstrukturen zu ändern zu können, ohne die Zuweisung zu überarbeiten.
Auf der Arbeit, in einem Projekt, das andere warten müssen, wäre mir das
zu peinlich. Da würde ich
ds.a=da[0];
ds.b=da[1];
ds.c=da[2];
schreiben. Und in beiden Fällen würde ich noch ein
assert(sizeof(ds)==sizeof(da))
mit einem passenden Kommentar hinzufügen. Code hält manchmal länger als
man glaubt, da freut sich Dein Nachfolger.
Geht es Dir
* um die Kopie (Zuweisung)? Dann ist memcpy gut genug.
* um einen cast? Das geht über Ptr. Ist nur „frimelei“, probiers aus
und frag erneut mit Deiner Lösung
* um die Zulässigkeit des casts (oder memcpy oder per Union)? Damit
kannst Du Bücher füllen. Viele tun es einfach erfolgreich. Und viele
fallen irgendwann drauf rein
Mir gehts um den Cast.
Das dahinter liegende praktische Problem ist, dass ich eine komplexe
Struktur mit Sollwerten füllen möchte, die ich in einem const uint8_t
Array abgelegt habe. Bislang habe ich dazu zwei Möglichkeiten in
Verwendung, eine union, die beide Datentypen enthält, und memcpy.
Allerdings interessiert mich wirklich, wie man es mit einem type cast
macht, da habe ich noch Lernbedarf.
Michael schrieb:> Das dahinter liegende praktische Problem ist, dass ich eine komplexe> Struktur mit Sollwerten füllen möchte, die ich in einem const uint8_t> Array abgelegt habe.
Das Problem alignment wurde ja nun schon öfter genannt. Das kann dir
deinen Plan verhageln, wenn du nicht genau weißt, was du da tust.
Wie man es mit einem cast richtig macht, hat Yalu schon beschrieben.
Yalu X. schrieb:> Der C-Standard verbietet es, auf das int-Array über den L-Value des> nichtkompatiblen Typs (struct dat_struct) zuzugreifen. An dieser Regel> ändert auch die mehrfache Casterei nichts (Kapitel 6, Abschnitt 5,> Absatz 7). Für memcpy gilt diese Einschränkung nicht.
In Kurzform: gar nicht.
Oliver
Moin,
Nach mal drueberschlafen find ich auch die Loesung von timgabinski am
vernuenftigsten.
Bei einer union mit 2 verschiedenen Datentypen ist's iirc eigentlich
auch schon eine Suende, wenn man da den einen reinschreibt und den
anderen ausliest.
casten mit Gewalt geht auch, ist aber unschoen:
1
printf("%d\n",((structdat_struct*)da)->b);
Das Problem ist da immer, dass du dich nicht verlassen kannst, wie der
compiler den struct im Speicher anlegt. Kann auch sein, dass der padding
bytes zwengs besserem alignment einbaut, wenn dein struct verschieden
grosse Elemente hat. Dann kommst du in die Hoelle...Und wenn du
versuchst, ihn mittels irgendwelcher wilden #pragma davon abzubringen,
erst recht.
Gruss
WK
Moin,
Michael schrieb:> in der> Hoffnung, dadurch eventuelle Probleme mit Alignment und Cell Padding zu> vermeiden.
Diese Hoffnung kannst du getrost fahren lassen...
Dagegen hilft nur die timgabinski-Methode.
Gruss
WK
Michael schrieb:> Das dahinter liegende praktische Problem ist, dass ich eine komplexe> Struktur mit Sollwerten füllen möchte, die ich in einem const uint8_t> Array abgelegt habe.
Dann mach das händisch wie im
Beitrag "Re: C typecast array nach struct" beschrieben. Ist
zwar mehr Tipparbeit und braucht auch zur Ausführungszeit ein paar Takte
mehr. Aber du macht es ja wohl nur einmal.
Tim schrieb:> Auf der Arbeit, in einem Projekt, das andere warten müssen, wäre mir das> zu peinlich. Da würde ich>> ds.a=da[0];> ds.b=da[1];> ds.c=da[2];>> schreiben.
Der ganz saubere Weg wäre, auch in die einzelnen member per memcpy zu
kopieren.
Aus cppreference
„ Where strict aliasing prohibits examining the same memory as values of
two different types, memcpy may be used to convert the values.“
Oliver
Dergute W. schrieb:> Bei einer union mit 2 verschiedenen Datentypen ist's iirc eigentlich> auch schon eine Suende, wenn man da den einen reinschreibt und den> anderen ausliest.
Abgesehen davon ist das der umständlichste Weg, weil man dann extra für
die Konvertierung erstmal noch einen neuen Datentyp definieren muss,
dann erst den Input rein und den Output wieder rauskopieren.
> Das Problem ist da immer, dass du dich nicht verlassen kannst, wie der> compiler den struct im Speicher anlegt. Kann auch sein, dass der padding> bytes zwengs besserem alignment einbaut, wenn dein struct verschieden> grosse Elemente hat.
Dann sprechen wir aber eh von einer anderen Sache, weil dann die
Elemente des Arrays ja auch nicht mehr alle vom selben Typ sein können
wie die der Struct.
Oliver S. schrieb:> Tim schrieb:>> Auf der Arbeit, in einem Projekt, das andere warten müssen, wäre mir das>> zu peinlich. Da würde ich>>>> ds.a=da[0];>> ds.b=da[1];>> ds.c=da[2];>>>> schreiben.>> Der ganz saubere Weg wäre, auch in die einzelnen member per memcpy zu> kopieren.
Wenn ich den Wert eines int in einen anderen kopieren will, warum sollte
da memcpy besser sein als eine einfache Zuweisung?
Michael schrieb:> Das dahinter liegende praktische Problem ist, dass ich eine komplexe> Struktur mit Sollwerten füllen möchte, die ich in einem const uint8_t> Array abgelegt habe.
Wieso legst du die Sollwerte in einem const uint8_t Array ab und nicht
gleich im passenden Datentyp, nämlich als struct dat_struct? Durch das
Vermischen von untereinander inkompatiblen Datentypen machst du dir das
Leben doch nur unnötig schwer.
Yalu X. schrieb:> Wieso legst du die Sollwerte in einem const uint8_t Array ab und nicht> gleich im passenden Datentyp, nämlich als struct dat_struct?
Wenn ich raten müßte: weil sie so im EEPROM liegen.
Und das EEPROM ist ja schließlich ein Array von uint8_t !!1zwölf
Michael schrieb:> die ich in einem const uint8_t> Array abgelegt habe
Warum denn ein "const"-Array?
Dann kann man auch gleich eine struct-Variable initialisieren.
Axel S. schrieb:> Yalu X. schrieb:>> Wieso legst du die Sollwerte in einem const uint8_t Array ab und nicht>> gleich im passenden Datentyp, nämlich als struct dat_struct?>> Wenn ich raten müßte: weil sie so im EEPROM liegen.> Und das EEPROM ist ja schließlich ein Array von uint8_t !!1zwölf
Dann könnte es auch mit dem cast auf "(uint8_t*)" funktionieren, wenn
man einen Compiler hat, der sowas wie "#pragma(pack,1)" unterstützt.
Wenn er das nicht kann, muss man das Padding etweder händisch erledigen,
oder man hat Leerstellen im EEPROM.
Rahul D. schrieb:> Axel S. schrieb:>> Yalu X. schrieb:>>> Wieso legst du die Sollwerte in einem const uint8_t Array ab und nicht>>> gleich im passenden Datentyp, nämlich als struct dat_struct?>>>> Wenn ich raten müßte: weil sie so im EEPROM liegen.>> Dann könnte es auch mit dem cast auf "(uint8_t*)" funktionieren, wenn> man einen Compiler hat, der sowas wie "#pragma(pack,1)" unterstützt.
Oder aber er verläßt sich auf das, was auch mit dem dümmsten Compiler
schon geht. Er kopiert die struct Byte für Byte. Und die Länge ermittelt
er mit sizeof(). Dann klappt das auch mit Padding und Pipapo.
Daß es nicht portabel ist, weil implemetation defined behavior, kann ihm
dabei egal sein. Er liest und schreibt ja auf der gleichen Architektur.
Rolf M. schrieb:> Wenn ich den Wert eines int in einen anderen kopieren will, warum sollte> da memcpy besser sein als eine einfache Zuweisung?
Hier gehts um ein uint8_t Array, welches zu größere Datentypen
zusammengesetzt wird.
Oliver
Michael schrieb:> Ich lege du die Sollwerte in einem const uint8_t Array ab, weil ich> nicht schreiben kann
Dass du nicht schreiben kannst, sieht man an dem Satz. SCNR
Wie wäre es, wenn du dich mal etwas weniger abstrakt äußern würdest?
Zur Programmierumgebung hast du doch auch noch nichts geschrieben. Ist
das alles so geheim?
Michael schrieb:> Wie mache ich in C einen Typecast, mit dem ich ein Array in eine> Struktur umwandle
Gar nicht, denn je nach Plattform, Compiler und Compileroptionen
(Optimierungen, Alignment, Debug) kommt eine andere Bytebelegung des
Arrays bei raus, sogar ggf. eine andere Grösse char x[sizeof(struct s)].
Man schreibt schön
1
ds.a=da[0];
2
ds.b=da[1];
3
ds.c=da[2];
und lässt das den compiler ggf. optimieren.
Obwohl deine gleichen Datentxpen wohl glatt gehen
Aber schon mit
Michael schrieb:> Ich lege du die Sollwerte in einem const uint8_t Array ab, weil ich> nicht schreiben kann> ds = 0x112233aabbcc;
Man kann aber bspw. schreiben:
Michael schrieb:> Es hilft nur weiter, wenn die Struktur so einfach wie in meinem> Beispiel> ist.
Sehen wir denn die original Daten/Arrays/Strukturen, oder nichts sagende
Fantasiegebilde?
Kann ich Strukturen in einem Binärfile abspeichern und wieder einlesen,
ohne dass ich Probleme mit Alignment, Cell Padding o. ä. bekomme? Das
scheint zu funktionieren, aber nach dem oben gesagten bin ich mir nicht
mehr sicher, ob das nur Zufall ist.
Michael schrieb:> Kann ich Strukturen in einem Binärfile abspeichern und wieder> einlesen,> ohne dass ich Probleme mit Alignment, Cell Padding o. ä. bekomme? Das> scheint zu funktionieren, aber nach dem oben gesagten bin ich mir nicht> mehr sicher, ob das nur Zufall ist.
Wenn Schreiben und lesen mit dem gleichen µC passiert.
Lese und Schreibe Programm mit dem gleichen Compiler(Version) übersetzt
wurden.
Mit den gleichen Compilereinstellungen.
Sonst? Vielleicht, vielleicht auch nicht.
Michael schrieb:> Kann ich Strukturen in einem Binärfile abspeichern und wieder einlesen,> ohne dass ich Probleme mit Alignment, Cell Padding o. ä. bekomme?
Nein.
Liest du dieselbe Struktur mit demselben Programm, geht es noch, aber
wenn das Programm mit anderen Compileroptionen neu kompiliert wird oder
gar auf einer anderen Plattform läuft, geht das in die Hose.
Yalu X. schrieb:> Auf jeden von ISO bzw. ANSI. In C90 stand die Regel allerdings noch in> Kapitel 6, Abschnitt 3, Absatz 6.
Sorry, ich bin zu blöd um das zu finden. In meiner ANSI/ISO 9899-1990
Kopie steht unter 6.3.6 "Additive Operators" nichts dazu - oder mein
Hirn denkt nicht in den Wegen des Standards.
Udo K. schrieb:> memcpy(&ds, da, sizeof(da));
Das C nicht einfach ist, sieht man auch am memcpy:
sizeof(da) ist im folgenden Beispiel in 50% der Fälle falsch.
Merke: mache nie ein typedef mit einem Array fixer Grösse - nimm gleich
ein typedef struct { int dat_array[3]; } dat_array_t;
1
typedef int dat_array[3];
2
3
void foo(dat_array da)
4
{
5
// Surprise
6
printf("%d\n", (int)sizeof(da));
7
}
8
9
int main()
10
{
11
dat_array da;
12
13
printf("%d\n", (int)sizeof(da));
14
foo(da);
15
}
Rolf M. schrieb:> Jede der Lösungen ist ein Hack. memcpy() ist davon meiner Ansicht nach> noch der sauberste. So modern muss der Compiler dafür übrigens auch> nicht sein. gcc macht das schon seit mindesten 25 Jahren.
Hängt halt davon ab wo und mit welchen Tools du arbeitest.
Ich verwende gerne eine eigene memcpy Funktion, und damit geht der Hack
schon nicht mehr. Ich will auch nicht, dass der Compiler nach Gutdünken
Funktionen einbaut oder entfernt.
Und der MS Compiler vom Driver Development Kit ist erst 15 Jahre alt,
kann aber den memcpy Hack nur mit voll aufgedrehten Optimierungen - was
schlecht ist wenn ich kleine Programme möchte.
Ich weiss Linuxer verwenden überall und immer -O3 :-).
Wenn du in einer Freestanding Umgebung arbeitest, dann geht der memcpy
Hack auch nicht.
Wenn du aber mit einem OS arbeitest, das Structs und Arrays in
unterschiedlichen Segmenten abspeichert, dann geht nur memcpy.
Nemopuk schrieb:> Alles was nach C99 kam, ist hoch modern, nicht original, nicht> vertrauenswürdig und daher in ersthaften Projekten "verboten" - könnte> man meinen.
Zumindest lassen sich 25 Jahre alte C99 Programme auch heute noch
relativ schmerzfrei übersetzen. Probiere das mal mit modernem C++ Zeugs,
da kannst du froh sein wenn das nach 5 Jahren noch kompiliert. Oft
brauchst du dazu nicht nur die richtige Compilerversion sondern auch
noch die passende Version der Build Umgebung. Viel Spaß dabei.
Tim schrieb:> Auf der Arbeit, in einem Projekt, das andere warten müssen, wäre mir das> zu peinlich. Da würde ich>> ds.a=da[0];> ds.b=da[1];> ds.c=da[2];
Das ist keine gute Lösung. In 5 Jahren gibt es ziemlich sicher neue
Datenelemente, die du damit nicht mit kopierst.
Das Übel liegt wohl schon darin, nicht das der TE nicht EINE EINZIGE
Datenstruktur verwendet.
Michael schrieb:> Kann ich Strukturen in einem Binärfile abspeichern und wieder einlesen,> ohne dass ich Probleme mit Alignment, Cell Padding o. ä. bekomme? Das> scheint zu funktionieren, aber nach dem oben gesagten bin ich mir nicht> mehr sicher, ob das nur Zufall ist.
Normalerweise geht das problemlos, auch mit unterschiedlichen Compilern
und Optimierungen auf einer Plattform. Die Compiler einer Plattform
sind meist binär kompatibel zum Platzhirsch.
Es gibt auch spezielle Compileroptionen, die das Alignment und Padding
festlegen.
Udo K. schrieb:> Yalu X. schrieb:>> Auf jeden von ISO bzw. ANSI. In C90 stand die Regel allerdings noch in>> Kapitel 6, Abschnitt 3, Absatz 6.>> Sorry, ich bin zu blöd um das zu finden.
Nein, ich hätte mich etwas klarer ausdrücken müssen.
> In meiner ANSI/ISO 9899-1990 Kopie steht unter 6.3.6 "Additive> Operators" nichts dazu - oder mein Hirn denkt nicht in den Wegen des> Standards.
Seite 38, Abschnitt 6.3 (noch vor 6.3.1), der entsprechende Absatz
beginnt mit
"An object shall have its stored value accessed only by an lvalue that
has one of the follwing types:
..."
Oliver S. schrieb:> Rolf M. schrieb:>> Wenn ich den Wert eines int in einen anderen kopieren will, warum sollte>> da memcpy besser sein als eine einfache Zuweisung?>> Hier gehts um ein uint8_t Array, welches zu größere Datentypen> zusammengesetzt wird.
Das kam aber auch gerade erst heraus. Ursprünglich ging es um ein Array
aus int, das in eine Struktur mit lauter int-Feldern kopiert werden
sollte. Jetzt geht es auf einmal um was komplett anderes.
Michael schrieb:> Kann ich Strukturen in einem Binärfile abspeichern und wieder einlesen,> ohne dass ich Probleme mit Alignment, Cell Padding o. ä. bekomme? Das> scheint zu funktionieren, aber nach dem oben gesagten bin ich mir nicht> mehr sicher, ob das nur Zufall ist.
Du solltest dich mal entscheiden, was du eigentlich willst. Zuerst war
es ein Array aus int, das du in int-Elemente einer Struktur kopieren
wolltest, aber unbedingt mit einem Cast. Als nächstes war es ein Array
aus Rohbytes, das in deinem Code initialisiert wurde. Und jetzt liest du
die Daten aus einem File. Wäre schön, wenn du gleich von Anfang an dein
Ziel nennen würdest statt es nach der Salamitaktik Scheibchen für
Scheibchen zu offenbaren.
Udo K. schrieb:> Ich verwende gerne eine eigene memcpy Funktion, und damit geht der Hack> schon nicht mehr.
Warum? In der Regel ist diese Funktion stark optimiert, und da ist es
oft - genau wie in diesem Fall - kontraproduktiv, die selbst zu
schreiben.
> Ich will auch nicht, dass der Compiler nach Gutdünken Funktionen einbaut> oder entfernt.
Wo ist in dem Fall das Problem? Am Ende ist das auch nicht viel anders
als ein Funktions-inlining, nur mit noch mehr Optimierungspotenzial.
Oder bist du dann auch grundsätzlich gegen inlining, weil das auch "nach
Gutdünken" Funktionen entfernt?
> Und der MS Compiler vom Driver Development Kit ist erst 15 Jahre alt,> kann aber den memcpy Hack nur mit voll aufgedrehten Optimierungen - was> schlecht ist wenn ich kleine Programme möchte.
Das ist kein Hack, sondern eine Optimierung.
> Ich weiss Linuxer verwenden überall und immer -O3 :-).> Wenn du in einer Freestanding Umgebung arbeitest, dann geht der memcpy> Hack auch nicht.
Das ist richtig. Ich nutze keine Freestanding-Implementationen. Wäre das
so, dann würde ich es dort entsprechend anders machen. Wobei man
zumindest bei gcc auch im Freestanding-Mode noch Zugriff auf diese
eingebauten Funktionen hat, wenn man sie nutzen möchte.
> Wenn du aber mit einem OS arbeitest, das Structs und Arrays in> unterschiedlichen Segmenten abspeichert, dann geht nur memcpy.
Ein solche OS wäre mir nicht bekannt.
> Zumindest lassen sich 25 Jahre alte C99 Programme auch heute noch> relativ schmerzfrei übersetzen. Probiere das mal mit modernem C++ Zeugs,> da kannst du froh sein wenn das nach 5 Jahren noch kompiliert.
Warum? Die sind auch Backwards-Kompatibel. Wenn was nicht mehr
compiliert, ist es fast immer so, dass das Programm schon von vorne
herein falsch war und der Compiler das nur nicht bemerkt hat.
> Oft brauchst du dazu nicht nur die richtige Compilerversion sondern auch> noch die passende Version der Build Umgebung. Viel Spaß dabei.
Die Build-Umgebung hat aber mit der Sprache nichts zu tun. Wenn dein 25
Jahre altes C-Programm irgendwelche Sonderlösungen in den Autotools
verwendet, kann das genauso Probleme machen.
> Tim schrieb:>> Auf der Arbeit, in einem Projekt, das andere warten müssen, wäre mir das>> zu peinlich. Da würde ich>>>> ds.a=da[0];>> ds.b=da[1];>> ds.c=da[2];>> Das ist keine gute Lösung. In 5 Jahren gibt es ziemlich sicher neue> Datenelemente, die du damit nicht mit kopierst.
Nun, es muss eben klar sein, dass beim Hinzufügen neuer Felder natürlich
auch dafür gesorgt werden muss, dass die ordnungsgemäß befüllt werden.
Und gerade bei der (De)Serialisierung dann Anpassungen gemacht werden
müssen.
> Michael schrieb:>> Kann ich Strukturen in einem Binärfile abspeichern und wieder einlesen,>> ohne dass ich Probleme mit Alignment, Cell Padding o. ä. bekomme? Das>> scheint zu funktionieren, aber nach dem oben gesagten bin ich mir nicht>> mehr sicher, ob das nur Zufall ist.>> Normalerweise geht das problemlos, auch mit unterschiedlichen Compilern> und Optimierungen auf einer Plattform. Die Compiler einer Plattform> sind meist binär kompatibel zum Platzhirsch.> Es gibt auch spezielle Compileroptionen, die das Alignment und Padding> festlegen.
Am besten verwendet man die [u]nitX_t-Datentypen und baut seine
Strukturen so auf, dass sie auf allen gängigen Plattformen automatisch
das richtige Alignment haben, garniert mit ein paar static_asserts, die
sicherstellen, dass man merkt, wenn's irgendwo doch Abweichungen geben
sollte. Damit hat man die meisten Stolperstellen schon erledigt.
Allerdings fehlt natürlich noch die Edianness.
Udo K. schrieb:> Das C nicht einfach ist, sieht man auch am memcpy:> sizeof(da) ist im folgenden Beispiel in 50% der Fälle falsch.
Diese Aussage stimmt so nicht.
(Dein Code suggests, dass Arrays by-value übergeben werden könnten.)
> Rolf M. schrieb:>> Jede der Lösungen ist ein Hack.>> Hängt halt davon ab wo und mit welchen Tools du arbeitest.
Das memcpy ist (wie oben beschrieben) als Hack "besser" als das struct
assignment (via type cast), weil es Aliasing berücksichtigt.
> Tim schrieb:>> ds.a=da[0];>> ds.b=da[1];>> ds.c=da[2];>> Das ist keine gute Lösung.
Das ist die einzige korrekte Lösung wenn man dem C Standard folgt.
btw: Die ersten Jahre in denen ich C programmiert hatte, habe ich C mehr
oder weniger als "komfortablen" Assembler betrachtet. Das gab ein böses
Erwachen. Da C an vielen Stellen optimieren könnte, muss man tatsächlich
die (abstrakte) C Spezifikation verinnerlichen.
Michael schrieb:> Ich lege du die Sollwerte in einem const uint8_t Array ab, weil ich> nicht schreiben kann> ds = 0x112233aabbcc;
Da gibt es 2 getrennte Aufgaben:
* A) das Füllen der Strukturelemente (ds.a = ...)
* B) das Zusammensetzen mehrerer Bytes zu einem Skalar
Nehmen wir "ds" aus Deinem Beispiel und das EEProm als da[1000].
Zu A: Nehmen wir an, jede Struktur wird nur mit einem uint8_t gefüllt.
Dann geht z.B.:
1
uint8_t*p;
2
p=&da[0];/* das gleiche wie "p=da;"
3
4
ds.a = *p; p++; /* Inhalt des pointers und pointer incrementieren*/
5
ds.b=*p++;/* oder dessen verkürzte Schreibweise */
6
ds.c=*p++;
alternativ
1
structdat_structds={da[0],da[1],da[2]};
Zu B: Wenn ds.a als 2 Byte int gefüllt werden soll, brauchst Du etwas
wie
1
uint8_t*p=da[0];
2
3
ds.a=p[0]+p[1]<<8;p+=2;
Abhängig davon, ob Du little- oder big-Endian hast (low-Byte zuerst oder
Highbyte. Versuche nicht, hier *p++ mehrfach in einer Anweisung zu
verwenden. Schreibe Dir Funktionen, die entsprechende Anzahl an Bytes in
den gewünschten Typ verwandeln. Und übergib die Adresse des ersten
Bytes. Dann sieht das z.B. so aus:
1
uint16_tbToInt16Little(uint8_tb[2])/* die meisten nehmen hier einen pointer statt eines Array. ist faktisch das gleiche */
2
{
3
uint16_tret=b[0]+b[1]<<8;/* little Endian */
4
returnret;
5
}
6
ds.a=bToInt16Little(da[0]);
7
ds.b=tToUint32Little(da[2]);
8
ds.c=bToUint16Little(da[6]);
(aber nicht so geschwätige Funktionsnamen nehmen! Deine Beispiele sind
bezüglich Namensgebung imho sehr gut und zeugen von Erfahrung. )
Yalu X. schrieb:> Man kann aber bspw. schreiben:> struct dat_struct ds = {> 123, // a> 456, // b> 789 // c> };>> Hilft das nicht weiter?Michael schrieb:> Es hilft nur weiter, wenn die Struktur so einfach wie in meinem Beispiel> ist.
Was wäre ein komplizierteres Beispiel, wo das nicht mehr funktioniert?
Auch Arrays und verschachtelte Datentypen können problemlos auf diese
Weise initialisiert werden,