Hallo zusammen,
habe ein struct angelegt. Bestehend aus mehreren Statusbits.
typedef struct {
unsigned ENABLE :1;
unsigned RESET :1;
unsigned NEW :1;
unsigned EN :1;
unsigned MODE :3;
unsigned MODE2 :1;
unsigned UP :1;
unsigned DOWN :1;
unsigned DIRECT :1;
unsigned STAT :1;
unsigned MUX :1;
unsigned CHANNEL :3;
}CFG;
Zusammengerechnet ergibt dies zwei Bytes. Nun möchte ich zum Beispiel
das
definierte Struct in eine 16Bit Variable kopieren. Nun suche ich eine
Möglichkeit, dem Compiler mitzuteilen, das mein Struct auch da
reinpasst.
Gruß Sascha
> Nun suche ich eine Möglichkeit, dem Compiler mitzuteilen, das mein> Struct auch da reinpasst.
Das brauchst Du ihm nicht zu sagen, das weiss der auch so. Ich vermute
Du suchst was anderes: Wenns standardwidrig sein darf man union, sonst
man 3 memcpy.
Möchtest du die Daten über eine Kommunikations Schnittstelle abschicken
oder abspeichern? Dann geht das nicht direkt, da C(++) das Layout von
Bitfeldern nicht definiert. Unter Serialisierung ist das mit Lösung
erläutert.
sascha_focus schrieb:> Nun suche ich eine> Möglichkeit, dem Compiler mitzuteilen, das mein Struct auch da> reinpasst.
__attribute__((packed))
wär' zu erwähnen.
sascha_focus schrieb:> Kommunikations Schnittstelle ist korrekt. Es geht um SPI. Wie würde> sowas denn mit dem Union aussehen?
Ungefähr so:
1
typedefunion{
2
uint16_talles;
3
structbits{
4
unsignedENABLE:1;
5
unsignedRESET:1;
6
unsignedNEW:1;
7
unsignedEN:1;
8
unsignedMODE:3;
9
unsignedMODE2:1;
10
unsignedUP:1;
11
unsignedDOWN:1;
12
unsignedDIRECT:1;
13
unsignedSTAT:1;
14
unsignedMUX:1;
15
unsignedCHANNEL:3;
16
};
17
}CFG;
Die saubere Lösung besteht darin, die Bits einzeln an zentraler Stelle
ein- und auszupacken:
1
uint16_tencode(CFGx){
2
uint16_ttmp=0;
3
if(x.ENABLE)tmp|=0x0001;
4
if(x.RESET)tmp|=0x0002;
5
/* usw */
6
returntmp;
7
}
8
9
CFGdecode(uint16_tx){
10
CFGx;
11
memset(&x,0,sizeof(CFG));
12
if(uint16_t&0x0001)x.ENABLE=1;
13
if(uint16_t&0x0002)x.RESET=1;
14
/* usw */
15
returnx;
16
}
Dann kannst du statt einzelnen Bits auch bool benutzen und mit
"true"/"false" arbeiten. Manche Compiler mögen das lieber.
Damit garantierst du, dass es überall funktioniert und du hast das
Datenformat gleichzeitig eindeutig definiert. Schnittstellen gehören
immer sauber definiert.
Normalerweise bin ich für die Union-Lösung, aber in deinem Fall ist sie
absolut ungeeignet. Was man innerhalb seines Programms macht, bleibt
einem selbst überlassen, aber in dem Moment, wo Kommunikation
stattfindet, lässt man das besser sein.
Die Union Lösung ist halt in C Implementation defined d.h. unportabel,
und in C++ komplett verboten. Daher ist die 2. Lösung mit den
Bit-Operationen deutlich besser. Da ist nur ein Tippfehler - es sollte
wohl "x" statt uint16_t heißen
Niklas Gürtler schrieb:> Da ist nur ein Tippfehler - es sollte> wohl "x" statt uint16_t heißen
Das ist vollkommen korrekt. ;-)
Und statt "x" zweimal zu vergeben, sollte man vielleicht eins davon
anders benennen. Aber hey, wofür hat man Compiler. ;-)
g457 schrieb:>> Nun suche ich eine Möglichkeit, dem Compiler mitzuteilen, das> mein>> Struct auch da reinpasst.>> Das brauchst Du ihm nicht zu sagen, das weiss der auch so. Ich vermute> Du suchst was anderes: Wenns standardwidrig sein darf man union, sonst> man 3 memcpy.
Union geht nicht. Wenn doch, dann zuvall
WENN es für eine konkrete Plattform ist,
UND du deinen Compiler kennst
UND konfigurierst
UND Du entsprechende asserts einbaust,
DANN kannst Du Unions verwenden.
Unions sind halt im Zugriff ausdrucksstärker als die portablen Aufsätze.
Ja, in 10 Jahren wird man dir das vorwerfen, ... hättest Mal sofort
richtig gemacht, .... Die Pharisäer, die alles richtig machen und bei
deren Software keiner Änderung braucht.
Schwachsinn, nicht in 100 Jahren, sondern heute. Code sollte soweit wie
möglich portablen gehalten werden. Für die Aufgabe gibt es einfache
Alternativen. Warum dann so einen sch.. machen?
Mach es gleich richtig, kostet nichts und tut nicht weh. ?
Schon 100 mal schrieb:> Code sollte soweit wie möglich portablen gehalten werden.
Portabler Code, ja. Bei HW- oder plattformspezifischem Code nein. Ein
Registerlayout mit structs (statt diesem 1<<PIN1) ist besser les- und
statisch analysierbar.
Schon 100 mal schrieb:> Mach es gleich richtig, kostet nichts und tut nicht weh.
Dann zeig doch Mal eine Lösung, die nicht viel kostet und wartbar ist
(wartbar im Sinne von an einer Stelle)
Oh, sorry, meinte eigentlich kann structs verwenden, aber das Fass ist
ähnlich. Ob der TO Unions braucht, wissen wir ja nicht.
Achim S. schrieb:>> Mach es gleich richtig, kostet nichts und tut nicht weh.> Dann zeig doch Mal eine Lösung, die nicht viel kostet und wartbar ist> (wartbar im Sinne von an einer Stelle)
Hab ich doch schon gemacht, Fipptehler inklusive?
Oder wie würdest du das anderweitig machen?
Die vorgeschlagene Union Variante ist natürlich Groten–schlecht. Auch
wenn sie auf den ersten Blick funktioniert, hat es nichts - aber auch
gar nichts - mit C Programmierung zu tun.
Es wäre nur eine zufällige, vom Compiler geduldete, Ausfuehrung.
S. R. schrieb:> Hab ich doch schon gemacht, Fipptehler inklusive?
Ja, aber da muss ich zum enum noch 2 konvertierungsroutinen schreiben.
Statt einmal den Compiler zu konfigurieren. Bei 5 structs vielleicht
noch OK. Oder für einen Plattformunabhangigen Treiber.
> Oder wie würdest du das anderweitig machen?
Ja. Structs + asserts.
Zudem ist es überhaupt nicht klar, ob der TO die Struktur überhaupt zu
anderen kommuniziert oder nur in einem seriellen eeprom speichert oder
dessen Bits manipuliert.
Achim S. schrieb:> Ja, aber da muss ich zum enum noch 2 konvertierungsroutinen schreiben.
Das macht man einmal nach Spec und gut ist. Allein die Verifikation des
Datenformats mit dem aktuellen Compiler (plus die Asserts) würde
vermutlich länger dauern als die beiden Routinen zu schreiben.
> Bei 5 structs vielleicht noch OK.> Oder für einen Plattformunabhangigen Treiber.
Wenn du 200 solcher Structs hast, dann ist die Lage ein bisschen anders,
zugegeben. Allerdings - und damit hab ich oft genug zu tun - hast du
dann meist noch ganz andere Probleme. Oder es handelt sich um
generierten Code, dann kann man die Routinen auch gleich mit generieren
lassen.
Schon 100 mal schrieb:> Es wäre nur eine zufällige, vom Compiler geduldete, Ausfuehrung
Naja, den Zufall kann man hier auch sicherstellen.
Die vorgeschlagenen Lösungen sind halt alle schwer wartbar, also write
only. Für portable Treiber OK, z.b.ein TCP/IP Stack. Für Systeme, die
sich laufend weiter entwickeln, sind die Kosten enorm und müssen mit
einkalkuliert werden. Und allignment und endianess kommen ja nochmal
oben drauf, und n Bits im Byte, ....
Achim S. schrieb:> Naja, den Zufall kann man hier auch sicherstellen.
Indem man bloß nie die Plattform/Compiler-Version/Optionen ändert und
auch noch in 20 Jahren den gleichen Controller nutzt?
Achim S. schrieb:> Und allignment und endianess kommen ja nochmal> oben drauf, und n Bits im Byte, ....
Ganz genau das ist das Problem der Union. Genau deshalb sollte man es
lieber mit bitweisen Operationen machen.
Achim S. schrieb:> Die vorgeschlagenen Lösungen sind halt alle schwer wartbar, also write> only.
Ich finde meine Lösung ist hinreichend gut wartbar. Die Syntax ist etwas
wortreich, aber es ist simpel, das Format anzupassen.