Forum: Compiler & IDEs 8 bit, 16 bit mischung in struktur - 8 bit breiten speicher vorgaukeln


von Thomas (Gast)


Lesenswert?

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!

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

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.

von Thomas (Gast)


Lesenswert?

Danke, das funktioniert tatsächlich.

Nächste Frage; woher weiß man sowas?

von Sven P. (Gast)


Lesenswert?

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.

von Olaf (Gast)


Lesenswert?

> 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

von Kernighan (Gast)


Lesenswert?

@ Olaf

YMMD...

Gruss vom Indianer

von Thomas (Gast)


Lesenswert?

@Olaf: dem muss ich widersprechen.

Des passt schon so!

von Marcus H. (mharnisch) Benutzerseite


Lesenswert?

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

von Kernighan (Gast)


Lesenswert?

Und was ist wenn diese Daten per Netzwerk (RS232, Ethernet, etc) von 
einer "Little Endian" zu einer "Big Endian" CPU übertragen werden?

Cheers,
Ritchie

von Sven P. (Gast)


Lesenswert?

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.

von Simon K. (simon) Benutzerseite


Lesenswert?

Sven Pauli wrote:
> meistens immer
Hehe, ja. Oder so! ;)

von Michael H. (mah)


Lesenswert?

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
Noch kein Account? Hier anmelden.