mikrocontroller.net

Forum: PC-Programmierung Bitstatusabfrage Macro - ist das sicher?


Announcement: there is an English version of this forum on EmbDev.net. Posts you create there will be displayed on Mikrocontroller.net and EmbDev.net.
Autor: Bernd K. (bernd_k97)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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

Autor: Nop (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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.

Autor: A. K. (prx)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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.

Autor: Nop (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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.

Autor: A. K. (prx)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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
Autor: Bernd K. (bernd_k97)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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

Autor: Dr. Sommer (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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.

Autor: Wilhelm M. (wimalopaan)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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?

Autor: Bernd K. (bernd_k97)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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.

Autor: Bernd K. (bernd_k97)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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.

Autor: Wilhelm M. (wimalopaan)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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

Autor: Dr. Sommer (Gast)
Datum:

Bewertung
1 lesenswert
nicht lesenswert
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
inline int ENC_S () {
  return (PINB>>_ENCPin)&1;
}
Etwas mehr Tipparbeit, dafür weniger Ungewissheit dass da was 
durcheinander gehen könnte...

Autor: Nop (Gast)
Datum:

Bewertung
-1 lesenswert
nicht lesenswert
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.

Autor: Johann L. (gjlayde) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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
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
bool is_set = TEST(x);
oder
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:
static inline __attribute__((__always_inline__))
bool is_set (uint8_t const volatile *port, uint8_t bitno)
{
    return (*port) & (1u << bitno);
}

uint8_t test (void)
{
    return is_set (&PINB, 7);
}

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

Autor: A. K. (prx)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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
Autor: Dr. Sommer (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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.

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.