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 | int offset = ....; |
3 | |
4 | int value = *( ptr + offset ); |
schreibst, oder
1 | int value = *( 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_t haveError; |
2 | uint8_t haveOverflow; |
3 | uint8_t isRed; |
4 | uint8_t isGreen; |
5 | uint8_t isBlue; |
6 | uint8_t frameError; |
7 | uint8_t answerReceived; |
8 | uint8_t clockwise; |
dann verbrauchen all diese Ja/Nein Entscheidungen zusammen 8 Stück uint8_t. Schreibst du das als
1 | struct flags_t |
2 | {
|
3 | uint8_t haveError : 1; |
4 | uint8_t haveOverflow : 1; |
5 | uint8_t isRed : 1; |
6 | uint8_t isGreen : 1; |
7 | uint8_t isBlue : 1; |
8 | uint8_t frameError : 1; |
9 | uint8_t answerReceived : 1; |
10 | uint8_t clockwise : 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:
1 | struct timestamp_t |
2 | {
|
3 | uint8_t Jahr : 7; |
4 | uint8_t Monat : 4; |
5 | uint8_t Tag : 5; |
6 | uint8_t Stunde : 5; |
7 | uint8_t Minute : 6; |
8 | uint8_t Sekunde: 5; |
9 | } TimeStamp; |
10 | // 0000000 0000 00000 00000 000000 00000 <-- 32bit
|
11 | //| Jahr | Monat | Tag | Stunde | Minute |Sekunde
|
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: > a[i] ist also dasselbe wie i[a]. Insbesonders kann man sich mit i["foobar"] die Buchstaben einzeln aus einem String greifen :-)
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.
Vielen Dank euch allen für die guten Beiträge. Ihr habt mir viel weiter geholfen.
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.