Hallo,
wer kann mir bei einem für mich unbekannten Code helfen.
Was bedeutet die Zahl mit dem Doppelpunkt in der struct?
struct
{
u32 var1 : 8;
u32 var2 : 8;
u32 var3 : 1;
u32 var4 : 1;
} test;
Und der andere Befehl:
UINT8 variable[2048] ={0};
0[(UINT32*)variable]
1[(UINT32*)variable]
Kann mir jemand sagen was da passiert?
Ich hoffe mir kann jemand helfen :)
LG Josef
Josef schrieb:> Und der andere Befehl:> UINT8 variable[2048] ={0};>> 0[(UINT32*)variable]> 1[(UINT32*)variable]>> Kann mir jemand sagen was da passiert?
Da hat jemand den 'obfuscated Programmer' raushängen lassen um damit
andere zu verwirren.
Das ist nichts anderes als
1
((UINT32*)variable)[0];
2
((UINT32*)variable)[1];
wobei das je nachdem in welchem Zusammenhang es vorkommt, auch als
1
*(UINT32*)variable
2
*((UINT32*)variable+1)
besser geschrieben werden könnte. Kommt aber auf den Zusammenhang an,
was dann klarer ist.
Danke für eure Antworten!
> Das ist nichts anderes als> ((UINT32*)variable)[0];> ((UINT32*)variable)[1];>> wobei das je nachdem in welchem Zusammenhang es vorkommt, auch als> *(UINT32*)variable> *((UINT32*)variable + 1)> besser geschrieben werden könnte. Kommt aber auf den Zusammenhang an,> was dann klarer ist.
Also ruft er aber auch wirklich immer UINT32 also 4 Byteweise auf das
Array zu und liest somit die ersten 4 Bytes und dann die nächsten 4
Bytes raus.
Wie kommt man eigentlich auf so ein Konstrukt? In welcher abstrusen
Literatur findet man so etwas :D
Zum Thema Bitfields ... Wozu macht man sich so etwas? Wenn ich das
richtig gelesen habe, kann man sich nicht sicher sein wie der compiler
den Speicher anlegt. Damit weiß man auch nicht ob man Speicherplatz
spart und von der Geschwindigkeit dürfte es somit auch schlechter sein
oder genauso als wenn man es nicht macht.
Kann man dann auch mit größeren Werten ein Bitfeld, also eine Variable
mit der anderen Überschreiben? Wenn Var1 4 Bits hat und ich dort einen
Wert größer 2^3 reinschreibe, zerschiesst er mir den nächsten? oder
fängt genau dass, das System ab? Dann wird es ja auch immer langsam wenn
man drauf zu greift.
Josef schrieb:> Wie kommt man eigentlich auf so ein Konstrukt? In welcher abstrusen> Literatur findet man so etwas :D
Das ist ein Nebeneffekt, wie Array-Indizierung in C definiert ist
1
a[i] <==> *( a + i )
genau deswegen kannst du ja auch mit Indizierungssyntax auf ein Array
zugreifen, welches du dir mit malloc allokiert hast, und von dem du nur
einen Pointer auf das erste Element hast. Aus genau dem gleichen Grund
funktioniert die 'Übergabe' eines Arrays an eine Funktion, in dem die
Funktion einen Pointer auf das erste Element eines Arrays hat.
Und es ist auch logisch:
Denn das Element a[i] eines Arrays a ist genau jenes Element, welches
vom Beginn des Arrays an gerechnet, i Einheiten entfernt ist.
Aber:
Addition ist kommutativ. a+i ist dasselbe wie i+a. Auch bei Pointern und
Offsets zu Pointern. Daher kann man
1
a[i] ===> *( a + i )
2
3
|
4
|
5
v
6
7
*( i + a )
8
9
i[a] <======
a[i] ist also dasselbe wie i[a]. Und zwar in genau der gleichen Art und
Weise, wie es keine Rolle spielt ob du
1
int*ptr=....;
2
intoffset=....;
3
4
intvalue=*(ptr+offset);
schreibst, oder
1
intvalue=*(offset+ptr);
Normalerweise benutzt das keiner, weil es unsinnig ist, sich da selbst
ein Ei zu legen.
Josef schrieb:> Zum Thema Bitfields ... Wozu macht man sich so etwas?
Damit man in einem Byte alle 8 Bit benutzen kann, selbst dann wenn man 8
Werte hat, die nur 0 oder 1 sein können (also die klassischen Bits).
Schreibst du
1
uint8_thaveError;
2
uint8_thaveOverflow;
3
uint8_tisRed;
4
uint8_tisGreen;
5
uint8_tisBlue;
6
uint8_tframeError;
7
uint8_tanswerReceived;
8
uint8_tclockwise;
dann verbrauchen all diese Ja/Nein Entscheidungen zusammen 8 Stück
uint8_t.
Schreibst du das als
1
structflags_t
2
{
3
uint8_thaveError:1;
4
uint8_thaveOverflow:1;
5
uint8_tisRed:1;
6
uint8_tisGreen:1;
7
uint8_tisBlue:1;
8
uint8_tframeError:1;
9
uint8_tanswerReceived:1;
10
uint8_tclockwise:1;
11
}Flags;
dann verbrauchen dieselben Informationen nur einen uint8_t. Und es gibt
Fälle, da ist dir das wichtiger als der etwas erhöhte Aufwand beim
zugriff auf die Einzelteile.
(Unter der Annahme, dass ein uint8_t aus 8 Bit besteht, was wohl der
Regelfall sein wird)
> Wenn ich das richtig gelesen habe, kann man sich nicht sicher> sein wie der compiler den Speicher anlegt.
Ganz so einfach ist das nicht.
Der Compiler muss das Zeugs schon zusammenpacken. Aber wie er die Bits
vergibt, das ist seine Sache.
> Damit weiß man auch nicht ob man Speicherplatz spart und von der> Geschwindigkeit dürfte es somit auch schlechter sein
Wenn der COmpiler überhaupt nicht packen würde, warum soll das dann von
der Geschwindigkeit her schlechter sein?
> Kann man dann auch mit größeren Werten ein Bitfeld, also eine> Variable mit der anderen Überschreiben?
Nein.
Definierst du einen Member eines Bitfields mit 3 Bit, dann passen da
auch nur Werte rein, die sich mit 3 Bit darstellen lassen. Du kannst da
nichts größeres zuweisen und du kriegst da auch nichts größeres raus.
> Wenn Var1 4 Bits hat und ich dort einen Wert größer 2^3 reinschreibe,> zerschiesst er mir den nächsten?
Nein
> oder fängt genau dass, das System ab? Dann wird es ja auch immer> langsam wenn man drauf zu greift.
Natürlich.
Eine alte Regel in der Informatik besagt "Time for Space".
Und das bedeutet, dass man sich oft Speicherplatz mit Laufzeit erkauft
bzw. umgekehrt Laufzeit holt indem man Speicher opfert. Irgendeinen Tod
muss man dann eben sterben. Niemand zwingt dich Bitfelder einzusetzen.
Wenn dir die Laufzeit wichtiger ist als 7 verschwendete Bytes UND
Zugriffe häufig vorkommen, dann wird man das eben nicht so benutzen.
Josef schrieb:> Zum Thema Bitfields ... Wozu macht man sich so etwas?
Mal angenommen du hast ein Programm das schon seit Jahren läuft. Jetzt
soll das Programm erweitert werden: Das Programm soll jetzt noch einen
kompletten Zeitstempel (Jahr:Monat:Tag:Stunde:Minute:Sekunde) "nebenbei"
versenden. Das bestehende kommunikations Protokoll erlaubt dir, eine
Payload von 4 Byte, nicht mehr!
Dann wäre eine Möglichkeit:
Die Sekunden werden durch 2 geteilt. Damit wird 1 Bit gespart und der
Fehler im Timestamp ist akzeptabel gering (je nach Anforderung)(Muss
beim Empfänger natürlich wieder mit 2 Multipliziert werden). Jahr begint
beim Jahr 2000. 7bit reichen von 0 bis 127, also bis zum Jahre 2127,
wobei bei der Übertragung die erste Ziffer, also die "2" weggelassen
wird, da sich diese Ziffer in absehbarer Zeit nicht ändert. (Ggf. könnte
man auch dem Jahr nur 6bit statt 7bit(geht dafür dann halt nur bis
2063), und den Sekunden dafür 6bit statt 5bit spendieren)
Das mit den Sekunden und dem Weglassen der ersten Ziffer bei dem Jahr
ist in diesem Falle eine Protokoll vereinbarung.
Um also deine Frage zu beantworten:
Der Zweck heiligt die Mittel!
Statt einem Bitfeld kannst du natürlich auch einfach einen uint32_t
nehmen und wie ein Weltmeister shiften. Wenn ich mich aber so an den
einen oder anderen Forenbeitrag erinnere (wo versucht wurde eine 0 zu
shiften
(z.b. 0 << 5) ) bevorzuge ich dann doch lieber das Bitfeld (lesbarer und
weniger fehleranfällig).
Grüße
Kaj schrieb:> Mal angenommen du hast ein Programm das...
Was soll uns dieses längliche Märchen von maximal miesem Code sagen? Was
ein Bitfeld ist, lässt sich auch in einem Satz erklären.
Da Dieter schrieb:> Was> ein Bitfeld ist, lässt sich auch in einem Satz erklären.
Es ging nicht darum was ein Bitfeld ist, sondern wozu man es benutzen
kann!
Da Dieter schrieb:> maximal miesem Code
Noch nie mit Kunden und Code/Hardware gearbeitet die nicht deiner Feder
entsprungen ist, was?
Karl Heinz schrieb:> Das ist nichts anderes als>> ((UINT32*)variable)[0];> ((UINT32*)variable)[1];
Und wozu genau soll das gut sein, wenn da keinerlei Zuweisung erfolgt?
Mark Brandis schrieb:> Karl Heinz schrieb:>> Das ist nichts anderes als>>>> ((UINT32*)variable)[0];>> ((UINT32*)variable)[1];>> Und wozu genau soll das gut sein, wenn da keinerlei Zuweisung erfolgt?
Die Semikolons sind ihm unbeabsichtigterweise reingerutscht. Aus dem
Kontext geht ja klar hervor, dass es hier um Ausdrücke und nicht um
Anweisungen geht.
Hallo,
Karl Heinz schrieb:> Addition ist kommutativ. a+i ist dasselbe wie i+a. Auch bei Pointern und> Offsets zu Pointern. Daher kann man> a[i] ===> *( a + i )>> |> |> v>> *( i + a )>> i[a] <======>> a[i] ist also dasselbe wie i[a].
Ich frage mich warum sich niemand über diese neckische Rechnung aufregt?
Das Ganze ist zwar formal absolut in Ordnung erweckt aber auch einen
absolut falschen Eindruck. a und i haben zwar ihre Positionen getauscht
aber nicht ihre Bedeutung. Bei i[a] ist a noch immer der Pointer auf das
Array und i der Index. Wäre es anders wäre es ein schlimmer Fehler!
Michi schrieb:> Ich frage mich warum sich niemand über diese neckische Rechnung aufregt?
Wahrscheinlich deshalb, weil alle verstanden haben, worin der springende
Punkt liegt.
> Das Ganze ist zwar formal absolut in Ordnung erweckt aber auch einen> absolut falschen Eindruck. a und i haben zwar ihre Positionen getauscht> aber nicht ihre Bedeutung.
Hab ich auch nicht behauptet.