Einzelne Bit in Bitfeld in for Schleife ansprechen

#7741920
Lesenswert?

Hallo!

Ich habe in C/C++ (ATmega328) zum Speichern der Ausgangszustände ein Bitfeld definiert:

1
struct Ausgaenge        // jeweils 1 Bit fuer Speicherung der Zustaende der Ausgänge
2
{
3
        uint16_t A1:1;
4
        uint16_t A2:1;
5
        uint16_t A3:1;
6
        uint16_t A4:1;
7
        uint16_t A5:1;
8
        uint16_t A6:1;
9
        uint16_t A7:1;
10
        uint16_t A8:1;
11
        uint16_t A9:1;
12
        uint16_t A10:1;
13
        uint16_t A11:1;
14
        uint16_t A12:1;
15
        uint16_t A13:1;
16
}
17
Ausgang;

Nun wollte ich folgendes machen:

1
for (uint8_t i=1; i<=13; i++)
2
    {
3
        Serial.print("A");
4
        Serial.print(i);
5
        Serial.print(": ");
6
        Serial.println(Ausgang.A(i));

Funktioniert aber leider so nicht. Was mache ich falsch? Können einzelne Bits in Bitfelder überhaupt über Variablen so angesprochen werden? Ich hab mir jetzt so einige Diskussionen über Bitfelder durchgelesen und das Gefühl bekommen lieber die Finger davon zu lassen und stattdessen ganz klassich einfach ein ganzes Byte pro Merker zu verwenden. C hat anscheinend keine vernünftigen "Werkzeuge" um einzelne Bit zu adressieren. Zumindest habe ich den Eindruck bekommen. Ist dem so?

Danke!

Gruß Rick

#7741923
Lesenswert?

A13 ist ein Variablenname und damit kein Index A[13]. Selbst wenn es ein Index wäre, würde er nicht mit runden Klammern angesprochen.

(Es gibt Sprachen, in denen sich Feldnamen als Variable ansprechen lassen, aber C++ gehört nicht dazu. In Matlab wäre Ausgang.("A13") ein valider Ausdruck.)

(Für die Variablennamen in struct/Bitfields gilt das Gleiche wie für Variablennamen in anderen Speicherbereichen.)

Der sinnvolle Anwendungsbereich von Bitfeldern ist sehr begrenzt. Wahrscheinlich lässt sich Dein Problem auf irgendeine andere Weise besser lösen.

#7741932
Lesenswert?

Zwar könnte man sich eine halblegale Lösung mit einer Union basteln, nur erfordert die dann die passende Bitmaske, und damit ist der ganze Sinn des Bitfields dahin. Dann kannst du auch gleich auf dem uint arbeiten.

Da die Zustände der Ausgänge aber doch eh in den PORTn-Registern stehen, ist der Sinn der ganzen Aktion sowieso fraglich.

Oliver

#7741944
Lesenswert?

Rick M. schrieb:

Ich habe in C/C++ (ATmega328) zum Speichern der Ausgangszustände ein

C und C++ ist nicht das selbe! Das Serial.print sieht mir nach Arduino aus, also C++.

Wenn du mit ganzen Bytes arbeiten würdest "unsigned char A[(13+CHAR_BIT-1)/CHAR_BIT];" könntest du z.B. mit "A[i/CHAR_BIT] & (1<<(i%CHAR_BIT))" ein bestimmtes Bit abfragen. (hier ist i 0 relativ!).

Von Bitfeldern kann man keine Arrays haben, daher kann man da nicht einfach so eins per index abfragen. Man könnte dafür aber eine Funktion mit switch case anlegen:

1
bool get_bit(Ausgang a, int i){
2
  switch(i){
3
    case 0: return a.A0;
4
    case 1: return a.A1;
5
    case 2: return a.A2;
6
    ...
7
  }
8
}

Da wir hier C++ haben, könntest du auch den [] und = Operator überladen. Ich hab schon lange kein C++ mehr gemacht, irgend was in die richtung:

1
class BitRef {
2
private:
3
  const int bit;
4
  unsigned char*const byte;
5
public:
6
  BitRef(unsigned char* b, int i) : byte(b), bit(i) {
7
  }
8
  operator bool() const {
9
    return *this->byte & (1<<this->bit);
10
  }
11
  bool operator=(bool b){
12
    if(b) *this->byte |= (1<<this->bit);
13
    else *this->byte &= ~(1<<this->bit);
14
  }
15
};
16
struct Ausgaenge {
17
  BitRef operator[](int i){
18
    return BitRef(this.A[i/CHAR_BIT], i);
19
  }
20
  ...
21
};
#7741962
Lesenswert?

(a >> i) & 1

Oft ist es günstiger, a & i zu verwenden und dann i zu schieben, vor allem, wenn man mehrere Bits hintereinander abfragen will. i erfüllt zwei Aufgaben: Bitmaske und Schleifenvariable (die nicht gezählt, sondern geschoben wird). Wichtig ist ja nur, ob es gleich oder ungleich 0 ist.

for(uint16_t i=1<<13; i; i>>=1){Serial.println(a&i?"1":"0");}

#7741975
Lesenswert?

1
#include <stdlib.h>
2
#include <stdio.h>
3
#include <stdint.h>
4
#include <stdbool.h>
5
#include <string.h>
6
#include <ctype.h>
7

8
union {
9
  uint16_t allbits;
10
  struct {
11
    uint16_t bit0:1;
12
    uint16_t bit1:1;
13
    uint16_t bit2:1;
14
    uint16_t bit3:1;
15
    uint16_t bit4:1;
16
    uint16_t bit5:1;
17
    uint16_t bit6:1;
18
    uint16_t bit7:1;
19
    uint16_t bit8:1;
20
    uint16_t bit9:1;
21
    uint16_t bit10:1;
22
    uint16_t bit11:1;
23
    uint16_t bit12:1;
24
    uint16_t bit13:1;
25
    uint16_t bit14:1;
26
    uint16_t bit15:1;
27
  } bit;
28
} mybitfield;
29

30
int main() {
31
  mybitfield.allbits = 0x0102;
32

33
  printf("Bit 0 = %d\n", mybitfield.bit.bit0);
34
  printf("Bit 1 = %d\n", mybitfield.bit.bit1);
35

36
  printf("\n");
37

38
  for (uint16_t i=0; i<16; i++)
39
  {
40
    printf("Bit %d = %d\n", i , (mybitfield.allbits >> i) & 1 );    
41
  }
42

43
  printf("\n");
44

45
  return 0;
46
}
Persönliche Seite #7741978
Lesenswert?

Torsten B. schrieb:

Oft ist es günstiger, a & i zu verwenden und dann i zu schieben, vor allem, wenn man mehrere Bits hintereinander abfragen will. i erfüllt zwei Aufgaben:

Genau richtig, insbesondere auch weil die AVR keinen Barrelshifter haben und (1 << i) langsam ist (O(i) Takte). Okay, bei der Ausgabe via Serialport spielt das keine Rolle, aber an anderer Stelle vielleicht schon.

In C++ gibt es auch noch std::bitset was ziemlich genau das macht:

Daniel A. schrieb:

Da wir hier C++ haben, könntest du auch den [] und = Operator überladen.

Nur leider liefert der AVR-GCC ja die C++ stdlib nicht mit.

#7741981
Lesenswert?

Mal ne doofe frage zu meinem eigenen Code oben ...

Wie müsste ich meine Anfrage für eine KI formulieren, damit ich einen Code wie den von mir oben geschrieben bekomme?

Gestern gab es einen Thread über KI-Code generieren...

Man kann den Bits dann natürlich auch gleich einen Namen verpassen

1
union {
2
  uint16_t allbits;
3
  struct {
4
    uint16_t drucker:1;
5
    uint16_t endschalter1:1;
6
    uint16_t overtemp:1;
7
    uint16_t foobar:1;
8
.
9
.
10
.
11
  } status;
12
} mybitfield;
(Firma: Gast) #7741994
Lesenswert?

Niklas G. schrieb:

Daniel A. schrieb:

Da wir hier C++ haben, könntest du auch den [] und = Operator überladen.

Nur leider liefert der AVR-GCC ja die C++ stdlib nicht mit.

Das ist dabei kein Hinderungsgund!

Niklas G. schrieb:

In C++ gibt es auch noch std::bitset

Hier allerdings schon.

Wobei es auch für AVR eine Libstdc++ Implementierung gibt. Nicht vollständig, aber immerhin.

#7742031
Lesenswert?

Man kann das auch stur runterhacken. Nicht schön, keine statische oder dynamische Erkennung von Fehlern, die Typen sind unzweckmäßig und der Aufwand (bei mehr Pins) groß.

Aber es läuft erstmal und man kann weitermachen. Und sich später um Alternativen bemühen. Und wenn man mehrere Ports braucht, wird man Muster erkennen, die man vereinheitlichen kann.

1
uint16_t GetAusgang(int i)
2
{
3
    switch(i)
4
    {
5
    case 1: return Ausgang.A1;
6
    case 2: return Ausgang.A2;
7
    case 3: return Ausgang.A3;
8
    ...
9
    case 13: return Ausgang.A13;
10
    }
11
    return 0; /* wasimmer der Default sein soll im Fehlerfall */
12
}
13

14
void SetAusgang(uint16_t s)
15
{
16
    switch(i)
17
    {
18
    case 1: Ausgang.A1 = s; break;
19
    case 2: Ausgang.A2 = s; break;
20
    case 3: Ausgang.A3 = s; break;
21
    ...
22
    case 13: Ausgang.A13 = s; break;
23
    }
24
}
Moderator Persönliche Seite #7742032
Lesenswert?

Georg M. schrieb:

Torsten B. schrieb:

a & i

Sorry, das verstehe ich nicht.

Ja, ist ein wenig ungünstig ausgedrückt. Mit i ist hier eine Maske gemeint.

Beispiel:

1
  uint16_t mask = 1;
2
  for (uint16_t i=0; i<16; i++)
3
  {
4
    printf("Bit %d = %d\n", i , (mybitfield.allbits & mask) ? 1 : 0);
5
    mask <<= 1;
6
  }

Hier wird der Verschiebeoperator mit der Konstante 1 verwendet. Das ist auf einem AVR wesentlich effizienter als eine Verschiebung mit einer variablen Anzahl von Bits.

#7742050
Lesenswert?

Georg M. schrieb:

Bitfelder stehen am Anfang des Programmierens.

Mitnichten.

Ohne Bitfelder kann man den Mikrocontroller nicht programmieren.

Man hat in C schon Hardware-nahe programmiert, als es Bitfelder überhaupt noch nicht gab. Für solche Programmierung wurde C ursprünglich entwickelt.

Bitfelder wurden nicht für die Abbildung von Hardware in die Sprache eingebaut, sondern um Daten platzsparend unterzubringen. RAM war teuer, Disks waren es auch.

Moderator Persönliche Seite #7742056
Lesenswert?

Georg M. schrieb:

Was falsch?

Man braucht keine Bitfelder, um Bits zu manipulieren, siehe Bitmanipulation <-- draufklicken!

Von daher ist Deine Aussage:

Ohne Bitfelder kann man den Mikrocontroller nicht programmieren.

komplett falsch.

q.e.d.

Ich würde sie auch niemals einem Anfänger empfehlen. Bitfelder sind höchst inkompatibler Blödsinn.

Moderator Persönliche Seite #7742076
Lesenswert?

Georg M. schrieb:

(prx) A. K. schrieb:

Man hat in C schon Hardware-nahe programmiert, als es Bitfelder überhaupt noch nicht gab.

Also standen Bits irgendwie ganz chaotisch, ungeordnet im Speicher?

Man kann mit den Operatoren ~, <<, >>, & und | sämtliche Bitoperationen machen, die man braucht. Bitfelder, so wie sie in C definiert sind, sind überhaupt kein Muss, um Bitmanipulationen vorzunehmen.

Bitfelder in C sind - wie es A.K. schon sagte - dazu gedacht, Informationen kompakt zu speichern. Mit Bitmanipulationen hat das überhaupt nichts zu tun. Die Brücke zu Bitmanipulationen kann man nur bauen, indem man (höchst inkompatbile) Annahmen über die verwendete Hardware (Stichwort: Endianess) macht. Das ist aber niemals im Sinne der Erfinder der C-Bitfelder gewesen.

#7742087
Lesenswert?

Georg M. schrieb:

Also standen Bits irgendwie ganz chaotisch, ungeordnet im Speicher?

nicht chaotisch. Sondern auf eine von mehreren möglichen spezifizierten Weisen.

"Mehrere mögliche" bedeutet nicht portabel, jederzeit änderbar, nicht unbedingt so, wie man es braucht, per Compiler-Option auf einmal ganz anders.

Beispielsweise ist nicht klar, ob A1 im OP das höchst- oder niederwertigste Bit ist (also Daten-Bit "D0" oder "D15" repräsentiert).

Moderator Persönliche Seite #7742092
Lesenswert?

Georg M. schrieb:

Wie programmiert man die Register?

Zum Beispiel für eine Initialisierung eines UARTs auf eienm AVR so:

1
#if USE_2X
2
    UART0_UCSRA |= (1<<UART0_U2X);
3
#else
4
    UART0_UCSRA &= ~(1<<UART0_U2X);
5
#endif
6
    UART0_UCSRC = UART0_UCSZ1_BIT_VALUE | UART0_UCSZ0_BIT_VALUE | UART0_URSEL_BIT_VALUE;
7
    UART0_UCSRB |= UART0_TXEN_BIT_VALUE;                                                            // enable UART TX

Siehst Du da Code, wo C-Bitfelder genutzt werden?

Betrachtet man sie als Zahlen/Variablen, oder als Bitfelder?

Man kann sie als Bitfelder betrachten, ohne C-Bitfelder zu benutzen. Auch Dein zitierter Wikipedia-Artikel modifiziert mit den C-Bitfeldern keine Hardware-Register!

Falls Du es immer noch nicht gemerkt hast: Es geht um die Verwendung von C-Bitfeldern und nicht allgemein um Bitfelder, an denen Du Dich aufhängst.

#7742096
Lesenswert?

Das wäre nur bei statischen Daten ein Bug der Entwicklungsumgebung, weil nur die komplett genullt werden, Füllbits engeschlossen.

Bitfelder sind eher schwach definiert, was die Sprache angeht. Wer solche Konstrukte für zuverlässige Abbildung von Hardware nutzen will, sollte auf Ada umsteigen. Da wurde das von vorneherein genau definiert.

#7742108
Lesenswert?

Niklas G. schrieb:

Nein, die sind im C bzw. C++ Standard spezifiziert.

Mag sein. Aber sie sind nicht hardware-gebunden, was mit der o.g. falschen Aussage impliziert wird. Wer außer dem (Pre-) Compiler kümmert sich denn um die Bitfelder bzw. übersetzt sie in Maschinencode? Als Compiler-Erweiterung sehe ich Funktionen an, die in früheren Versionen nicht vorhanden waren.

Persönliche Seite #7742110
Lesenswert?

Rahul D. schrieb:

Mag sein. Aber sie sind nicht hardware-gebunden, was mit der o.g. falschen Aussage impliziert wird.

Was heißt "hardware-gebunden"? Aller Code läuft auf Hardware...

Rahul D. schrieb:

Als Compiler-Erweiterung sehe ich Funktionen an, die in früheren Versionen nicht vorhanden waren.

Also einen großen Teil der aktuellen C-Versionen? Da ist ja schon einiges hinzugekommen. Die meisten Leute verstehen unter Spracherweiterungen Features, welche nicht in der Spezifikation auftauchen und nur von einzelnen Compilern unterstützt werden.

#7742111
Lesenswert?

Georg M. schrieb:

Bitfelder stehen am Anfang des Programmierens. Ohne Bitfelder kann man den Mikrocontroller nicht programmieren.

Was dein Bild zeigt, ist überhaupt kein Bitfeld. Es ist ein Prozessorregister.

Ein Register oder eine Speicherzelle wird nicht dadurch zum Bitfeld, dass man die Bits in einer Dokumentation nummeriert und das Ganze dann "Bitfeld" nennt.

#7742113
Lesenswert?

Rahul D. schrieb:

Mag sein. Aber sie sind nicht hardware-gebunden, was mit der o.g. falschen Aussage impliziert wird.

Missverständlich. Sie müssen korrekt benutzt werden. Anfänger treffen hier aber oft Annahmen die so nicht definiert sind.

Bitfelder sind eine Möglichkeit eine 1 Bit Info unter einem Namen zu speichern.

Bitfelder sind aber kein Ersatz für Bitoperationen auf Bytes, wenn ich am Ende das ganze Byte brauche.

#7742116
Lesenswert?

Meistens ist es besser, auch 1 Bit Infos, einfach unter einem Byte zu speichern und auf Bitfelder zu verzichten. Es gibt so gut wie keinen Nachteil. Der gesparte Speicher dürfte heute keine Rolle mehr spielen.

Man kann die Variable ebenfalls benennen, man kann die in Arrays packen, in Strukturen. Ohne Probleme. Einfach den Unsinn mit Bitfelder sparen. Vor allem als Anfänger.

#7742156
Lesenswert?

Walter T. schrieb:

Daniel A. schrieb:

könnte man ja meinen, das wird alles genullt.

Könnte man meinen, das erste Feld würde genullt.

Bei C werden, wenn man ein Feld eines Structs initialisiert, alle anderen mit 0 initialisiert. Padding muss aber nicht zwingend genullt werden, und - das wusste ich damals nicht - anonyme Bitfelder auch nicht.

#7742182
Lesenswert?

Cyblord -. schrieb:

Meistens ist es besser, auch 1 Bit Infos, einfach unter einem Byte zu speichern und auf Bitfelder zu verzichten. Es gibt so gut wie keinen Nachteil. Der gesparte Speicher dürfte heute keine Rolle mehr spielen.

Man hat dann aber verloren, wenn die einzelnen Bits in einer Schleife verarbeitet werden sollen.

Rick M. schrieb:

Es handelt sich hierbei nicht um die Ausgänge des uC, sondern um Ausgänge einer Regelung deren Status über den DL-Bus eingelesen wurden.

Ich verstehe den TO auch so, er bekommt irgendwoher einen uint16 und möchte die einzelnen Bits auswerten. Da hilft es wenig ihm den Tipp zu geben für die Bits komplette Bytes zu verwenden.

#7742183
Lesenswert?

Obelix X. schrieb:

Cyblord -. schrieb:

Meistens ist es besser, auch 1 Bit Infos, einfach unter einem Byte zu speichern und auf Bitfelder zu verzichten. Es gibt so gut wie keinen Nachteil. Der gesparte Speicher dürfte heute keine Rolle mehr spielen.

Man hat dann aber verloren, wenn die einzelnen Bits in einer Schleife verarbeitet werden sollen.

Wieso das? Du kannst so viele Variablen wie du willst in einem Array halten und in Schleifen verarbeiten.

(Firma: 1984now) #7742226
Lesenswert?

(prx) A. K. schrieb:

Ob S. schrieb:

Doch haben sie. Eigentlich alle außer den "klassischen" Tinys.

Der mir bekannte Teil des AVR-Befehlssatzes kennt nur Shift/Rotate um eine Stelle.

Ja, das heißt aber nicht, dass es keinen Barrelshifter gäbe.

Man kann übrigens auch unter Verwendung der mul-Instruktion shiften...

OK, lohnt nur bei großen Shifts. So ungefähr ab 5 Bits, wenn ich das richtig in Erinnerung habe. Irgendwann hatte ich das mal analysiert, ist aber inzwischen mehr als ein Jahrzehnt her.

#7742230
Lesenswert?

Ob S. schrieb:

Heilige Einfalt. Was glaubst du, was ein "Hardware-Multiplizierer" wohl im Kern sein mag...

Ich habe eine gewisse Vorstellung davon, wie ein kombinatorischer Multiplizierer funktioniert und ging bisher davon aus, dass das so implementiert sei. Ein Barrel-Shifter ist nicht dabei. Aber wenn dir andere Informationen vorliegen: immer raus damit.

Moderator #7742275
Lesenswert?

Ob S. schrieb:

Man kann übrigens auch unter Verwendung der mul-Instruktion shiften...

OK, lohnt nur bei großen Shifts. So ungefähr ab 5 Bits, wenn ich das richtig in Erinnerung habe.

Ja, das geht mittels eines Zugriffs auf eine 8 Byte große Lookup-Tabelle mit den Werten {0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80} und einer Multiplikation. Das dürfte auf einem AVR sogar ab einem variablen Shift um 2 schneller als die Variante mit Schleife sein. Noch schneller geht es ohne MUL und ohne Schleife, wenn man dafür 8·256=2048 Bytes für die Lookup-Tabelle opfert.

(prx) A. K. schrieb:

Ob S. schrieb:

Heilige Einfalt. Was glaubst du, was ein "Hardware-Multiplizierer" wohl im Kern sein mag...

Ich habe eine gewisse Vorstellung davon, wie ein kombinatorischer Multiplizierer funktioniert und ging bisher davon aus, dass das so implementiert sei. Ein Barrel-Shifter ist nicht dabei.

Auch meine Vorstellungskraft reicht nicht aus, um in einem halbwegs sinnvoll implementierten Multiplizierer (egal welcher Architektur) einen Barrel-Shifter zu erkennen.

Aber vielleicht kann Ob. S. den Multiplizierer seiner Vorstellung skizzieren und darin den Barrel-Shifter markieren.

#7742284
Lesenswert?

Hallo,

ich würde es anders lösen.

Eine 16 Bit Variable für die 13 Bits um die Zustände zu speichern.

enums, Namen für Bitnummer, praktisch dein struct Bitfeld nur in enums.

Funktion zum setzen der enum Bitnummer in der 16 Bit Variable.

Funktion zum löschen der enum Bitnummer in der 16 Bit Variable.

Eine Funktion zum iterieren über die 13 Bit Positionen für Portübergabe etc..

Wenn man in einer for Schleife viele/alle Bitpositionen vergleichen möchte, dann fängt man mit shiften nicht immer bei 0 an. Man führt die aktuelle Bitposition vom Shift für den kompletten Schleifendurchlauf mit. Damit muss je Schleifendurchlauf immer nur einmal weiter geschiftet werden.

Damit die Bitschubserei schneller geht, für den Fall das man die aktuelle Bitpos. in einer Schleife nicht mitführen kann, kann man sich eine weitere Funktion anlegen, Bitnummer als Eingangsparameter, damit springt man in ein Switch-Case und bekommt die Wertigkeit des Bits zurück.

#7742301
Lesenswert?

Man sollte vor irgendwelchen "Optimierungen" erstmal klären, wie gut und schnell es überhaupt nötig ist! Die Standardlösungen sind vermutlich für 99% aller Anwendungen incl. des OP mehr als ausreichend!

https://www.mikrocontroller.net/articles/AVR-GCC-Codeoptimierung#Prinzipien_der_Optimierung

1
bool outputs[13];  // einfach, schnell, ein paar Bytes "verschwendet"
2

3
uint16_t outputs;  // Zugriff über Bitmasken, minimaler RAM-Bedarf, etwas mehr Programmspeicher und etwas "langsamer"
4

5
//Struct mit Bitfeld, wenig Ram, viel Programmspeicher, auch eher "langsam", viel Schreibarbeit
6

7
struct Ausgaenge        // jeweils 1 Bit fuer Speicherung der Zustaende der 
8
{
9
        uint16_t A1:1;
10
        uint16_t A2:1;
11
        uint16_t A3:1;
12
        uint16_t A4:1;
13
        uint16_t A5:1;
14
        uint16_t A6:1;
15
        uint16_t A7:1;
16
        uint16_t A8:1;
17
        uint16_t A9:1;
18
        uint16_t A10:1;
19
        uint16_t A11:1;
20
        uint16_t A12:1;
21
        uint16_t A13:1;
22
} Ausgang;
23

24
for (uint8_t i=1; i<=13; i++)
25
    {
26
        bool tmp;
27
        Serial.print("A");
28
        Serial.print(i);
29
        Serial.print(": ");
30
        switch(i) {
31
          case  1: tmp = Ausgang.A1; break;
32
          case  2: tmp = Ausgang.A2; break;
33
          case  3: tmp = Ausgang.A3; break;
34
          case  4: tmp = Ausgang.A4; break;
35
          case  5: tmp = Ausgang.A5; break;
36
          case  6: tmp = Ausgang.A6; break;
37
          case  7: tmp = Ausgang.A7; break;
38
          case  8: tmp = Ausgang.A8; break;
39
          case  9: tmp = Ausgang.A9; break;
40
          case 10: tmp = Ausgang.A10; break;
41
          case 11: tmp = Ausgang.A11; break;
42
          case 12: tmp = Ausgang.A12; break;
43
          case 13: tmp = Ausgang.A13; break;
44
       }  
45
       Serial.println(tmp);
46
     }
(Firma: 1984now) #7742309
Lesenswert?

Yalu X. schrieb:

Auch meine Vorstellungskraft reicht nicht aus, um in einem halbwegs sinnvoll implementierten Multiplizierer (egal welcher Architektur) einen Barrel-Shifter zu erkennen.

Nunja. Die Tatsache, dass die mul*-Instruktionen der AVR8 zwei und nicht nur einen Takt kosten, läßt stark vermuten, dass hier eine Kombination aus einem Barrel-Shifter und kombinatorischer Logik verwendet wurde.

Wär's rein kombinatorisch, sollte nämlich schlicht ein Takt reichen.

Aber das ist natürlich nur ein Indiz, kein wirklicher Beweis. Insofern gebe ich dir Recht.

Für wirkliche Beweise müßte man an Infos rankommen, die MC kaum freiwillig rausrücken dürfte.

Beitrag #7742415 wurde von einem Moderator gelöscht.
Moderator #7742427
Lesenswert?

Arduino F. schrieb:

Alexander schrieb:

Die eigentliche Frage war doch nur, ob es möglich ist zur Laufzeit Variablenbezeichner dynamisch zu konstruieren.

Ein klares nein! Das können nur Interpretersprachen.

Das nennt sich "Reflection" und ist nicht auf Interpretersprachen beschränkt. Beispiele für kompilierte Sprachen mit Reflection sind Java, C# (und andere .NET-Sprachen), Go, Object Pascal, Objective C und natürlich Common Lisp.

#7742448
Lesenswert?

Georg M. schrieb:

Ich denke, es ist schon ein Unterschied, ob man einen kleinen Mikrocontroller programmiert (größtenteils Hardware Programmierung)

Für Bitschubsereien, Konstanten und alles was vorab errechnet werden kann ist man bei C++ besser aufgehoben. In C teilen sich Präprozessor und Compiler die Arbeit nicht immer optimal, auch wenn C kontinuierlich nachzieht.

Allerdings sind unsere C++-Projekte meist aus dem Ruder gelaufen, weil es unüberschaubare Möglichkeiten gibt. Und da es uns nicht auf ein paar kB ankommt, sind wir wieder bei C. (Ja, man kann Programmierer zähmen und sogar einen Klammerstil vorgeben ;-).

#7742508
Lesenswert?

Daniel A. schrieb:

Obelix X. schrieb:

Ich verstehe den TO auch so, er bekommt irgendwoher einen uint16 und möchte die einzelnen Bits auswerten. Da hilft es wenig ihm den Tipp zu geben für die Bits komplette Bytes zu verwenden.

Naja, "x & (1ul<<i)" usw. funktioniert auch bei uint16_t.

Hast du den Beitrag gelesen, auf den ich mich beziehe?

#7742543
Lesenswert?

Georg M. schrieb:

verstehe ich immer noch nicht den praktischen Sinn, die Nützlichkeit.

Walter T. schrieb:

Der sinnvolle Anwendungsbereich von Bitfeldern ist sehr begrenzt.

Bitfelder sind dann sinnvoll, wenn Speicherplatz wirklich gespart werden muss.

Da die Variablen in Funktionen ohnehin nur sehr kurz leben, in Funktionen also fast nie.

Wenn man Daten mehr oder weniger dauerhaft speichern will, z.B. in einem EEPROM oder globale Flags, sind Bitfelder teilweise sinnvoll.

#7742552
Lesenswert?

Georg M. schrieb:

verstehe ich immer noch nicht den praktischen Sinn, die Nützlichkeit.

Wenn du keine ganzen Bytes brauchst, und du willst unbedingt etwas platz sparen, kannst du damit den Compiler Zeugs zusammenfassen lassen, und musst das nicht selbst manuell mit Bitshifts machen.

Ich hatte das z.B. mal verwendet, um bei einer Streaming UTF-8 Validierungsfunktion, den Zustand in nur 1 Byte (bzw. sogar nur 7 Bits, wird aber aufgerundet), zu packen: https://github.com/Daniel-Abrecht/ml666/blob/41d337313e4178bd2bbd882d6f7fa62c7cba44a6/include/ml666/utils.h#L62-L77 https://github.com/Daniel-Abrecht/ml666/blob/41d337313e4178bd2bbd882d6f7fa62c7cba44a6/src/utils.c#L341-L413 Ist sicher nicht besonders Effizient, und ich weiss nicht mehr, warum ich das in 1 Byte packen wollte. Ich glaube ich wollte das irgendwo in ein Struct Reinquetschen, wo gerade noch etwas Platz übrig war (Padding), und wo ich dieses klein halten wollte, weil es sehr oft verwendet worden wäre.

#7742554
Lesenswert?

Cyblord -. schrieb:

Aber genau hier lauert das Problem. Die Repräsentation von Bitfeldern im Speicher ist nicht garantiert.

Ist in den meisten Fällen nicht schlimm. Das EEPROM wird meist vom selben Programm gelesen wie vorher geschrieben.

Bei Dateien ist die Serialisierung sowieso komplizierter als eine einzelne Variable oder so zu dumpen.

#7742556
Lesenswert?

Cyblord -. schrieb:

Walter T. schrieb:

Wenn man Daten mehr oder weniger dauerhaft speichern will, z.B. in einem EEPROM oder globale Flags, sind Bitfelder teilweise sinnvoll.

Aber genau hier lauert das Problem. Die Repräsentation von Bitfeldern im Speicher ist nicht garantiert.

Nein, bei den beiden geschilderten Problemstellungen ist das kein Problem. Das Problem faengt erst da an, wenn irgendwer auf die geniale Idee kommt, irgendwelche Register, hinter denen sich HW verbirgt, mit sowas "schoener" machen zu wollen. Wo halt die Reihenfolge der Bitfelder von LSB zu MSB oder umgekehrt nicht mehr wurst ist.

Gruss WK

#7742559
Lesenswert?

Niklas G. schrieb:

Was heißt "hardware-gebunden"? Aller Code läuft auf Hardware...

Guck dir mal den 8051 an. Der hat Bitfelder bzw. Befehle, um Bits in Register direkt anzusprechen.

(prx) A. K. schrieb:

Der Präprozessor, der mit dem "Pre-" vielleicht gemeint ist, interessiert sich in keinster Weise für Sprachkonstrukte von C, soweit diese nicht mit # anfangen. Also auch nicht für Bitfelder.

Deswewgen auch in Klammern.

Cyblord -. schrieb:

Bitfelder sind aber kein Ersatz für Bitoperationen auf Bytes, wenn ich am Ende das ganze Byte brauche.

Genau. Was der Compiler daraus macht, könnte man auch von Hand erledigen. Das ist eine Compiler-Erweiterung, die Komfort schafft. Wo die definiert ist und wer die umsetzt, ist für Anfänger (und viele andere) ziemlich egal. Man sollte schon wissen, was passiert. Es ist ja keine Magie.

#7742563
Lesenswert?

Naja, bei Registern sollte das ja kein Problem sein. Die sind sowieso Platformabhängig, da macht es also nichts, sich auf das zu Verlassen, was der Compier da an verhalten spezifiziert (zumindest sofern er das tut). Dann muss man halt wissen, wie der das handhabt, genauso wie man auch wissen muss, was die Register tun.

Ein grösseres Problem ist, wenn manche meinen, sie könnten die Bitfelder Platformunabhängig zum Dekodieren von Protokollen nutzen, und es dann halt auf einer anderen Plattform nicht mehr geht, weil die da halt anders angeordnet wurden.

Rahul D. schrieb:

Das ist eine Compiler-Erweiterung, die Komfort schafft.

Nein, das ist keine Compiler-Erweiterung. Bitfelder sind teil des C Standards, es sind nur gewisse Aspekte davon IB.

#7742574
Lesenswert?

Daniel A. schrieb:

Ein grösseres Problem ist, wenn manche meinen, sie könnten die Bitfelder Platformunabhängig zum Dekodieren von Protokollen nutzen, und es dann halt auf einer anderen Plattform nicht mehr geht, weil die da halt anders angeordnet wurden.

Protokolle sollten schon eindeutig und nicht von irgendwelchen Endians o. dergl. abhängig sein. Und wenn, dann ist die Umsetzung das Problem des Entwicklers / Programmierers (m/w/d). Etwas Grundwissen sollte man schon haben oder sich erarbeiten.

Rahul D. schrieb:

Das ist eine Compiler-Erweiterung, die Komfort schafft.

Nein, das ist keine Compiler-Erweiterung. Bitfelder sind teil des C Standards, es sind nur gewisse Aspekte davon IB.

Der Ur-C-Compiler hatte bzw. der -Standard kannte diese Funktion nicht, also ist es eine Erweiterung. Die Bitfelder kamen doch erst in "letzter" Zeit dazu (ca. in den letzten 20 Jahren - bei einer Programmiersprache, die ca. 50 Jahre alt ist...). Dass der Standard mit der Zeit erweitert wird, ist ja nichts negatives.

Persönliche Seite #7742576
Lesenswert?

Rahul D. schrieb:

Guck dir mal den 8051 an. Der hat Bitfelder bzw. Befehle, um Bits in Register direkt anzusprechen.

Das können viele andere Architekturen auch.

Rahul D. schrieb:

Genau. Was der Compiler daraus macht, könnte man auch von Hand erledigen

Das gilt aber für viele Dinge in C. Das (a << b) lässt sich auf Plattformen wie AVR wie gesagt überhaupt nicht direkt umsetzen sondern braucht auch eine vom Compiler implementierte Hilfsfunktion. Sind Bitshifts jetzt auch eine "Compiler-Erweiterung"? Wie ist es mit 16bit-Multiplikation, Division, usw.?

Rahul D. schrieb:

Der Ur-C-Compiler hatte bzw. der -Standard kannte diese Funktion nicht, also ist es eine Erweiterung.

Aber eine Erweiterung durch den Standard, nicht einen Compiler. Somit ist der Begriff "Compiler-Erweiterung" ziemlich unpassend. Insbesondere auch weil dann fast alle Features die man heute so nutzt eine "Compiler-Erweiterung" wären, z.B. die "+=" Operatoren, Parameter-Typen, void-FUnktionen usw...

Persönliche Seite #7742606
Lesenswert?

Walter T. schrieb:

Wird jetzt gerade darüber diskutiert, dass 1 Befehl in einer Hochsprache nicht 1 Befehl in Assembler und auch nicht 1 Takt in der Ausführung entspricht?

Ja, nach Rahuls Logik sind Sprachelemente wie C-Bitfields die nicht direkt einzelne Assemblerbefehle übersetzt werden können "Compiler-Erweiterungen". Daher müssen beim AVR auch variable Bitshifts, 16bit-Multiplikationen und Zugriffe auf lokale Stack-Variablen (erfordern Adressberechnungen) "Compiler-Erweiterungen" sein.

(Firma: Privat) #7742615
Lesenswert?

Niklas G. schrieb:

Man kann Daten mit weniger als 8bit platzsparend unterbringen und dennoch recht komfortabel zugreifen, meistens auf Kosten der Laufzeit.

Ich meine, die Wertigkeit eines Bytes bleibt doch immer die gleiche, ob jetzt nur 3 Bit oder 8 Bit relevant sind. Platz für 8 Bit ist doch immer gegben. Also so wie auch Integervariablen auf einem 32Bit System in 4 Bytes gehalten werden.

Gibt es denn noch was kleineres als 1 Bit ? Dachte immer, das wäre die kleinste Einheit, so wie ich das mal vor über 35 Jahren gelernt habe :

1
1 Byte = 8 Bit
2
1 KB = 1024 Bytes
3
1 MB = 1024 KB
4
usw.

Jedenfalls kenne ich mal keine Sprache, die < 1 Bit kann. Speichertechnisch, wenn auf Datenträger geschrieben wird, sogar nur byteweise.

Klär mich doch mal auf.

#7742618
Lesenswert?

Niklas G. schrieb:

Aber eine Erweiterung durch den Standard, nicht einen Compiler. Somit ist der Begriff "Compiler-Erweiterung" ziemlich unpassend. Insbesondere auch weil dann fast alle Features die man heute so nutzt eine "Compiler-Erweiterung" wären, z.B. die "+=" Operatoren, Parameter-Typen, void-FUnktionen usw...

Habe ich mich auf einen speziellenn Compiler bezogen? Für die Kortinthenkacker: Bitfelder sind eine Erweiterung von C-Compilern (im Vergleich zu früheren Versionen, die dem Standard hinzugefügt wurde). Zufrieden?

Ob andere Programmiersprachen Bitfelder unterstüzen, weiß ich nicht (interessiert hier ja auch gar nicht).

Persönliche Seite #7742619
Lesenswert?

Heinz B. schrieb:

Jedenfalls kenne ich mal keine Sprache, die < 1 Bit kann

Hä? Davon habe ich überhaupt nicht geredet. Mit Bitfields kann man z.B. eine 3bit Variable, eine 4bit Variable und eine 1bit Variable in einem Byte unterbringen. Genau das was hier von Anfang an diskutiert wurde. Ich habe nur noch mal auf den Punkt gebracht, dass der Sinn von C-Bitfields diese Speicherersparnis ist und nichts anderes.

#7742623
Lesenswert?

Niklas G. schrieb:

Sind dann der "+=" Operator, void-Funktionen, Parameter mit Typangabe auch Erweiterungen für dich?

Nur mal so zur "Logik": Wenn es etwas in einem Standard bisher nicht gab, das aber im Standard definiert wurde: Wie sollte man das dann nennen? Wurde die Programmiersprache dann erweitert? Wer kümmert sich dann um die Umsetzung dieser Erweiterung im Maschinencode?

Persönliche Seite #7742627
Lesenswert?

Rahul D. schrieb:

Wie sollte man das dann nennen?

Sprachelement/Feature. Anfangs kann man noch "neu" dazu schreiben, aber "neu" sind C-Bitfields sicher nicht mehr.

Rahul D. schrieb:

Wer kümmert sich dann um die Umsetzung dieser Erweiterung im Maschinencode?

Weil alle Sprachfeatures von Compilern umgesetzt werden, auch die die von Anfang an in der Sprache waren, ist es unlogisch nur bei "neuen" Features ein "Compiler" voranzustellen. Logischer wäre es, den Ausdruck "Compiler-Erweiterung" nur für Features zu nutzen, die nur von einem bestimmten Compiler, aber nicht vom Standard unterstützt werden.

#7742629
Lesenswert?

Rahul D. schrieb:

Wenn es etwas in einem Standard bisher nicht gab, das aber im Standard definiert wurde: Wie sollte man das dann nennen?

Entweder es gibt etwas im Standard, oder eben nicht. Wenn man es genauer wissen will, kann man angeben, welchen Standard man meint.
In C89 z.B. sind bitfields schon drin, in C23 sind sie immer noch. Und ältere Standards gibt es nicht wirklich (Ausser man sieht K&R als (quasi-)Standard?).

Übrigens, jeder C Standard sagt immer, die älteren sind damit obsolete & ersetzt. Der C Standard ist also immer der neuste.

Ich persönlich bevorzuge aber C11/C17 gegenüber C23.

Moderator #7742639
Lesenswert?

Rahul D. schrieb:

Die Bitfelder kamen doch erst in "letzter" Zeit dazu (ca. in den letzten 20 Jahren - bei einer Programmiersprache, die ca. 50 Jahre alt ist...).

Da hast du dich etwas verschätzt. Die Bitfelder gab es zwar nicht von Anfang an, wurden bereits 1978 (also vor 46 Jahren) im K&R-Buch beschrieben.

Es gab sie somit schon mindestens 11 Jahre vor dem ersten ANSI- und 12 Jahre vor dem ersten ISO-Standard.

#7742640
Lesenswert?

Moin,

Yalu X. schrieb:

Es gab sie somit schon mindestens 11 Jahre vor dem ersten ANSI- und 12 Jahre vor dem ersten ISO-Standard.

Und ich bin mir auch ziemlich sicher, dass schon in einer einsemestrigen, kombinierten Pascal/C-Vorlesung, die ich weit zurueck im letzten Jahrtausend besuchte, eindringlich davor gewarnt wurde, die zu verwenden, weil nicht definiert ist, in welcher Reihenfolge/Richtung der Compiler die verbaut...

Gruss WK

(Firma: Privat) #7742652
Lesenswert?

Das ist ja mein Defizit : Was ist denn eine 1Bit oder 3Bit Variable ? Jedenfalls hatte ich im PC-Bereich noch nie was davon gehört. Mag es vl. im Mikroprozessorbereich geben. Selbst bei WINDOWS kann man nur byteweise Speicher allocieren. Allocmem verlangt da die Anzahl Bytes nicht die Anzahl Bits. Oder schreib mal hier, wie du mit der API 3 Bit Speicher allocierst.

#7742653
Lesenswert?

Yalu X. schrieb:

Da hast du dich etwas verschätzt. Die Bitfelder gab es zwar nicht von Anfang an, wurden bereits 1978 (also vor 46 Jahren) im K&R-Buch beschrieben.

Es gab sie somit schon mindestens 11 Jahre vor dem ersten ANSI- und 12 Jahre vor dem ersten ISO-Standard.

Ok. Mit C programmiere ich erst seit 2005. Danach wurden die Bitfelder populär (zumindest gab es zu der Zeit und danach hier im Forum diverse Anfragen zu dem Thema - soweit ich mich richtig erinnere).

#7742726
Lesenswert?

Rahul D. schrieb:

Mit C programmiere ich erst seit 2005. Danach wurden die Bitfelder populär

Das bezweifle ich. Gerade in den 20 Jahren davor war Portabilität im embedded-Bereich weniger wichtig. Es gab noch kein Internet, wo man seinen Kram für ganz andere Prozessorfamilien bereitgestellt hat.

Du hast einen neuen Prozessor, ein oder ein paar Dutzend Projekte damit und entweder vom Compilerhersteller oder nach ein paar Stunden sämtliche Register-Elemente zugreifbar in der Form Uart.Flags.OERR als einzelnes Bit. Oder als 5Bit breites Register von B2..B6 um schreiben zu können: Uart.BRR.PRE=31.

Davor kam eine Stunde blättern im Compilerhandbuch nach Reihenfolge, padding, etc. So wie Du auch zu Beginn wissen willst, was (-5%3) ergibt oder sizeof(int).

#7742758
Lesenswert?

Bruno V. schrieb:

Das bezweifle ich.

Tu, was du willst. Es sind meine Erfahrungen und Erinnerungen.

Gerade in den 20 Jahren davor war Portabilität im embedded-Bereich weniger wichtig. Es gab noch kein Internet, wo man seinen Kram für ganz andere Prozessorfamilien bereitgestellt hat.

Damals habe ich mir teilweise noch (AVR-)Datenblätter ausgedruckt.

Einen Kommentar zum Rest erspare ich mir, da ich den Sinn deines Geschrieben nicht verstehe.

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren