Forum: Compiler & IDEs struct / jedes einzelne bit lesen


von Andre Duecke (Gast)


Lesenswert?

Hallo Gemeinde,

vielleicht zunächst vorweg: Die Frage dreht sich nicht um den GCC 
sondern den Keil C51 Compiler...

Ich habe ein umfangreiches struct mit Bitfeldern und einer Gesamtgröße 
von 10 Byte.
1
typedef struct {
2
  unsigned int header1 :4;
3
  unsigned int header2 :4;
4
  unsigned int data1   :4;
5
  unsigned int parity  :1;
6
  unsigned int state   :1;
7
8
  ...usw.    
9
10
} SensorFrame;

So habe komfortabel Zugriff auf die einzelnen Bestandteile des 
Datenpaketes.

Für die Übertragung dieses Paketes benötige ich aber jedes einzelne Bit.
Theoretisch würde ich also gern sowas machen, wie:
1
for(int i=0; i<sizeof(SensorFrame); i++) {
2
3
  currentBit = ((frame >> i) & 0x01);
4
5
  ...
6
}

Aber das geht afaik nicht, weil nicht garantiert ist, dass die Daten des 
struct wirklich lückenlos im Speicher hintereinanderliegen.

Hat jemand eine Idee, wie einerseits eine komplexe Datenstruktur 
aufbauen kann, die einfach zu handhaben ist und andererseits die 
möglichkeit bietet, schnell alle bits von vorn bis hinten 
durchzublättern?

Würde mich über eure Vorschläge und Ideen sehr freuen.

Danke, Andre´

von Jim M. (turboj)


Lesenswert?

Andre Duecke schrieb:
> Aber das geht afaik nicht, weil nicht garantiert ist, dass die Daten des
> struct wirklich lückenlos im Speicher hintereinanderliegen.

Wenn sizeof (SensorFrame) * 8 == Anszal Bits ist, dann müssen sie 
lückenlos im Speicher stehen.

von Wursty (Gast)


Lesenswert?

Jim Meba schrieb:
> Andre Duecke schrieb:
> Aber das geht afaik nicht, weil nicht garantiert ist, dass die Daten des
> struct wirklich lückenlos im Speicher hintereinanderliegen.
>
> Wenn sizeof (SensorFrame) * 8 == Anszal Bits ist, dann müssen sie
> lückenlos im Speicher stehen.

sizeof liefert die Größe in Byte, was soll da raus kommen?

Bei Keil gibt es "packed" um das alignment selber zu steuern. Das ist in 
der Hilfe gut erklärt.

von Andre Duecke (Gast)


Lesenswert?

Hallo ihr,

vielen Dank schonmal.

Sorry wegen der for-Schleife. Stimmt natürlich - sizeof() liefert die 
Anzahl der Bytes - nicht der Bits.

Im echten Code soll die Struktur per Interrupt bit für bit durchlaufen 
werden. Da gibt es dann diese for-Schleife nicht, sondern eine globale 
Variable, die von der ISR verwendet und jeweils 
hochgezählt/zurückgesetzt wird.

Ich habe mir __packed mal angesehen, konnte es aber noch nicht testen,
da ich noch ein weiteres Problem habe.

wenn ich nämlich z.B. sowas mache:
1
typedef struct {
2
  unsigned int header1 :4;
3
  unsigned int header2 :4;
4
  unsigned int data1   :4;
5
  unsigned int parity  :1;
6
  unsigned int state   :1;  
7
} SensorFrame;
8
9
SensorFrame f;
10
11
bit test = ((f >> 12) & 0x01);

Dann erhalte ich den Compilerfehler: ">>: bad operand type".

Scheinbar kann ich auf eine Struktur keine bitshift-Operationen 
anwenden.

Hat da vielleicht noch jemand einen Tip, wie man es stattdessen machen 
könnte?

Vielen Dank,

Andre´

von Peter II (Gast)


Lesenswert?

Andre Duecke schrieb:
> Hat da vielleicht noch jemand einen Tip, wie man es stattdessen machen
> könnte?

einfach die struct per memcopy in ein Byte Array kopieren und die Bytes 
bitsweise versenden.

von Wursty (Gast)


Lesenswert?

cast operator, aber das ist alles Pfusch.

von Peter II (Gast)


Lesenswert?

Nachtrag:
1
typedef struct {
2
  unsigned int header1 :4;
3
  unsigned int header2 :4;
4
  unsigned int data1   :4;
5
  unsigned int parity  :1;
6
  unsigned int state   :1;
7
8
  ...usw.    
9
10
} SensorFrame;
11
12
void Send( uint8_t* data, size_t size ) {
13
   
14
}
15
16
Send( (uint8_t*)&SensorFrame, sizeof( SensorFrame ) );

von Klaus (Gast)


Lesenswert?

Die übliche Methode für solche und ähnliche Fälle beruht darauf, dass 
packed structs sich in der (Faust-)Regel (d.h. die meisten Compiler 
machen das seit Jahren reproduzierbar so) 1:1 ohne Informationsverlust 
in ein Byte-Array casten lassen. Allerdings ist das vom Standard nicht 
garantiert, so dass man bei neuen Compilern immer mal checken sollte, ob 
das geht. In den letzten Jahrzehnten sind mir von der "Regel" keine 
Abweichungen vorgekommen - aber "Shit happens" :-)

Das Problem zur Compile- oder Laufzeit die Größe in Bits festzustellen 
lässt sich programmatisch nicht ausschliesslich unter Verwendung von 
Präprozessor und Compiler lösen, da die geringste Auflösung das Byte 
ist. Da musst Du #define verwenden (oder ein Äquivalent).

von Klaus (Gast)


Lesenswert?

In gewisser Weise ist das "Pfusch". Das ist richtig. Wie gesagt, deckt 
der Standard diesen Cast nicht ab (das Verhalten ist undefiniert, d.h. 
jeder Compiler kann das anders machen oder sogar verweigern.)
Aber interessant ist auch, dass dieses Verhalten seit Jahrzehnten von 
vielen Compilern durchgängig so implementiert ist. Der Fall tritt 
einfach sehr häufig auf.
Es gibt natürlich Methoden das Standardkonform zu implementieren. Dazu 
wird jedes Mitglied mit seiner Grösse einzeln berücksichtigt und heraus 
geschoben. Soviel ist klar. Kann man so machen. Ob man es sollte ist 
kontrovers.

Eine Ausnahme sind Projekte, in denen MISRA oder ähnliche Regelwerke 
angewendet werden sollen. Dort kann der "Pfusch" verboten sein.

Eine endgültige oder allgemeingültige Aussage dazu ist schwierig. Viele 
Profis machen das so. Die "Erbsenzähler" haben aber ebenso recht es zu 
vermeiden.

von .... (Gast)


Lesenswert?

Sind wieder alle Leute schlau hier und kennen sich mit dem Standard aus 
und was nicht.... Konzentriert ihr euch mal auf das Niederregnen von 
Worst-Case-Fällen mit eurem mega Fachwissen, und ignoriert die 
eigentliche Lösung:


   u n i o n


wieviel der Software wird letztlich auf andere Platformen Portiert oder 
werden jemals von einem Anderen compiler übersetzt... im Hobby- und 
Hardwarenahen bereich ist das doch schon eher ein spezialfall - gerade 
bei jemanden der an soeinem Problem scheitert. Es ist SINNLOSE 
Wichtigtuerei hier anzufangen sich zu beschweren welch schlechte Manier 
und wie unsicher es ist, die Struktur auf ein Char-Array zu casten und 
dann zu verarbeiten... wenn es funktioniert, ist es für dieses Projekt 
gut genug.
Ob es funktioniert? 2min Debuggen.. fertig. Man kann aber auch weiterhin 
stundenlang drüber diskutieren. Hebt das Ego diverser Leute.. bringt 
aber NULL.
Habt spaß.

von Wursty (Gast)


Lesenswert?

Was kommt denn da für ne Flachpumpe aus der Ecke?
Eine union ist der gleiche Pfusch wie ein cast.

@Klaus, wer benutzt Misra und warum macht der das? :-P

von Rolf M. (rmagnus)


Lesenswert?

Wursty schrieb:
> Eine union ist der gleiche Pfusch wie ein cast.

Schlimmer, und dazu noch umständlicher zu schreiben.

von db8fs (Gast)


Lesenswert?

Jetzt will ich aber auch mal mitflamen: warum ist Union denn so schlecht 
hier an dieser Stelle?

Ich denke, eine Union ist ein hervorragendes Mittel um Binärdaten zu 
verwursten und kann, richtig und vernünftig eingesetzt, wirklich fast 
Wunderdinge bei Binärprotokollen bewirken, gerade durch Schachtelungen 
(siehe BSD Sockets). Sobald man solche Protokolle crunchen muss, ist man 
mit einer sauberen Strukturierung durch Unions gegenüber einer 
std::bitset<> überlegen, einfach weil die Union das Parsen halb für 
einen übernimmt und zusätzlich noch gescheite Strukturierung schafft. 
Gerade beim eventuellen späteren Debuggen von Binärdaten ist das 'ne 
Offenbarung gegenüber Lösungen per Cast.

In C++ ist aufgrund der unklaren Typisierung ist für Einsteiger 
natürlich die Verwendung von std::bitset<> anzuraten, für C würde ich 
auf jeden Fall die Lösung per Union bevorzugen.
1
typedef struct 
2
{
3
  unsigned int Value;
4
5
  struct 
6
  {
7
    unsigned int header1 :4;
8
    unsigned int header2 :4;
9
    unsigned int data1   :4;
10
    unsigned int parity  :1;
11
    unsigned int state   :1;
12
    //...usw. auf 32Bit aligned
13
  } Bits;
14
}   
15
16
} SensorFrame;
17
18
SensorFrame blub;
19
blub.Value = 0x30;
20
blub.Value = blub.Value << 2;
21
blub.Bits.data1 = 23;

von db8fs (Gast)


Lesenswert?

Sorry, das oberste 'struct' ist durch 'union' zu ersetzen:

typedef union
{
  unsigned int Value;

  struct
  {
    unsigned int header1 :4;
    unsigned int header2 :4;
    unsigned int data1   :4;
    unsigned int parity  :1;
    unsigned int state   :1;
    //...usw. auf 32Bit aligned
  } Bits;
}

} SensorFrame;

SensorFrame blub;
blub.Value = 0x30;
blub.Value = blub.Value << 2;
blub.Bits.data1 = 23;

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.