Forum: Mikrocontroller und Digitale Elektronik C typecast array nach struct


von Michael (k-mte)


Lesenswert?

Wie mache ich in C einen Typecast, mit dem ich ein Array in eine 
Struktur umwandle?
1
struct dat_struct
2
  {
3
  int a, b, c;
4
  };
5
    
6
struct dat_struct ds;
7
8
typedef int dat_array [3];
9
10
dat_array da = {1, 2, 3};
11
  
12
ds = (???) da;

von Dergute W. (derguteweka)


Lesenswert?

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

von Nemopuk (nemopuk)


Lesenswert?

Man könnte einen Umweg über void* versuchen. Wenn der Cast irgendwie 
vermeidbar ist, dann vermeide ihn.

von Michael (k-mte)


Lesenswert?

10002 verwende ich auch. Eine Notlösung, die überflüssige Laufzeit 
generiert, obwohl doch zum Zeitpunkt des Kompilierens bereits alles 
bekannt ist.

von Max H. (nilsp)


Lesenswert?

Wenn es unbeding sein muss: Mach ds zu einem pointer und caste zumindest 
ein mal über char *.

Ungefähr so:
1
struct dat_struct
2
{
3
  int a, b, c;
4
};
5
    
6
struct dat_struct * ds;
7
typedef int dat_array [3];
8
dat_array da = {1, 2, 3};
9
  
10
void test (void)
11
{
12
  ds = (struct dat_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.

von Rolf M. (rmagnus)


Lesenswert?

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.

von Udo K. (udok)


Lesenswert?

Michael schrieb:
> Wie mache ich in C einen Typecast, mit dem ich ein Array in eine
> Struktur umwandle?
>
>
1
> struct dat_struct
2
>   {
3
>   int a, b, c;
4
>   };
5
> 
6
> struct dat_struct ds;
7
> 
8
> typedef int dat_array [3];
9
> 
10
> dat_array da = {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.

: Bearbeitet durch User
von Clemens S. (zoggl)


Lesenswert?

Was willst du tun? - nicht deine Lösung mit strukt und Typcast.

Vielleicht ein Union mit strukt und array?

Sg

von Oliver S. (oliverso)


Lesenswert?

1
fahrrad f = (fahrrad)fisch;

Geht alles. Nur warum?

Oliver

: Bearbeitet durch User
von Yalu X. (yalu) (Moderator)


Lesenswert?

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 (struct dat_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.

: Bearbeitet durch Moderator
von Rolf M. (rmagnus)


Lesenswert?

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
1
   memcpy(&ds, da, sizeof ds);

von Udo K. (udok)


Lesenswert?

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.

von Yalu X. (yalu) (Moderator)


Lesenswert?

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
  sizeof da

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.

von Rolf M. (rmagnus)


Lesenswert?

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.

von Nemopuk (nemopuk)


Lesenswert?

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.

von Tim (timgabinski)


Lesenswert?

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.

Beitrag #7924105 wurde vom Autor gelöscht.
von Bruno V. (bruno_v)


Lesenswert?

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

: Bearbeitet durch User
von Michael (k-mte)


Lesenswert?

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.

von Oliver S. (oliverso)


Lesenswert?

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

von Dergute W. (derguteweka)


Lesenswert?

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",((struct dat_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

von Michael (k-mte)


Lesenswert?

Ok, ich werde es mir zu Herzen nehmen und memcpy verwenden - in der 
Hoffnung, dadurch eventuelle Probleme mit Alignment und Cell Padding zu 
vermeiden.

von Dergute W. (derguteweka)


Lesenswert?

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

von Axel S. (a-za-z0-9)


Lesenswert?

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.

von Oliver S. (oliverso)


Lesenswert?

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

von Rolf M. (rmagnus)


Lesenswert?

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?

von Yalu X. (yalu) (Moderator)


Lesenswert?

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.

von Axel S. (a-za-z0-9)


Lesenswert?

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

von Rahul D. (rahul)


Lesenswert?

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.

von Arduino F. (Firma: Gast) (arduinof)


Lesenswert?

Ja, jetzt kommen "wir" zur Wurzel des Problems!
Anstelle an den Symptomen der "Krankheit" rum zu basteln.

von Rahul D. (rahul)


Lesenswert?

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.

von Axel S. (a-za-z0-9)


Lesenswert?

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.

von Oliver S. (oliverso)


Lesenswert?

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

von Michael (k-mte)


Lesenswert?

Ich lege du die Sollwerte in einem const uint8_t Array ab, weil ich 
nicht schreiben kann
1
ds = 0x112233aabbcc;

von Rahul D. (rahul)


Lesenswert?

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?

von Michael B. (laberkopp)


Lesenswert?

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
1
struct dat_struct
2
  {
3
  int a;
4
  unsigned char b; 
5
  int c;
6
  };
kann das derb in die Hose gehen.

von Nemopuk (nemopuk)


Lesenswert?

Vielleicht kann der TO  auch C++, obwohl er nach C gefragt hat. Denn 
damit lässt es sich elegant lösen. Man könnte von Arduino abgucken:

https://docs.arduino.cc/learn/programming/eeprom-guide/
https://github.com/arduino/ArduinoCore-avr/blob/master/libraries/EEPROM/src/EEPROM.h

von Yalu X. (yalu) (Moderator)


Lesenswert?

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:
1
struct dat_struct ds = {
2
  123, // a
3
  456, // b
4
  789  // c
5
};

Hilft das nicht weiter?

von Michael (k-mte)


Lesenswert?

Es hilft nur weiter, wenn die Struktur so einfach wie in meinem Beispiel 
ist.

von Arduino F. (Firma: Gast) (arduinof)


Lesenswert?

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?

von Michael (k-mte)


Lesenswert?

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.

von Arduino F. (Firma: Gast) (arduinof)


Lesenswert?

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.

von Michael (k-mte)


Lesenswert?

Es ist schon ein Wunder, dass meine Programme überhaupt funktionieren, 
zumindest so ein bischen...

von Michael B. (laberkopp)


Lesenswert?

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.

von Michael (k-mte)


Lesenswert?

Ok, vielen Dank an alle, ich habe viel gelernt!

von Udo K. (udok)


Lesenswert?

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.

von Yalu X. (yalu) (Moderator)


Lesenswert?

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:
..."

von Rolf M. (rmagnus)


Lesenswert?

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.

von Mikro 7. (mikro77)


Lesenswert?

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.

von Bruno V. (bruno_v)


Lesenswert?

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
struct dat_struct ds={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_t bToInt16Little(uint8_t b[2]) /* die meisten nehmen hier einen pointer statt eines Array. ist faktisch das gleiche */
2
{
3
uint16_t ret = b[0]+b[1]<<8; /* little Endian */
4
   return ret;
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. )

: Bearbeitet durch User
von Yalu X. (yalu) (Moderator)


Lesenswert?

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,

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.