Hallo Leute, ich verwende seit langem dieses C-Makro zur Statusabfrage eines einzelnen Pins ohne 100%-ig sicher zu sein ob das sauber ist: #define _ENC_S ((PINB>>_ENCPin)&1) üblich ist ja eher: #define bit_is_set(var, bit) ((var) & (1 << (bit))) Ist der einzige Unterschied, dass letzteres auch mit einer Maske aus mehreren bits funktioniert, während das oben nur eine Einzelbit-Funktionalität hat? Danke euch, Bernd
Bernd K. schrieb: > #define bit_is_set(var, bit) ((var) & (1 << (bit))) > > Ist der einzige Unterschied, dass letzteres auch mit einer Maske aus > mehreren bits funktioniert Tut es nicht. Hauptvorteil ist aber, daß bei fester Bitnummer das Shiften zur Compilezeit aufgelöst werden kann.
Wobei es möglich ist, dass ein Compiler die erste Variante auch optimiert, wenn er darf, aber bei der zweiten ist das einigermassen sicher. Bei abgeschalteter Optimierung könnte die erste Variante ein Griff ins Klo sein.
A. K. schrieb: > Wobei es möglich ist, dass ein Compiler die erste Variante auch > optimiert, wenn er darf Direkt optimieren kann er das nicht, weil die Variable da geshiftet wird, und die speist sich letztlich ja aus memory mapped IO mit volatile. Er könnte höchstens SEHR schlau sein und Variante 1 selber auf Variante 2 umschreiben, aber darauf würde ich mich nicht verlassen.
Nop schrieb: > Er könnte höchstens SEHR schlau sein und Variante 1 selber auf > Variante 2 umschreiben, aber darauf würde ich mich nicht verlassen. GCC/amd64 macht das bei eingeschalteter Optimierung. Aber auch ich würde das lieber bleiben lassen. Bei GCC/AVR könnte das bei abgeschalteter Optimierung auf ziemlich schaurigen Code rauslaufen. Das Prinzip ((x >> n) & 1) hat noch dazu den Nachteil, dass ein Rechtsshift recht wahrscheinlich eine Optimierung auf Operationen unterhalb sizeof(int) verhindert, zumindest wenn x nicht einfach bloss ein Port oder eine Variable ist, da Bits vom auf int erweiterten Zwischenergebnis nach unten rutschen.
:
Bearbeitet durch User
Vielen Dank für die Hinweise. Meine Befürchtung war, dass #define _ENC_S ((PINB>>_ENCPin)&1) den Inhalt des Registers (hier PINB) unter bestimmten Umständen selbst schiebt und damit verändert. Das wäre natürlich tötlich. Ich hab das Verhalten noch nicht beobachtet und ein Zuweisungsoperator ist ja auch nicht vorhanden. Naja es blieben halt Zweifel. Gruss, Bernd
Bernd K. schrieb: > #define _ENC_S ((PINB>>_ENCPin)&1) Bezeichner die mir Unterstrich + Großbuchstaben oder 2 Unterstrichen anfangen sind in C und C++ der Standard Bibliothek vorenthalten und im User Code verboten.
Bernd K. schrieb: > Vielen Dank für die Hinweise. Meine Befürchtung war, dass > #define _ENC_S ((PINB>>_ENCPin)&1) den Inhalt des Registers (hier PINB) > unter bestimmten Umständen selbst schiebt und damit verändert. Das wäre > natürlich tötlich. Ich hab das Verhalten noch nicht beobachtet und ein > Zuweisungsoperator ist ja auch nicht vorhanden. Naja es blieben halt > Zweifel. Warum bleiben Zweifel?
Dr. Sommer schrieb: > Bernd K. schrieb: >> #define _ENC_S ((PINB>>_ENCPin)&1) > > Bezeichner die mir Unterstrich + Großbuchstaben oder 2 Unterstrichen > anfangen sind in C und C++ der Standard Bibliothek vorenthalten und im > User Code verboten. Danke! Fand den einführenden Unterstrich sowieso immer lästig.
Wilhelm M. schrieb: > Bernd K. schrieb: >> Vielen Dank für die Hinweise. Meine Befürchtung war, dass >> #define _ENC_S ((PINB>>_ENCPin)&1) den Inhalt des Registers (hier PINB) >> unter bestimmten Umständen selbst schiebt und damit verändert. Das wäre >> natürlich tötlich. Ich hab das Verhalten noch nicht beobachtet und ein >> Zuweisungsoperator ist ja auch nicht vorhanden. Naja es blieben halt >> Zweifel. > > Warum bleiben Zweifel? ob z.B. bestimmte Compiler-Optionen evtl. doch die Absicht des Programmierers missverstehen. Kann man eigentlich das Ergebnis des Precompilers irgendwo anschauen. Also den Zustand des Quelltextes nachdem alle Makros und Konstanten in den Text eingesetzt sind? Die o-Files sind dann ja schon Maschinencode.
Bernd K. schrieb: > Wilhelm M. schrieb: >> Bernd K. schrieb: >>> Vielen Dank für die Hinweise. Meine Befürchtung war, dass >>> #define _ENC_S ((PINB>>_ENCPin)&1) den Inhalt des Registers (hier PINB) >>> unter bestimmten Umständen selbst schiebt und damit verändert. Das wäre >>> natürlich tötlich. Ich hab das Verhalten noch nicht beobachtet und ein >>> Zuweisungsoperator ist ja auch nicht vorhanden. Naja es blieben halt >>> Zweifel. >> >> Warum bleiben Zweifel? > > ob z.B. bestimmte Compiler-Optionen evtl. doch die Absicht des > Programmierers missverstehen. Du solltest keine Optionen setzen, deren Konsequenz Du nicht verstehst. Aber: in diesem Fall ist das nicht möglich. > Kann man eigentlich das Ergebnis des Precompilers irgendwo anschauen. > Also den Zustand des Quelltextes nachdem alle Makros und Konstanten in > den Text eingesetzt sind? Die o-Files sind dann ja schon Maschinencode. Ja: Option -E
Bernd K. schrieb: > Kann man eigentlich das Ergebnis des Precompilers irgendwo anschauen Eigentlich ist es hier sowieso unnötig den Präprozessor zu nutzen. Warum nicht einfach
1 | inline int ENC_S () { |
2 | return (PINB>>_ENCPin)&1; |
3 | }
|
Etwas mehr Tipparbeit, dafür weniger Ungewissheit dass da was durcheinander gehen könnte...
Dr. Sommer schrieb: > }Etwas mehr Tipparbeit, dafür weniger Ungewissheit dass da was > durcheinander gehen könnte... Erstens besteht da kein Vorteil, und zweitens erhöht es die Ungewißheit, weil "inline" nur eine unverbindliche Anregung für den Compiler ist.
Bernd K. schrieb: > Kann man eigentlich das Ergebnis des Precompilers irgendwo anschauen. An einfachsten ist -save-temps zu den Optionen hinzuzufügen. Dann wird für jede Compilation Unit ein .i (C), .ii (C++) sowie ein .s erzeugt bzw. nicht gelöscht. Der präprozessierte Code hängt ab vom gewählten Debug-Format: Ab Dwarf-3 können makros "debuggt" werden, daher enthält .i/.ii dann auch alle Makro-Definitionen, d.h. solche per #define, solche per -D sowie Built-in Makros. A. K. schrieb: > Nop schrieb: >> Er könnte höchstens SEHR schlau sein und Variante 1 selber auf >> Variante 2 umschreiben, aber darauf würde ich mich nicht verlassen. > > GCC/amd64 macht das bei eingeschalteter Optimierung. Aber auch ich würde > das lieber bleiben lassen. Bei GCC/AVR könnte das bei abgeschalteter > Optimierung auf ziemlich schaurigen Code rauslaufen. Ohne Optimierung wird es in beiden Fällen nicht-optimalen Code geben. Zudem werden Makros nicht optimiert :-) sondern es wwerden im Kontext der Ersetzung bestimmte Trandformationen gemacht. Der Context wird hier vermutlich sowas sein wie
1 | if (TEST(x)) ... |
so dass z.B. für ein SBRC oder SBIC mehr optimiert werden muss als nur Shift + Bittest. Andere denkbare Verwendungen wären
1 | bool is_set = TEST(x); |
oder
1 | bool more_than_one_is_set = TEXT(x) + TEST(y) + TEST(z) > 1; |
Falls es komplexe Ausdrücke werden, z.B. weil Makros geschachtelt werden, ist auch zu überlegen, stattdessen inline-Funktionen zu verwenden:
1 | static inline __attribute__((__always_inline__)) |
2 | bool is_set (uint8_t const volatile *port, uint8_t bitno) |
3 | {
|
4 | return (*port) & (1u << bitno); |
5 | }
|
6 | |
7 | uint8_t test (void) |
8 | {
|
9 | return is_set (&PINB, 7); |
10 | }
|
Allerdings ist auch bekannt, dass avr-gcc Arithmetik mit Bits wie in y = TEST(x) nicht optimal übersetzt. Wer die sportliche Herausforderung liebt, dem kann ich gerne sagen, wo was geschraubt werden muss (nicht im avr-Teil). Inzwischen ist v8 ja raus und die Entwicklung wieder in Stage I, d.h. offen. https://gcc.gnu.org/gcc-8/changes.html#avr
Johann L. schrieb: > Ohne Optimierung wird es in beiden Fällen nicht-optimalen Code geben. :-) Nur könnte ich mir vorstellen, dass der unoptimierte Code bei (x >> N) etwas brachialer ausfällt als bei (x & K).
:
Bearbeitet durch User
Nop schrieb: > Erstens besteht da kein Vorteil, und zweitens erhöht es die Ungewißheit, > weil "inline" nur eine unverbindliche Anregung für den Compiler ist. Wenn man schlauer als der Compiler ist und sicher sein will dass es geinlined wird, fügt man ein "__attribute__((always_inline))" hinzu. Funktionen haben gegenüber Makros immer den Vorteil, dass es keine Probleme mit Scope und Kollisionen gibt, dass die Seiteneffekte von Parametern nicht mehrfach evaluiert werden, dass man sieht dass hier etwas aufgerufen wird und es sich nur um eine dumme Variable handelt, dass man nicht sehr mit Klammern aufpassen muss um keine Syntax-Fehler zu erzeugen. Daher würde ich sofern möglich immer Funktionen Makros bevorzugen.
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.