Forum: Mikrocontroller und Digitale Elektronik Struct über Array legen, alignment Warnung


von Adam P. (adamap)


Lesenswert?

Hallo zusammen,

folgende Situation.

System:
ARM Cortex M4 und Atmel Studio.

Ich erhalte einen Datensatz (Befehl), dieser liegt dann in einem 1040 
Byte großem Buffer.

Um diese Daten nun einfacher verarbeiten zu können (Protokollabsprache) 
und nicht jedes mal mit einem Index zugreifen zu müssen, habe ich mir 
z.B. folgende Struct aufgebaut:
1
struct cmd_request
2
{
3
   uint16_t size;
4
   uint8_t type;
5
   uint16_t cmd;
6
   uint8_t data[CMD_REQUEST_DATA_SIZE];
7
} __attribute__ ((__packed__));
8
typedef struct cmd_request cmd_request_t;

Diese lege ich nun als Pointer auf die Adresse des Buffers.
Soweit so gut, funktioniert auch alles.

Natürlich bekomme ich vom Compiler die Warnung:
"packed attribute causes inefficient alignment for 'size'"

Ist ja auch verständlich,
aber wie könnte ich dieses Problem umgehen, evtl. anders lösen?
Habt ihr da eine Idee?

Weiterhin möchte ich doppelte Datenhaltung vermeiden,
sonst könnte man die Struct umbauen und die Werte einzeln zuweisen.

von Niklas G. (erlkoenig) Benutzerseite


Lesenswert?

Adam P. schrieb:
> aber wie könnte ich dieses Problem umgehen, evtl. anders lösen?

Serialisierung

von Adam P. (adamap)


Lesenswert?

Niklas G. schrieb:
> Serialisierung

Ja das ist mir bekannt...jedoch hat das doppelte Datenhaltung zur folge.

RAW-Array und das Datenpaket.

von Random .. (thorstendb) Benutzerseite


Lesenswert?

Wenn ich __PACKED für Serialisierung verwende, sind meine Arrays immer 
aligned. Ggf. fülle ich mit padding (Reserved) auf, damit das Alignment 
wieder passt. Somit braucht der Compiler das auch nicht umzuschieben bei 
der Auswertung, sonst bekommst du einen Haufen ineffizienter BYTE 
accesses.

Und - wie auch bei dir: Das Array mit der variablen Länge kommt immer 
ans Ende. Sachen wie Size kommen immer an den Anfang.

Dein Array, unter Beibehaltung der Reihenfolge (ggf. kannst auch 
umsortieren):
1
struct cmd_request
2
{
3
   uint16_t size;
4
   uint16_t res1;
5
   uint8_t type;
6
   uint8_t res2;
7
   uint16_t cmd;
8
   uint8_t data[CMD_REQUEST_DATA_SIZE];
9
} __attribute__ ((__packed__));
10
typedef struct cmd_request cmd_request_t;

von Jemand (Gast)


Lesenswert?

Adam P. schrieb:
> Diese lege ich nun als Pointer auf die Adresse des Buffers.
> Soweit so gut, funktioniert auch alles.

Du darfst in C nicht einfach wahllos Pointer auf Objekte  unpassenden 
Typs zeigen lassen, der Zugriff durch diese ruft undefiniertes Verhalten 
hervor!

von Niklas G. (erlkoenig) Benutzerseite


Lesenswert?

Adam P. schrieb:
> Ja das ist mir bekannt...jedoch hat das doppelte Datenhaltung zur folge.

Wie empfängst du die Daten denn? Falls jedes Byte einzeln einliest, 
kannst du die µSer-Bibliothek mit einem Input-Iterator benutzen. Dann 
werden die Bytes einzeln in das struct eingefüllt.

von Adam P. (adamap)


Lesenswert?

Niklas G. schrieb:
> Wie empfängst du die Daten denn?

Ich erhalte sie per USB mit DMA und einer Callback.

von Adam P. (adamap)


Lesenswert?

Jemand schrieb:
> Du darfst in C nicht einfach wahllos Pointer auf Objekte  unpassenden
> Typs zeigen lassen

Ist doch gar nicht wahllos... mir ist doch bekannt wie die Daten zu 
interpretieren sind.

von Adam P. (adamap)


Lesenswert?

Niklas G. schrieb:
> Dann
> werden die Bytes einzeln in das struct eingefüllt.

Ja, aber ich wollte ja die Struct nicht befüllen sondern diese als 
"Maske" über einen Buffer legen um einen komfortablen Zugriff zu haben.
So war zumindest die Grundidee.

von Random .. (thorstendb) Benutzerseite


Lesenswert?

Nachtrag: Du musst deinen Empfangsbuffer ebenfalls mit __align(4) an die 
richtige Position packen (falls der Compiler das nicht eh schon macht, 
aber sicher ist sicher).

von Adam P. (adamap)


Lesenswert?

Random .. schrieb:
> Du musst deinen Empfangsbuffer ebenfalls mit __align(4) an die
> richtige Position packen

Das ist schon so.

von Niklas G. (erlkoenig) Benutzerseite


Lesenswert?

Adam P. schrieb:
> Ich erhalte sie per USB mit DMA und einer Callback.

Ok, dann wird es komplizierter.

Adam P. schrieb:
> Ja, aber ich wollte ja die Struct nicht befüllen sondern diese als
> "Maske" über einen Buffer legen um einen komfortablen Zugriff zu haben.
> So war zumindest die Grundidee.

Das ist in C und C++ nicht wohldefiniert / portabel möglich. Die 
doppelte Datenhaltung wäre für Dinge wie Byte-Reihenfolge-Tauschen 
sowieso teilweise erforderlich.

Nach dem Konvertieren kannst du ja den "raw" Puffer direkt wieder für 
Empfang per DMA benutzen. Somit ist die Datenhaltung nur so Halb-Doppelt 
:)

von Adam P. (adamap)


Lesenswert?

Niklas G. schrieb:
> Nach dem Konvertieren kannst du ja den "raw" Puffer direkt wieder für
> Empfang per DMA benutzen. Somit ist die Datenhaltung nur so Halb-Doppelt
> :)

So hatte ich es ja bis jetzt auch.

Nun dachte ich ok, dann mach ich eine Serialisierung aber nutze Zeiger:
1
struct cmd_request
2
{
3
  uint16_t size;
4
  uint8_t *type;
5
  uint16_t *cmd;
6
  uint8_t *data;
7
};
8
typedef struct cmd_request cmd_request_t;
9
10
...
11
12
cmd.size = 1024;
13
14
cmd.type = &buf[0];
15
cmd.cmd = (uint16_t *)&buf[1];
16
cmd.data = &buf[3];

Aber da gefällt ihm der cast nicht:
"cast increases required alignment of target type"

Ich glaub ich werde die "doppelte" Datenhaltung beibehalten, entferne 
die " _attribute_ ((_packed_))" und bastel mir die serialize() / 
deserialize() Funktionen.

Alles nicht so wirklich zufriedenstellend :-)

von Niklas G. (erlkoenig) Benutzerseite


Lesenswert?

Adam P. schrieb:
> Aber da gefällt ihm der cast nicht:
> "cast increases required alignment of target type"

Du weist ja auch Pointer zu!

So war das wohl gemeint:
1
cmd.size = 1024;
2
3
cmd.type = buf[0];
4
cmd.cmd = buf [1] | (((uint16_t) buf [2]) << 8);
5
memcpy (cmd.data, buf+3, cmd.size);

Wichtig hier: Bitshifts statt cast auf uint16_t* verwenden. Dadurch wird 
es wohldefiniert und portabel.

Mithilfe der µSer-Bibliothek ginge es so:
1
#include <iostream>
2
#include <cstdint>
3
#include <cstddef>
4
#include <uSer/uSer.hh>
5
6
static constexpr std::size_t CMD_REQUEST_DATA_SIZE = 3;
7
8
struct cmd_request {
9
  USER_STRUCT(cmd_request)
10
  
11
  uint16_t size;
12
  uint8_t type;
13
  uint16_t cmd;
14
  uint8_t data [CMD_REQUEST_DATA_SIZE];
15
16
  USER_ENUM_MEM (size, type, cmd, data)
17
};
18
19
int main () {
20
  std::uint8_t rxBuffer [8] = { 3, 0, 42, 0xAD, 0xDE, 1, 2, 3 };
21
  cmd_request req;
22
  
23
  uSer::deserialize (rxBuffer, req);
24
  
25
  std::cout << "cmd_request: size=" << req.size << ", type=" << (int) req.type << ", cmd=0x" << std::hex << req.cmd << ", data = {" << (int) req.data[0] << ", " << (int) req.data[1] << ", " << (int) req.data[2] << "}" << std::endl;
26
}

Wozu brauchst du überhaupt das "size"? Bei USB-Paketen ist doch die 
Größe immer bekannt. Du kannst doch, genau wie bei Control Endpoints, 
die Daten mit einem nicht-vollständigen oder leeren Paket abschließen.

von Adam P. (adamap)


Lesenswert?

Niklas G. schrieb:
> Du weist ja auch Pointer zu!

Ja, schau dir doch die Struct Definition an, da sind Pointer drin.

von Adam P. (adamap)


Lesenswert?

Niklas G. schrieb:
> Mithilfe der µSer-Bibliothek ginge es so:

Ist aber C++.

von Niklas G. (erlkoenig) Benutzerseite


Lesenswert?

Adam P. schrieb:
> Ja, schau dir doch die Struct Definition an, da sind Pointer drin.

Achso, ja aber wozu? Wenn du die auf den "raw" Puffer zeigen lässt, ist 
zur ursprünglichen Version kaum was gewonnen.

Adam P. schrieb:
> Ist aber C++.

Es muss nur die 1 Datei mit dem "deserialize" Aufruf mit C++ kompiliert 
werden; der Rest des Projekts kann C bleiben:

https://erlkoenig90.github.io/uSer-doc/html/tutorial.html#CCompat

von Adam P. (adamap)


Lesenswert?

OK, danke.

Werde ich mir mal in einer ruhigen Minute genauer anschauen.

von PittyJ (Gast)


Lesenswert?

Adam P. schrieb:
> Niklas G. schrieb:
>> Dann
>> werden die Bytes einzeln in das struct eingefüllt.
>
> Ja, aber ich wollte ja die Struct nicht befüllen sondern diese als
> "Maske" über einen Buffer legen um einen komfortablen Zugriff zu haben.
> So war zumindest die Grundidee.

So etwas hat man früher öfters gemacht. War dann Jahre später immer die 
Ursache von Problemen, wenn sich Compiler oder CPU (32->64 Bit) geändert 
haben.

Deshalb macht man das heute nicht mehr, sondern serialisiert sauber und 
freut sich in 10 Jahren, dass es immer noch geht.

von Random .. (thorstendb) Benutzerseite


Lesenswert?

Lässt sich denn das Protokoll nicht mehr ändern?

von Adam P. (adamap)


Lesenswert?

Random .. schrieb:
> Lässt sich denn das Protokoll nicht mehr ändern?

:-D

Klar, wenn es nur an mir liegen würde, sofort. Aber leider geht das 
nicht so einfach.

Aber beim nächsten mal werde ich genau auf sowas achten.

Ich meine, das mit der "sauberen" Serialisierung ist ja gar nicht so 
wild,
ich dachte nur, dass es auch sauber mit Zeigern einer Struct geht, 
leider scheint dies nicht so gängig zu sein.

Aber deshalb habe ich ja auch in die Runde gefragt ;)

von Niklas G. (erlkoenig) Benutzerseite


Lesenswert?

Adam P. schrieb:
> ich dachte nur, dass es auch sauber mit Zeigern einer Struct geht,
> leider scheint dies nicht so gängig zu sein.

Damit vermeidest du nur das Padding-Problem, aber ggf. hast du immer 
noch Probleme mit Alignment, Byte-Reihenfolge und Vorzeichen-Formaten.

Zusätzlich verschwendest du damit noch Speicher für die Zeiger. Da 
kannst du dir besser inline-Getter-Funktionen bauen, welche den Zeiger 
"live" berechnen - das wird vermutlich Laufzeit und Speicher sparen, 
aber dennoch o.g. Probleme haben.

von Hanna (Gast)


Lesenswert?

Was du suchst sind UNIONS

Einfach mal danach googlen, aber das sollte genau das sein was du 
brauchst oder?

von Bernd K. (prof7bit)


Lesenswert?

Adam P. schrieb:
> Ist ja auch verständlich,
> aber wie könnte ich dieses Problem umgehen, evtl. anders lösen?
> Habt ihr da eine Idee?

Indem Du Dein Struct NICHT packed machst (Todsünde) und stattdessen 
einfach die Member alle so umsortierst daß sie automatisch von selbst 
schon aligned sind.

Lies das: http://www.catb.org/esr/structure-packing/

Und dann machst Du eine union aus byte array und struct um 
sichertzstellen daß das byte array mindestens so streng aligned ist wie 
es das struct erfordert.

von Adam P. (adamap)


Lesenswert?

OK.

Welchen Ansatz würdet ihr dann bevorzugen, wenn man die Daten mithilfe 
von serialize() / deserialize() verarbeitet:

1) mit Reserve-Byte
1
typedef struct cmd_request_t
2
{
3
   uint16_t size;
4
   uint8_t type;
5
   uint8_t reserved_0;
6
   uint16_t cmd;
7
   uint8_t data[CMD_REQUEST_DATA_SIZE];
8
} cmd_request_t;

2) Andere Reihenfolge
1
typedef struct cmd_request_t
2
{
3
   uint16_t size;
4
   uint16_t cmd;
5
   uint8_t type;
6
   uint8_t data[CMD_REQUEST_DATA_SIZE];
7
} cmd_request_t;

von Niklas G. (erlkoenig) Benutzerseite


Lesenswert?

Bernd K. schrieb:
> Und dann machst Du eine union aus byte array und struct um
> sichertzstellen daß das byte array mindestens so streng aligned ist wie
> es das struct erfordert.

Kannst du bei solchen Hinweisen vielleicht dazu schreiben, dass ein 
solches Vorgehen in C plattform-abhängig und nicht portabel, und in C++ 
ganz verboten ist?

chrieb im Beitrag #5842163:
> Lies das: http://www.catb.org/esr/structure-packing/

Hier geht es aber lediglich darum, Speicher zu sparen, und nicht um 
Serialisierung/Konvertierung...

Bernd K. schrieb:
> Indem Du Dein Struct NICHT packed machst (Todsünde) und stattdessen
> einfach die Member alle so umsortierst daß sie automatisch von selbst
> schon aligned sind.

Er kann das Protokoll nicht ändern.

Adam P. schrieb:
> Welchen Ansatz würdet ihr dann bevorzugen,

Keinen von beiden, weil der Compiler es immer noch kaputtoptimieren kann 
(außer bei unions in C) und das auf Big-Endian-Systemen immer noch 
schief geht.

Hanna schrieb:
> Was du suchst sind UNIONS

Die sind ausschließlich zum Speicher Sparen, aber nicht zum Daten 
Konvertieren gemacht. Wie auch schon im vom 1. Beitrag verlinkten 
Artikel erläutert. Einfach mal den Thread lesen...

von Adam P. (adamap)


Lesenswert?

Niklas G. schrieb:
> Keinen von beiden

Niklas G. schrieb:
> Wie auch schon im vom 1. Beitrag verlinkten
> Artikel erläutert. Einfach mal den Thread lesen...

Jetzt bin ich verwirrt.

von Adam P. (adamap)


Lesenswert?

Ich dachte was im Artikel steht wäre soweit ok.

Oder soll ich die Struct einfach so belassen:
1
typedef struct cmd_request_t
2
{
3
   uint16_t size;
4
   uint8_t type;
5
   uint16_t cmd;
6
   uint8_t data[CMD_REQUEST_DATA_SIZE];
7
} cmd_request_t;

Da werden dann halt "leer-bytes" eingefügt, was mich ja durch das 
Serialisieren aber nicht weiter stören sollte. Da ich ja die Werte 
direkt zuweise (falls nötig mit Bit-Shifting), sehe ich das richtig?

von Niklas G. (erlkoenig) Benutzerseite


Lesenswert?

Adam P. schrieb:
> Jetzt bin ich verwirrt.

Achso, du meintest, welchen Ansatz wählen wenn man sowieso explizit 
serialisiert (Bitshifts & Co)?

Dann auf jeden Fall den 2. Ansatz. Reservierte Bytes braucht man in 
structs nur, wenn man sie "direkt" ohne Serialisierung schickt/einliest; 
der Compiler baut solches Padding automatisch ein, solange man eben 
nicht "packed" verwendet, was man sowieso nicht tun sollte. Ohne 
"packed" ist das "reserved_0" unnütz.

Da CMD_REQUEST_DATA_SIZE wahrscheinlich > 1 ist und "data" auch aligned 
ist, wird der Speicherverbrauch in beiden Fällen gleich sein.

von Adam P. (adamap)


Lesenswert?

Niklas G. schrieb:
> Achso, du meintest, welchen Ansatz wählen wenn man sowieso explizit
> serialisiert (Bitshifts & Co)?
>
> Dann auf jeden Fall den 2. Ansatz.

Ja genau, so meinte ich das.

Adam P. schrieb:
> Oder soll ich die Struct einfach so belassen:

Da würde er es ja automatisch einfügen, aber würde mich ja auch net 
stören.
Wäre halt von der logischen Datenreihenfolge her identisch.

von Hanna (Gast)


Lesenswert?

In welcher ANSI C ist denn definiert, dass eine UNION nur zum Speicher 
sparen verwednet werden kann?

Faktisch gibst du dem ein und selben Speicherbereich 2 oder mehrere 
Variablennamen etc.

Für was das letztlich Verwendung findet ist dem Programmierer 
überlassen.

Fakt ist:

UNION wird an dieser Stelle funktionieren, braucht keinen erweiterten 
Speicherplatz und kommt ohne Pointerverbiegerei hin.

Also was will man mehr?

von Adam P. (adamap)


Lesenswert?

Hanna schrieb:
> UNION wird an dieser Stelle funktionieren, braucht keinen erweiterten
> Speicherplatz und kommt ohne Pointerverbiegerei hin.

Meinst du das etwa so:
1
typedef union foo_t
2
{
3
  uint8_t buf[1040];
4
  
5
  struct
6
  {
7
    uint16_t a;
8
    uint8_t b;
9
    uint16_t c;
10
    uint8_t d[512];
11
  };
12
} foo_t;

Weil, das geht so nicht. Da hast du ebenfalls das alignment Problem.

von Bernd K. (prof7bit)


Lesenswert?

Niklas G. schrieb:
> und in C++
> ganz verboten ist?

Es ist nicht explizit erlaubt weil keine Klausel enthalten ist die das 
wörtlich erlaubt. Verboten ist es jedoch nicht!

Aber aus der Anforderung im jeweiligen ABI wie ein struct 
zwingenderweise physikalisch anzuordnen ist und aus der Garantie im C++ 
Standard daß "common initial members" immer übereinander zu liegen 
kommen folgt aber im logischem Schluß daß structs in einer Union am 
ersten Member bündig ausgerichtet übereinander liegen müssen. Und aus 
den Anforderungen des jeweiligen ABI folgt auch das Alignment des 
gesamten Gebildes.

Daraus kann man nun logisch schließen daß Type-Punning gemäß des 
Datenlayouts der jeweiligen Platform immer funktionieren wird, es ist 
also durchaus garantiert, wenn auch nur indirekt!

Diese logische Verkettung zweier Garantien zur (unausgesprochenen weil 
impliziten Garantie des Type-Punning) reduziert also das ganze Problem 
auf die Betrachtung wie die Daten auf dem jeweiligen ABI physikalisch im 
Speicher angeordnet sein werden. Und das kann man für die jeweiligen 
angedachten Zielplattform(en) schwarz auf weiß nachlesen und 
glücklicherweise steht da heute auch fast überall diesbezüglich das 
selbe drin, also wird es in der Praxis (von der wir hier sprechen) 
eigentlich ganz einfach.

Zukünftige Abweichungen kann man mit ein paar strategisch gut plazierten 
statischen asserts zur Compilezeit abfangen wenn man ein 
Über-Perfektionist sein will, dann muss an der Stelle halt in 50 oder 
100 Jahren wenn bekannt ist wie die neuen Prozessoren die mit allen 
Traditionen brechen funktionieren nochmal jemand kurz Hand anlegen. Aber 
wahrscheinlich ist C(++) bis dahin dann sowieso obsolet.

von Niklas G. (erlkoenig) Benutzerseite


Lesenswert?

Hanna schrieb:
> In welcher ANSI C ist denn definiert, dass eine UNION nur zum Speicher
> sparen verwednet werden kann?

Es gibt nur ein "ANSI C". Welches ziemlich veraltet ist. Es ist in C wie 
gesagt erlaubt (IIRC aber nur in neueren Standards, nicht in ANSI-C), 
unions zum "Konvertieren" zu nutzen, aber in keinem Standard ist ein 
garantiertes Verhalten definiert.

Hanna schrieb:
> UNION wird an dieser Stelle funktionieren

Definiere "funktionieren".

Hanna schrieb:
> Also was will man mehr?

Eine Version, deren Funktionsfähigkeit vom Standard garantiert ist, und 
welche auf allen Plattformen und Compilern funktioniert, und vielleicht 
sogar C++-aufwärtskompatibel ist.

von Olaf (Gast)


Lesenswert?

> UNION wird an dieser Stelle funktionieren, braucht keinen erweiterten
> Speicherplatz und kommt ohne Pointerverbiegerei hin.

Das sehe ich auch so. Das funktioniert und habe ich schon oft selbst 
benutzt.

Das Problem ist natuerlich das es Probleme geben kann wenn man seinen 
Source in 10Jahren mal auf einen ganz anderen Controller weiterverwenden 
will und sich auf die gute alte Funktionalitaet verlaesst!
Und natuerlich rasten die ganzen Konzernprogrammierer hier aus weil sie 
die Erfahrung bereits gemacht haben. :-)

Andererseits wenn man weiss was man macht und es nur fuer einen kleinen 
Controller ist wo man nur wenig Resourcen hat wieso nicht. Erzieht 
wenigstens die ganzen Abschreiber im Internetzeitalter zum selber 
denken. .-)

Olaf

von Niklas G. (erlkoenig) Benutzerseite


Lesenswert?

Bernd K. schrieb:
> Daraus kann man nun logisch schließen daß Type-Punning gemäß des
> Datenlayouts der jeweiligen Platform immer funktionieren wird

Hier steht was anderes:

https://stackoverflow.com/a/11996970

von Bernd K. (prof7bit)


Lesenswert?

Niklas G. schrieb:
> Es ist in C wie
> gesagt erlaubt (IIRC aber nur in neueren Standards, nicht in ANSI-C)

Es wurde ein klärender Satz hinzufügt der die implizit bereits logisch 
herleitbare Erlaubnis nochmal explizit wörtlich erwähnt hat damit der 
eilige Leser nicht so lange drüber nachdeken muß.

Im C++-Standard hat man das wohl bislang noch nicht für nötig befunden 
oder schlichtweg vergessen. Ist ja eigentlich auch redundant.

von Bernd K. (prof7bit)


Lesenswert?

Niklas G. schrieb:
> Hier steht was anderes:
>
> https://stackoverflow.com/a/11996970

Bitte präzisiere oder zitiere den entsprechenden Satz, das ist ein 
ganzer Stackoverflow-Thread, den kenn ich schon, worauf nimmst Du 
konkret Bezug?

von Niklas G. (erlkoenig) Benutzerseite


Lesenswert?

PS: Big-Endian-Plattformen existieren, und für diese muss man bei allen 
sich aufs Speicherlayout verlassenden Varianten dann #ifdefs und 
Byte-Tauschen einbauen, wobei die #ifdef-Bedingung selbst nur mit 
compilerspezifischen Erweiterungen möglich ist (z.B: 
_ORDER_BIG_ENDIAN_ beim GCC).

Diese Tauscherei ist dann auch nicht mehr Aufwand als es direkt richtig 
mit Bitshifts zu machen.

Bernd K. schrieb:
> worauf nimmst Du
> konkret Bezug?

"That is, although we can legitimately form an lvalue to a non-active 
union member (which is why assigning to a non-active member without 
construction is ok) it is considered to be uninitialized."

von Jemand (Gast)


Lesenswert?

Adam P. schrieb:
> mir ist doch bekannt wie die Daten zu interpretieren sind.

Das ist absolut irrelevant. Wäre sonst auch implementationsdefiniert, 
nicht undefiniert.

von Bernd K. (prof7bit)


Lesenswert?

Dann will ichs mal anders formulieren: Ist einem der Anwesenden auch nur 
ein einziger Fall bekannt bei dem Type-Punning mit Unions in der Praxis 
nicht wie erwartet funktioniert hat obwohl alle Anforderungen des ABI 
bezüglich Datenlayout, Endianness und Alignment auf der betreffenden 
Plattform sorgfältig recherchiert und buchstabengetreu erfüllt waren?

Also ein Fall in dem der Compiler quasi gesagt hat "Du hast zwar alle 
Offsets richtig ausgerechnet und eigentlich müssten Deine Daten genau 
dort liegen wo Du gerade zugreifen willst aber ich habe jetzt keine Lust 
weil das im Standard nicht wörtlich von mir verlangt wird und deshalb 
mach ich jetzt einfach mal was ganz anderes, auch wenn das mehr Aufwand 
für mich bedeutet!", sowas in der Art?

von PittyJ (Gast)


Lesenswert?

Bernd K. schrieb:
> Dann will ichs mal anders formulieren: Ist einem der Anwesenden auch nur
> ein einziger Fall bekannt bei dem Type-Punning mit Unions in der Praxis
> nicht wie erwartet funktioniert hat obwohl alle Anforderungen des ABI
> bezüglich Datenlayout, Endianness und Alignment auf der betreffenden
> Plattform sorgfältig recherchiert und buchstabengetreu erfüllt waren?

Wenn sich alle im Straßenverkehr korrekt verhalten würden, bräuchte man 
keinen Sicherheitsgurt.
Mir hat er einmal das Leben gerettet.

Wenn es eine potentielle Gefahrenstelle gibt, dann umschifft man die. 
Und die einfachste Art ist die Serialisierung.

von Bernd K. (prof7bit)


Lesenswert?

PittyJ schrieb:
> Wenn es eine potentielle Gefahrenstelle gibt

Das war die Frage. Ist es eine Gefahrenstelle (rein logisch kann es 
keine sein denn das ABI diktiert(!) haarklein das Speicherlayout einer 
Union), oder ist es nur eine unbegründete Befürchtung weil niemand 
offizielles bislang bestätigt hat daß es erlaubt ist (und die ganze Zeit 
schon war) wie es bei C geschehen ist?

von Markus F. (mfro)


Lesenswert?

Es **ist** erlaubt.

Allerdings (vornehmlich, weil es nun mal Big- und Little-Endian-Systeme 
gibt) implementation defined. Also so, wie es sich der Compiler- (bzw. 
Plattformhersteller gedacht hat).

Wenn das nicht so funktionieren würde, würden Generationen von 
TCP-Stacks (s. struct sockaddr) auf die Nase fallen.

von Jemand (Gast)


Lesenswert?

Markus F. schrieb:
> Es **ist** erlaubt.
>[…]
> Wenn das nicht so funktionieren würde, würden Generationen von
> TCP-Stacks (s. struct sockaddr) auf die Nase fallen.

Ja, das ist so krass erlaubt, dass im Linux Kernel extra die 
Aliasing-Regeln für solche Spielereien by-default gelockert sind.
Muss unglaublich C-konform sein, dieser Code!

von Markus F. (mfro)


Lesenswert?

Jemand schrieb:
> Ja, das ist so krass erlaubt, dass im Linux Kernel extra die
> Aliasing-Regeln für solche Spielereien by-default gelockert sind.

... und strict aliasing hat was genau mit unions zu tun?

von gre (Gast)


Lesenswert?

ich peil irgednwie das problem nicht

sollte das nicht so gehen :

typedef struct _attribute_ ((_packed_)){
   uint16_t size;
   uint8_t type;
   uint16_t cmd;
   uint8_t data[CMD_REQUEST_DATA_SIZE];
} cmd_request_t;

// buffer für daten
uint8_t buffer[1040];

// irgendwo pointer zuweisen
cmd_request_t *mybuf = (cmd_request_t*) buffer;

//lesen
uint16_t oldsize = mybuf->size;

//schreiben
mybuf->size = 0;


??

von Jemand (Gast)


Lesenswert?

Markus F. schrieb:
> Jemand schrieb:
>> Ja, das ist so krass erlaubt, dass im Linux Kernel extra die
>> Aliasing-Regeln für solche Spielereien by-default gelockert sind.
>
> ... und strict aliasing hat was genau mit unions zu tun?

Was hat dein Post mit Unions zu tun? Du zitierst nichts und Linux nutzt 
für sockaddr Pointer-Casts.

von Niklas Gürtler (Gast)


Lesenswert?

gre schrieb:
> sollte das nicht so gehen :

Eben nicht. Thread nochmal lesen, inkl. Links. Das erklären wir jetzt 
nicht noch mal.

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.