Hallo, ich sehe hier in einem Programm folgende eine Konstruktion, warum macht man das so? #define MAKRO 3 #define NEUESMAKRO ((MAKRO<<8) & 0xFF00) Wo ist der Unterschied zu #define NOCHEINNEUESMAKRO (MAKRO<<8) Was gewinnt man durch die UND-Operation an dieser Stelle? Sinn scheint ja wohl zu sein, dass das untere Byte definitiv auf Null steht. In welcher Situation könnte es denn passieren, dass nach dem Shift hier keine Null im niederen Byte steht?
Shift schrieb: > In welcher Situation könnte es denn passieren, dass nach dem > Shift hier keine Null im niederen Byte steht? Ich kenne keine. Meines Wissens werden beim Left-Shift immer 0en reingeschoben. Anders ist es beim Right-Shift im Zusammenhang mit vorzeichenbehafteten Variablen.
Shift schrieb: > Was gewinnt man durch die UND-Operation an dieser Stelle? Gar nichts. Es wäre möglich, dass diese Makros automatisch generiert wurden. Oder dass es andere Makros gibt, wo das UND notwendig ist, und es hier aus Konsistenzgründen beibehalten wurde.
Ich würde mal sagen, das "& 0xFF00" löscht der Optimizer einfach, d.h. der erzeugte Code ist der gleiche.
Allerdings habe ich auf Wikipedia folgende interessante Bemerkung gefunden: --------- Ebenso ist das Ergebnis laut C- und C++-Sprachnorm undefiniert, wenn die Anzahl der Bitverschiebungen größer oder gleich der Bitbreite der Rechenarchitektur ist.[2] Wird beispielsweise auf einer 32-Bit-Architektur von Intel-Prozessoren gearbeitet (IA32), so bewirkt eine Verschiebung um 32 Stellen oft gar keine Veränderung des Ergebnisses, d. h. für x = y << 32 ergibt sich x == y. Der Grund liegt in der Art und Weise, wie die Compiler die Schiebeoperation in Maschinencode umsetzen. --------- Und tatsächlich:
1 | #include <stdio.h> |
2 | #include <stdint.h> |
3 | |
4 | int main() |
5 | {
|
6 | uint32_t x = 0xFFFFFFFF; |
7 | |
8 | printf ("%u\n", x); |
9 | |
10 | x <<= 32; |
11 | |
12 | printf ("%u\n", x); |
13 | }
|
$ cc t.c && ./a.out t.c: In function 'main': t.c:10:5: warning: left shift count >= width of type x <<= 32; ^ 4294967295 4294967295 Hier passiert tatsächlich - bis auf die Warnung - ... nichts! Ersetze ich uint32_t durch uint8_t, initialisiere ich x mit 0xFF und schiebe nur um 8 Stellen, kommt aber tatsächlich nach dem Schieben wieder 0 raus. Fazit: Das vom TO angegebene Makro könnte höchstens relevant sein auf Büchsen, bei denen uint8_t identisch mit unsigned int ist. Aber da bringt das Schieben um 8 Stellen sowieso nichts.
:
Bearbeitet durch Moderator
@ Frank M. (ukw) Benutzerseite >Ebenso ist das Ergebnis laut C- und C++-Sprachnorm undefiniert, wenn die >Anzahl der Bitverschiebungen größer oder gleich der Bitbreite der >Rechenarchitektur ist.[2] Wird beispielsweise auf einer >32-Bit-Architektur von Intel-Prozessoren gearbeitet (IA32), so bewirkt >eine Verschiebung um 32 Stellen oft gar keine Veränderung des >Ergebnisses, d. h. für x = y << 32 ergibt sich x == y. Der Grund liegt >in der Art und Weise, wie die Compiler die Schiebeoperation in >Maschinencode umsetzen. >--------- Das halte ich für ein Gerücht. Sowohl was die Rechenarchitektur als auch das Verhalten bei IA32 angeht. Auch der AVR kann 32, ja sogar 64 Bit Variablöen verarbeiten, und das macht er auch korrekt! Eher ist so, das ein (konstantes) Lichtsschieben um mehr Bits als die Variable umfasst immer 0 ergibt, ggf. sogar optimiert ohne echtes Schieben. Der Compiler mag eine Warnung ausgeben, aber er MUSS die Schiebeoperation ausführen!
Falk B. schrieb: > Der Compiler mag eine Warnung ausgeben, > aber er MUSS die Schiebeoperation ausführen! Du meinst also, gcc hat hier einen Bug? Ich glaube sogar, dass gcc die Schiebeoperation durchführt, aber der Prozessor nicht. ;-) Nochmal die Ausgabe:
1 | $ cc t.c && ./a.out |
2 | t.c: In function 'main': |
3 | t.c:10:5: warning: left shift count >= width of type |
4 | x <<= 32; |
Der Compiler meckert also genau den Fall an, der bei Wikipedia erwähnt ist. Output:
1 | 4294967295 |
2 | 4294967295 |
0xFFFFFFFF um 32 Stellen nach links geschoben ergibt also 0xFFFFFFFF.
1 | # gcc --version |
2 | gcc (Debian 4.9.2-10) 4.9.2 |
1 | # cat /proc/cpuinfo | grep model |
2 | ... |
3 | model name : Intel(R) Core(TM) i7-2600 CPU @ 3.40GHz |
:
Bearbeitet durch Moderator
@ Frank M. (ukw) Benutzerseite >> Der Compiler mag eine Warnung ausgeben, >> aber er MUSS die Schiebeoperation ausführen! >Du meinst also, gcc hat hier einen Bug? Keine Ahnung, aber es würde mich schon arg wundern, wenn DAS ein normgerechtes Verhalten von C wäre. Wobei, es ist ja C . . . . 8-0 >Ich glaube sogar, dass gcc die Schiebeoperation durchführt, aber der >Prozessor nicht. ;-) Ein 2. FDIV Bug? ;-)
Falk B. schrieb: > Ein 2. FDIV Bug? ;-) Wohl eher ein Feature ;-) Zitat von: The C programming language: The result is undefined if the right operand is negative, or greater than or equal to the number of bits in the left expression’s type. Zitat von IA-32 Intel Architecture Software Developer’s Manual 3 The 8086 does not mask the shift count. However, all other IA-32 processors (starting with the Intel 286 processor) do mask the shift count to 5 bits, resulting in a maximum count of 31. This masking is done in all operating modes (including the virtual-8086 mode) to reduce the maximum execution time of the instructions. ----- Der Compiler muss also gar nichts bei Left-Shifts >= 32 (im Fall von uint32_t). EDIT: Sowohl auf einer Linux-Büchse mit ELF 32-bit als auch als ELF 64-bit Executable ist das Verhalten identisch.
1 | # file a.out |
2 | a.out: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.15, not stripped |
:
Bearbeitet durch Moderator
Also mal wieder so ein "tolles" Feature von C, ich hab's geahnt.
Falk B. schrieb: > Also mal wieder so ein "tolles" Feature von C, ich hab's geahnt. Aber so gut wie nicht relevant. Ich glaube nicht, dass wir beide jemals auf die Idee kommen werden, einen 32-Bit-Wert um 32 Bit oder mehr zu shiften. Das hört sich doch schon so ziemlich sinnlos an.
@ Frank M. (ukw) Benutzerseite >Aber so gut wie nicht relevant. Ich glaube nicht, dass wir beide jemals >auf die Idee kommen werden, einen 32-Bit-Wert um 32 Bit oder mehr zu >shiften. Das hört sich doch schon so ziemlich sinnlos an. Als konstante Verschiebung eher nicht, aber als variable Verschiebung ist das durchaus denkbar. In dem Fall kann man nur hoffen, daß die variable Verschiebung als Mehrfachverschiebung umgesetzt wird und somit zum erwarteten Ergebnis führt. Schön ist das aber nicht.
Falk B. schrieb: > Als konstante Verschiebung eher nicht, aber als variable Verschiebung > ist das durchaus denkbar. Ja, zum Beispiel in einer Schleife das Verschieben um eins. Oder (ungünstig ohne Barrel-Shifter) das Verschieben mit:
1 | for (i = 0; i < sizeof (uint32_t); i++) |
2 | {
|
3 | if (irgendwas & (1<<i)) |
4 | {
|
5 | setze_bit_auf_1(); |
6 | }
|
7 | else
|
8 | {
|
9 | setze_bit_auf_0(); |
10 | }
|
11 | }
|
Aber auch da läuft das i nur bis 31 und nicht 32. Von daher keine Gefahr. Wenn ich tatsächlich einen Wert von 32 Bit um mehr als 31 Bit variabel verschiebe, dann betrachte ich das als Bug in meinem Programm - nicht als Bug im Compiler.
@Frank M. (ukw) Benutzerseite >Ja, zum Beispiel in einer Schleife das Verschieben um eins. >Oder (ungünstig ohne Barrel-Shifter) das Verschieben mit: >for (i = 0; i < sizeof (uint32_t); i++) Da fehlt wohl ein *8, denn die Schleife ist nach 4 Durchläufen beendet. { > if (irgendwas & (1<<i)) Ich bin ein Fan von Bitmasken, vor allem auf dem AVR. Ist deutlich schneller. >Aber auch da läuft das i nur bis 31 und nicht 32. Von daher keine >Gefahr. Wenn ich tatsächlich einen Wert von 32 Bit um mehr als 31 Bit >variabel verschiebe, dann betrachte ich das als Bug in meinem Programm >- nicht als Bug im Compiler. Hmm, auch ein Blickwinkel 8-0
Falk B. schrieb: > Da fehlt wohl ein *8, denn die Schleife ist nach 4 Durchläufen beendet. Stümmt :-)
Shift schrieb: > #define MAKRO 3 > #define NEUESMAKRO ((MAKRO<<8) & 0xFF00) > > Wo ist der Unterschied zu > > #define NOCHEINNEUESMAKRO (MAKRO<<8) Das Ergebnis wird auf die 16 Bits geclippt, wenn MAKRO mal größer als 255 ist. Bei 32 Bit Registern wäre das dann nicht mehr egal. Das macht man oft wenn die höherwertigen Bits eine andere Funktion haben. Und ja, im Beispiel überflüssig und spätestens vom Optimizer entfernt.
Frank M. schrieb: > Fazit: Das vom TO angegebene Makro könnte höchstens relevant sein auf > Büchsen, bei denen uint8_t identisch mit unsigned int ist. Weniger als 16 Bits für int/unsigned sind nicht zum C Standard konform.
:
Bearbeitet durch User
Frank M. schrieb: > Ich glaube nicht, dass wir beide jemals > auf die Idee kommen werden, einen 32-Bit-Wert um 32 Bit oder mehr zu > shiften. Man soll ja Nichts auf die lange Bank schieben. :) MfG Paul
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.