folgende Problematik. Vorhanden ist eine gegebene Struktur mit unterschiedlichen datentypen (uint8 / uint16). Der vorhandene Arbeitsspeicher ist 16-bit breit -> folge, compiler versucht natürlich, uint16-variablen in einer zelle zu speichern, wobei bei der mischung von uint8 und uint16 datentypen somit lücken (unbenutzt) entstehen. ziel ist nun, die struktur mittels uart-schnittstelle (8-bit) zu versenden. am einfachsten wäre das, wenn ich einen pointer (Uint8*) auf begin der struktur setze und die daten der reihe nach raussende. optimal geht das, wenn ich einen pointer auf struct_status_tx_8 setze. setze ich einen pointer auf struct_status_tx_16_A so werden natürlich die Lücken auch gesendet - das soll verhindert werden. natürlich kann ich die struktur so aufbauen wie in struct_status_tx_8, aber wenn ich dann einen uint16 wert incrementieren will, so tue ich mich schwer (neue temporäre uint16, aufteilen, zuweisen, etc.). gibt es eine directive, womit man festlegt, dass diese lücken quasi nicht entstehen, bzw. die struktur im ganzen 8-bit organisiert bleibt? verwendeter compiler: gcc typedef struct { Uint8 adr; // belegt bit 0..7 Uint8 sta_lb; // belegt bit 8..15 Uint8 sta_hb; // belegt bit 16..23 Uint8 err; // belegt bit 24..31 }struct_status_tx_8; // sizeof() liefert 4 Bytes, korrekt! typedef struct { Uint8 adr; // belegt bit 0..7 // bit 8..15 frei Uint16 sta; // belegt bit 16..31 Uint8 err; // belegt bit 32..39 // bit 40..47 frei }struct_status_tx_16_A; // sizeof() liefert 6 Bytes, korrekt! typedef struct { Uint16 sta; // belegt bit 0..15 Uint8 adr; // belegt bit 16..23 Uint8 err; // belegt bit 24..31 }struct_status_tx_16_B; // sizeof() liefert 4 Bytes, korrekt!
Thomas wrote: > gibt es eine directive, womit man festlegt, dass diese lücken quasi > nicht entstehen, bzw. die struktur im ganzen 8-bit organisiert bleibt? Ja:
1 | __attribute__((packed)) |
Aber: je nach Zielplattform wird dabei der Code entweder einfach nur weniger effizient, aber manch Prozessor (insbesondere die RISC-Typen) können dir dann auch schnell mal einen unaligned access trap schmeissen. Allemal sinnvoller ist es, die Struktur gleich so aufzubauen, dass zuerst alle 16-bit-Werte reingepackt werden und danach dann die 8-bit-Werte. Damit hat man schlimmstenfalls am Ende ein einziges Byte padding.
Danke, das funktioniert tatsächlich. Nächste Frage; woher weiß man sowas?
Thomas wrote:
> Nächste Frage; woher weiß man sowas?
Soweit ich weiß:
__attribute__(()) gehört nicht direkt zum C-Standard, sondern ist ein
Merkmal des verwendeten Compilers (ähnlich wie der ganze #pragma-Kram).
Demnach findet man sowas z.B. im Handbuch des Compilers.
> Danke, das funktioniert tatsächlich.
Aber nur zufaellig weil beide beteiligten CPUs bei dir High und Low
Byte in derselben Reihenfolge im Speicher ablegen...
Olaf
@ Olaf YMMD... Gruss vom Indianer
@Olaf: dem muss ich widersprechen. Des passt schon so!
Jörg Wunsch wrote: > Aber: je nach Zielplattform wird dabei der Code entweder einfach nur > weniger effizient, Ja. > aber manch Prozessor (insbesondere die RISC-Typen) können dir dann > auch schnell mal einen unaligned access trap schmeissen. Nein. Das attribut ist doch gerade dazu da, dem Compiler zu sagen, dass er code erzeugen soll, der auf der gewählte Zielplatform die Daten zuverlässig liest. Gruß Marcus
Und was ist wenn diese Daten per Netzwerk (RS232, Ethernet, etc) von einer "Little Endian" zu einer "Big Endian" CPU übertragen werden? Cheers, Ritchie
Kernighan wrote: > Und was ist wenn diese Daten per Netzwerk (RS232, Ethernet, etc) von > einer "Little Endian" zu einer "Big Endian" CPU übertragen werden? Das ist dann quasi scheißegal, weil zwischen Host-Byte-Order und Network-Byte-Order unterschieden wird, wobei letztere festgelegt ist. Dazu gibts dann in jeder guten Standardbibliothek Konvertierungsfunktionen. Bei RS232 wird meistens immer zuerst das kleinste Bit übertragen.
Sven Pauli wrote:
> meistens immer
Hehe, ja. Oder so! ;)
also wenn Du die Kontrolle über das "wire format" hast, würde ichs umbauen, sodass Variable immer minimum alignment haben - offsets von 16bit variablen immer modulo 2 = 0, 32bit Variable auf mod 4 = 0 usw Sieh Dir mal zB die IP header an - zB http://www.faqs.org/rfcs/rfc791.html wenns nicht passt - exlpizit padding bytes einfügen umbauen auf "alle shorts/longs zuerst" geht nicht immer elegant, zB wenn ein Längen- oder Variantenfeld drin ist diese alignment-Technik hat ihren Grund - solche Protokollheader auf einer >8bit CPU auseinanderzuklamüsern ist schlimm, wenn du keine 16 & 32bit operationen mehr verwenden kannst (ich durfte anno schnee ein Protokoll auf einer RISC-CPU dekodieren, bei denen longs auf einer 24-bit-Grenze waren - danke, Bill!) was auch geht statt einem compiler-spezifischen attribut: definier Dir Deine eigene "byte aligned" short, zB typedef struct myshort_s { uint8_t b1; uint8_t b2; } myshort; diese struct sollte byte-aligned passen und du kannst sie statt einem short einsetzen, aber dann kannst Du erst wieder b1 und b2 separat verwursten - Makros helfen, machen es aber weder schöner noch schneller; byte order ist auch noch zu berücksichtigen. -mah ps: wenn ich ein wire-format entwerfen müsste: - alignment wie oben - type-length-value Notation verwenden (http://en.wikipedia.org/wiki/Type-length-value), das machts einfach zu verarbeiten und problemlos erweiterbar - alles auf network byte order normieren
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.