Preprozessor Problem

Gast #178715
Hi,

ich habe ein Preprozessor Problem mit dem Makro sbi.

Folgendes habe ich zusammengebaut:

#define P0_N_RESET     PORTB,7
...
void init(void)
  {
  sbi(P0_N_RESET);
      }
...

Der GCC bringt nun folgende Fehlermeldung:
my_p0.h:20:20: macro "sbi" requires 2 arguments, but only 1 given

Mir ist klar, dass er es nicht check, dass er erst P0_N_RESET ersetzen
muss. Aber was kann ich tun ?
Gast
#define    SBI(p, b)   do { (p) |= 1<<(b) } while(0)
#define    CBI(p, b)   do { (p) &= ~(1<<(b)) } while(0)

#define    LED_PORT    PORTx
#define    LED_BIT     3

#define    LED_ON()    SBI(LED_PORT, LED_BIT)
#define    LED_OFF()   CBI(LED_PORT, LED_BIT)
Gast
Das ist mir allerdings auch unklar.

#define    SBI(p, b)   do { (p) |= 1<<(b) } while(0)

hätte ich so geschrieben

#define    SBI(p, b)   ((p) |= 1<<(b))

Man muss allenfalls den Compiler anweisen, daß p volatile ist, aber das
sollte in den entsprechenden Headerdateien ja eh' so definiert sein.
Gast
Das "do { ...} while(0)" wird benötigt, um zu erreichen dass das Makro
sich exakt gleich verhält wie ein normaler Funktionsaufruf, d.h. das
Makro stellt ein Statement da.

Übrigens habe ich einen kleinen Fehler gemacht, ich habe die
Strichpunkte vergessen (schäm).

Hier ein kleines Beispiel:


#define     BAD(a)   (bla = (a))
#define     GOOD(a)   do { bla = (a); } while(0)

int bla;

int main()
{
  BAD(123), "abc";   /* Hier meckert der Compiler nicht!!! */

  GOOD(123), "abc";  /* Hier bricht der Compiler mit einem Fehler ab
*/

  return 0;
}


Das Makro BAD ist ein Assignment, also eine Expression. Das Makro GOOD
ist ein Statement.
@Michael (ein anderer)

"Das "do { ...} while(0)" wird benötigt, um zu erreichen dass das
Makro
sich exakt gleich verhält wie ein normaler Funktionsaufruf, d.h. das
Makro stellt ein Statement da."


Ich verstehe nur Bahnhof.
Wozu braucht man denn sowas ?
Ein Statement ist alles, was ein ; hat oder in {} eingeschlossen ist.


Statt do{...}while(0) nehme ich lieber for(;;){...break;}, ist halt
Geschmackssache.
Als einzige Anwendung für sowas kenne ich bisher nur, daß man mehrere
Abbruchbedingungen testen will, also mit if(...)break; abbricht.



@Alle

Ich habe das original Posting so verstanden, daß er den Pin nur mit
einem einzigen Namen versehen will, d.h. daß der Name die Byte- und die
Bitadresse enthält.

Und das macht nur meine Lösung.


Peter
@Michael (ein anderer)

Ich hab das mal ausprobiert:


char i;

char bla( void )
{
  return 7;
}


void test( void )
{
  i = bla(), "abc";

  "abc";

  i = 5, "abc";

  do{ i = 6; }while(0), "abc";
}

Nur die letzte Zuweisung ergibt eine Fehlermeldung, d.h. sie verhält
sich nicht wie ein Funktionsaufruf !!!

Funktionsaufrufe könne durchaus sinnvoll durch Komma getrennt werden.

In allen anderen Zeilen wird "abc" vom Compiler als Nonsens
betrachtet und einfach ignoriert.
Man muß halt wissen, was man hinschreibt und ich wüßte jetzt absolut
nicht, warum jemand sowas hinschreiben sollte.


Peter
Gast
"Das "do { ...} while(0)" wird benötigt, um zu erreichen dass
   das Makro sich exakt gleich verhält wie ein normaler
   Funktionsaufruf, d.h. das Makro stellt ein Statement da."

Das scheint nur für Leute von Bedeutung zu sein, die mehrere durch
Semikola getrennte Dinge in eine Zeile schreiben und sich vom Compiler
auf die Finger klopfen lassen wollen, wenn sie Komma und Semikolon
verwechseln.

Es gibt Probleme, von denen man gar nicht wusste, daß Leute sie haben
können.
Gast
Der Kommaoperator wird übrigens in § 6.5.17 vom Standard beschrieben,
und ist ein Sequence-Point.

Man kann mehrere Funktionen etc. damit trennen, sollte aber immer daran
denken, daß der rechte Operand den Gesamtausdruck bestimmt, da kann man
böse auf die Fresse fallen.

Man kann sich auch darauf verlassen, daß der Kommaoperator geringere
Bindung als die Zuweisung (=) hat, aber Ausdrücke wie

p = i = 5, "abc";

sind dadurch schwerer lesbar, man muß also wissen, daß sie als

(p = i = 5), "abc";

und nicht als

p = (i = 5, "abc");

ausgewertert werden. Im ersten Fall steht in p (das z.B. char* sein
könnte) eine 5, im letzten Fall die Adresse von "abc", und das könnte
für den Rest des Programms von entscheidender Wichtigkeit sein ;-)

Spaß mit Auswertung und Sequence-Points kann man vor allem mit && und
|| haben, wenn man ifs sparen und den Code verkomplizieren will.

a || (b = 5);

als Abkürzung für

if (!a)
  b = 5;

und Faxen dieser Art. Nicht genutzte aber bewertete Statements wie
obiges "abc"; sind eigentlich nur lustig, um Anfänger in den Wahnsinn
zu treiben, wenn man z.B. mal ein

42;

in den Code einstreut. Alle Leute mit -Werror werden sich bedanken.
Gast
> Es gibt Probleme, von denen man gar nicht wusste, daß Leute sie
> haben können.

Jupp, siehe auch

if (i == 42)

vs.

if (42 == i)

Ich empfinde die erste Variante beim Lesen als "logischer", viele
bevorzugen die letzte, weil sie gegen Vertipper schützt. Naja,
Geschmackssache eben.

Antwort schreiben

Eine eigene Frage? Dieser Thread ist über 21 Jahre alt. Stell sie in einem eigenen Thread – dort lesen mehr Leute mit, und sie wird öfter und schneller beantwortet.

Neuen Thread starten

Eine Lösung, Ergänzung oder Rückmeldung zu diesem Thread? Dann antworte hier.

Formatierung
  • [c]C-Code[/c]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] – Link zu einem Artikel
  • Verweis auf einen anderen Beitrag: Rechtsklick auf dessen Beitrags-ID (#8078628), „Adresse kopieren“, in den Text einfügen

Mehr zur Formatierung

Längeren Sourcecode als Anhang, nicht in den Text. JPG nur für Fotos und Scans; Zeichnungen und Screenshots als PNG oder GIF. Siehe Bildformate.

Schon registriert? Anmelden

Wird nicht angezeigt. Wir schicken dir einen Link; sobald du damit bestätigst, erscheint dein Beitrag.

Mit dem Abschicken legst du einen kostenlosen Account an und erkennst die Nutzungsbedingungen und die Datenschutzerklärung an.