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