Forum: Compiler & IDEs Struct Hilfe


Announcement: there is an English version of this forum on EmbDev.net. Posts you create there will be displayed on Mikrocontroller.net and EmbDev.net.
von sascha_focus (Gast)


Bewertung
0 lesenswert
nicht lesenswert
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

von sascha_focus (Gast)


Bewertung
0 lesenswert
nicht lesenswert
Sorry,

sollte unsigned char sein

von g457 (Gast)


Bewertung
0 lesenswert
nicht lesenswert
> 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.

von Niklas Gürtler (Gast)


Bewertung
0 lesenswert
nicht lesenswert
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.

von sascha_focus (Gast)


Bewertung
0 lesenswert
nicht lesenswert
Kommunikations Schnittstelle ist korrekt. Es geht um SPI. Wie würde 
sowas denn mit dem Union aussehen?

Gruß Sascha

von MWS (Gast)


Bewertung
0 lesenswert
nicht lesenswert
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.

von S. R. (svenska)


Bewertung
0 lesenswert
nicht lesenswert
sascha_focus schrieb:
> Kommunikations Schnittstelle ist korrekt. Es geht um SPI. Wie würde
> sowas denn mit dem Union aussehen?

Ungefähr so:
1
typedef union {
2
  uint16_t alles;
3
  struct bits {
4
    unsigned ENABLE                 :1;
5
    unsigned RESET                  :1;
6
    unsigned NEW                    :1;
7
    unsigned EN                     :1;
8
    unsigned MODE                   :3;
9
    unsigned MODE2                  :1;
10
    unsigned UP                     :1;
11
    unsigned DOWN                   :1;
12
    unsigned DIRECT                 :1;
13
    unsigned STAT                   :1;
14
    unsigned MUX                    :1;
15
    unsigned CHANNEL                :3;
16
  };
17
}CFG;

Die saubere Lösung besteht darin, die Bits einzeln an zentraler Stelle 
ein- und auszupacken:
1
uint16_t encode(CFG x) {
2
  uint16_t tmp = 0;
3
  if(x.ENABLE) tmp |= 0x0001;
4
  if(x.RESET)  tmp |= 0x0002;
5
  /* usw */
6
  return tmp;
7
}
8
9
CFG decode(uint16_t x) {
10
  CFG x;
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
  return x;
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.

von Niklas Gürtler (Gast)


Bewertung
0 lesenswert
nicht lesenswert
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

von S. R. (svenska)


Bewertung
1 lesenswert
nicht lesenswert
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. ;-)

: Bearbeitet durch User
von Schon 100 mal (Gast)


Bewertung
0 lesenswert
nicht lesenswert
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

von A. S. (achs)


Bewertung
-1 lesenswert
nicht lesenswert
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.

von Schon 100 mal (Gast)


Bewertung
1 lesenswert
nicht lesenswert
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. ?

von A. S. (achs)


Bewertung
0 lesenswert
nicht lesenswert
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.

: Bearbeitet durch User
von Niklas Gürtler (Gast)


Bewertung
0 lesenswert
nicht lesenswert
Achim S. schrieb:
> Dann zeig doch Mal eine Lösung, die nicht viel kostet und wartbar ist
> (wartbar im Sinne von an einer Stelle)

https://erlkoenig90.github.io/uSer-doc/html/tutorial.html#ConWidth

von S. R. (svenska)


Bewertung
0 lesenswert
nicht lesenswert
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?

von Schon 100 mal (Gast)


Bewertung
-1 lesenswert
nicht lesenswert
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.

von Niklas G. (erlkoenig) Benutzerseite


Bewertung
0 lesenswert
nicht lesenswert
Hier noch ein Beispiel, wie die Aufgabenstellung mit der µSer-Bibliothek 
umgesetzt werden kann:
1
#include <iostream>
2
#include <cstdint>
3
#include <uSer/uSer.hh>
4
5
struct CFG {
6
  USER_STRUCT (CFG)
7
  USER_MEM(std::uint8_t, ENABLE, uSer::Width<1>)
8
  USER_MEM(std::uint8_t, RESET, uSer::Width<1>)
9
  USER_MEM(std::uint8_t, NEW, uSer::Width<1>)
10
  USER_MEM(std::uint8_t, EN, uSer::Width<1>)
11
  USER_MEM(std::uint8_t, MODE, uSer::Width<3>)
12
  USER_MEM(std::uint8_t, MODE2, uSer::Width<1>)
13
  USER_MEM(std::uint8_t, UP, uSer::Width<1>)
14
  USER_MEM(std::uint8_t, DOWN, uSer::Width<1>)
15
  USER_MEM(std::uint8_t, DIRECT, uSer::Width<1>)
16
  USER_MEM(std::uint8_t, STAT, uSer::Width<1>)
17
  USER_MEM(std::uint8_t, MUX, uSer::Width<1>)
18
  USER_MEM(std::uint8_t, CHANNEL, uSer::Width<3>)
19
20
  USER_ENUM_MEM(ENABLE, RESET, NEW, EN, MODE, MODE2, UP, DOWN, DIRECT, STAT, MUX, CHANNEL)
21
};
22
23
24
int main () {
25
  std::uint16_t raw [1];
26
  CFG myCfg { 1, 1, 1, 0, 3, 0, 0, 0, 0, 0, 0, 4 };
27
  uSer::serialize (raw, myCfg);
28
  
29
  std::cout << "0x" << std::hex << std::setw(4) << std::setfill('0') << raw [0] << std::endl;
30
}

Mehr Schreibarbeit als eine Union, aber dafür portabel und 
wohldefiniert.

von A. S. (achs)


Bewertung
0 lesenswert
nicht lesenswert
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.

von S. R. (svenska)


Bewertung
0 lesenswert
nicht lesenswert
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.

von A. S. (achs)


Bewertung
0 lesenswert
nicht lesenswert
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, ....

von Niklas G. (erlkoenig) Benutzerseite


Bewertung
1 lesenswert
nicht lesenswert
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.

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.