Forum: Compiler & IDEs verschachtelte defines?


von cerberus (Gast)


Lesenswert?

Hallo Leute!
Vermutlich eine ganz doofe Frage und auch schon 1000mal gefragt, aber
ich finds einfach nicht.
Oft aufgerufene Funktionne lassen sich über Makros definieren, b.B.

#define stop PORTC &= ~0xF0

Kann man auch defines über mehrere Operationen definieren, also

#define stop PORTC &= ~0xF0 + PORTD &= ~0x0F
?

von Wolfgang Horn (Gast)


Lesenswert?

He, cerberus....

..oh, hallo, Ihr drei Köpfe an der Schwelle zum Totenreich Hades,
welcher von Euch ist so modern und spielt mit C? An welchen von Euch
darf ich mich wenden? :-)

Ja, man kann. Ich benutze solche Konstruktionen wie zwischen den
Anführungszeichen:

"#define sleep_until_bit_is_set(A,B) while
(!BITTEST(A,B)){SLEEP_ENABLE;SLEEP;}"

Muß allerdings in eine einzige Zeile passen, und die äußeren Klammern
sind gespitzte Klammern.

Ich habe schon Code von Künstlern gelesen, der mehr als 80 Zeichen
hatte. Ich halte es aber gern so einfach wie möglich, weil das
Programmieren manchmal wie ein Krieg gegen das Heer der Bugs ist, und
da gilt:

"Es ist alles im Kriege sehr einfach, aber das Einfachste ist
schwierig." ( Carl von Clausewitz)
"Im Krieg hat nur das Einfache Erfolg". (Carl von Clausewitz)

Ciao
Wolfgang

von Jörg Wunsch (Gast)


Lesenswert?

Man kann Makros über beliebig viele physische Zeilen fortsetzen, indem
man jeweils die vorherige Zeile mit einem Backlash (als letztes
Zeichen vor dem Newline!) enden lässt.

#define very_long_macro \
  do(some, foo); \
  and(some, bar)

Aber Achtung vor Seiteneffekten, während man Obiges gut in einer Zeile
benutzen kann:

  very_long_macro;

sieht's hier schon böse aus:

  if (something)
    very_long_macro;

Das Ergebnis wird dann nicht ganz das sein, was gewünscht wurde. ;-)

(Das ist der Grund für die gelegentlich anzutreffenden do { ... }
while(0) Makro-Konstruktionen, wurde hier auch schon diskutiert.)

von cerberus (Gast)


Lesenswert?

Ok, Wenn ich Euch recht verstehe, sollte das dann so aussehen?

#define stop {PORTC &= ~0xF0, PORTD &= ~0x0F}

?

von Chris (Gast)


Lesenswert?

> Ok, Wenn ich Euch recht verstehe, sollte das dann so aussehen?
> #define stop {PORTC &= ~0xF0, PORTD &= ~0x0F}

Genau.

Der Komma-Operator ist zwar nicht ganz angebracht, aber er stört in dem
Fall nicht im Geringen (höchstens beim Verständnis, wieso es
funktioniert).

von Chris (Gast)


Lesenswert?

s/Geringen/Geringsten


Nachtrag: Mach lieber ein do {} while(0) drumherum, wie Jörg
vorgeschlagen hat.

von cerberus (Gast)


Lesenswert?

ich werd mal beides probieren. Das Komma war natürlich falsch, es sollte
ein ";" (Semikolon) sein.

von Chris (Gast)


Lesenswert?

> Das Komma war natürlich falsch, es sollte ein ";" (Semikolon) sein.

Wie gesagt, ein Komma geht an der Stelle auch. :)

von ---- (Gast)


Lesenswert?

Welchen Vorteil hat die "do ... while(0)"-Konstruktion gegenüber einem
normalen {}-Klammernpaar?

----, (QuadDash).

von Chris (Gast)


Lesenswert?

> Welchen Vorteil hat die "do ... while(0)"-Konstruktion gegenüber
> einem normalen {}-Klammernpaar?

Ein primitives Beispiel:

if(irgendwas)
  MAKRO(1);
else
  return 0;

Wenn MAKRO nun mit {} definiert ist, ergibt das einen Compilerfehler,
da {}; zwei Anweisungen sind und das else damit in der Luft hängt.

von ---- (Gast)


Lesenswert?

Hm, ok. Oder anders formuliert:
Man ist auf der "sicheren" Seite, wenn man ein Makro entweder mit:
#define Makro  { funktion(); }
oder
#define Makro
definiert.

Schlecht hingegen ist:
#define Makro  {}

Oder habe ich noch eine Kleinigkeit übersehen?

----, (QuadDash).

von Chris (Gast)


Lesenswert?

> Man ist auf der "sicheren" Seite, wenn man ein Makro entweder mit:
> #define Makro  { funktion(); }

Eben nicht, hier hast du wieder die {} drumherum. Außerdem ist Makro
ohne Parameter kein Makro, sondern ein definiertes Präprozessor-Symbol
(Textersetzung); bin mir in dem Punkt aber etwas unsicher, ich meine es
wird manchmal auch als Makro bezeichnet.

Auch ein
#define MAKRO(x) funktion(x);
kann ungünstig sein, da es in meinem o.g. Beispiel expandieren würde
zu:
if(irgendwas)
  funktion();;
else
  return 0;

Auch hier wieder zwei Anweisungen nach dem if, wodurch das else alleine
dasteht.

Ein
#define MAKRO(x) funktion(x)
ist dagegen möglich.

Wenn man mehrere Funktionen aufruft oder Anweisungen hat, nimmt man do
{} while(0). Wichtig: Ohne abschließendes Semikolon.

von ---- (Gast)


Lesenswert?

Ich glaube eben hat's Klick gemacht...

1.
#define Makro(a)  { funktion(a); }
wird vom Präprozessor ungünstig so expandiert:

if(irgendwas)
  { funktion(a); };
else
  return 0;

... und dabei ist das letzte ";" in der relevanten Zeile vom
C-Standard nicht erlaubt. Oder?

Ist meine Aussage so korrekt?


2. Und was würde passieren, wenn ich das #define so ansetze:
#define Makro(a)  { funktion(a) }
bzw. in erweiterterter Form:
#define Makro(a)  { funktion1(a); funktion2(a) }
... wird zu:
if(irgendwas)
  { funktion1(a); funktion2(a) };
else
  return 0;

Wäre das korrekt?

Danke für die Erklärung.

----, (QuadDash).

von Jörg Wunsch (Gast)


Lesenswert?

> ... und dabei ist das letzte ";" in der relevanten Zeile vom
> C-Standard nicht erlaubt. Oder?

Doch, es ist -- aber es schließt halt deine if-Anweisung an dieser
Stelle ab.  Das passiert mit deinem zweiten Vorschlag nach wie vor
genauso.  Der Syntaxfehler entsteht dann, wenn der Compiler das
"else"
sieht, da er keine if-Anweisung mehr hat, der er es zuordnen kann.
(Noch schlimmerer Fall: es gibt noch eine if-Anweisung ,,weiter
oben'', der er es zuordnen kann -- das ist aber gar nicht die, der
du
es zugeordnet haben willst.)

von Peter Dannegger (Gast)


Lesenswert?

Erinnert mich irgendwie an: wer schreibt den unleserlichsten C-Code.

Man kann sich viele Konstellationen ausdenken, aber keine ist 100%
sicher gegen Schreibfehler.


Mein Vorschlag:

Einfach bei if, else und Konsorten immer {} verwenden, wenn man nicht
100% sicher ist, daß der bedingte Ausdruck atomar ist.
Damit bleibt der Code schön leserlich und es gibt keine Seiteneffekte.

Speziell bei geschachtelten ifs machen die {} den Code klar und
übersichtlich.

Oftmals werden Macros auch groß geschrieben um sie klar von anderen
Ausdrücken unterscheiden zu können.


Peter

von Chris (Gast)


Lesenswert?

> Einfach bei if, else und Konsorten immer {} verwenden, wenn man
> nicht 100% sicher ist, daß der bedingte Ausdruck atomar ist.

f(0);

Ist das nun eine Funktion oder ein Makro? Natürlich, Makros schreibt
man groß; trotzdem bleibt das mögliche Missverständnis bestehen.

Ich fände es überflüssig und unschön, immer sowas zu schreiben:
if(irgendwas)
{
    MAKRO(x);
}

nur, weil es sich um ein Makro handelt.

Makros sind in C häufig eine Krücke für inline-Funktionen (in C++ sieht
man daher praktisch nie Makros) und sollten deswegen einem einem
Funktionsaufruf weitestgehend ähneln. Das erreicht eben mit do {}
while(0), ohne Mehrdeutigkeiten und Fallen.

von Werner B. (Gast)


Lesenswert?

und was ist mit

#define MACRO(a);   { tuasmit((a)); tuwasanderesmit((a)); }

dann wird doch auch der ";" ersetzt, oder nicht?

von Schlappi (Gast)


Lesenswert?

Nö,

aber das ; nach MACRO(a) ist überflüssig

Makros sind reine Ersetzungen!!!

von cerberus (Gast)


Lesenswert?

Klasse, das artet ja inzwischen richtig aus. Da ich nun die Audienz auf
meiner Seite habe, werde ich die Gelegenheit doch gleich ausnutzen.

Bekannt ist, dass mein Stop - Makro 4 Ports auf 0 setzt

#define stop PORTC &= ~0xF0

aber es kann auch passieren das zwei weitere Ports auf 0 geschrieben
werden müssen  (wie nehmen hier mal exemplarisch 4, weil es für das
Beispiel egal ist)

#define stop {PORTC &= ~0xF0, PORTD &= ~0x0F}
bzw.
#define stop do {PORTC &= ~0xF0, PORTD &= ~0x0F}while(0)

Das Problem ist, dass es von einem Speicherbit abhängig ist, ob es nun
das Eine oder Andere Define sein muss und natürlich gehören zu einem
"Stop" auch noch 2 "Runs" die das gleiche Problem haben, dass sie 2
Ports oder einen Port bedienen müssen.

Immerhin ersetzt ein define ja nur eine (komplizierte) Anweisung. Die
Frage ist, ob es einen einfachen Weg gibt, per Abfrage eines Bits
(wird beim verändern im EEProm gesichert und beim Reset vom EEProm
zurückgelesen) das Define zu definieren?

von Werner B. (Gast)


Lesenswert?

>Makros sind reine Ersetzungen!!!

Eben, alles vor dem ersten Leerzeichen wird ersetzt, also auch der
";"

von Peter Dannegger (Gast)


Lesenswert?

@Chris,

"
Ich fände es überflüssig und unschön, immer sowas zu schreiben:
if(irgendwas)
{
    MAKRO(x);
}
"

Ich finds schön, aber nach Schönheit wird nicht bezahlt.

Oft weiß man auch nicht sofort, wieviele Ausdrücke in ein if kommen und
da macht man schon rein prophylaktisch die Klammern.
Und wenns dann doch bei nur einem Ausdruck bleiben sollte, macht sich
doch keiner die Mühe, die Klammern wieder zu löschen.


Peter

von Chris (Gast)


Lesenswert?

> Eben, alles vor dem ersten Leerzeichen wird ersetzt, also auch
> der ";"

Falsch. Der Makroname sowie (wenn vorhanden) die Argumentliste werden
ersetzt. Alle folgenden Token nicht. C-Code wird tokenweise geparst,
Leerzeichen sind optional. Du darfst ja auch 1+1 ohne Leerzeichen
dazwischen schreiben, trotzdem erkennt der Compiler das korrekt. (ok,
hier gehts um dem Präprozessor; da ist es aber im Grunde das gleiche
Prinzip, nur kennt der weniger Token.)

Nur zum allgemeinen Verständnis, folgendes ist korrekter und
standardkonformer C-Code:

PORTD = 0;;;;;;;;;;;;;;;;;;;;;;;;;;;;

Ein alleinstehendes Semikolon ist bereits ein gültiges C-Statement.
Deswegen sind {}; zwei Statements.


> Und wenns dann doch bei nur einem Ausdruck bleiben sollte, macht
> sich doch keiner die Mühe, die Klammern wieder zu löschen.

Du solltest nicht von dir auf andere schließen. :-)
Es gibt durchaus Programmierer, die Wert auf schönen Code legen
(zugunsten der Wartbarkeit und Überschaubarkeit).



> Immerhin ersetzt ein define ja nur eine (komplizierte) Anweisung.
Nein, wie oben schon geschrieben wurde, ist ein Makro eine sture
Textersetzung, die zur Compilezeit ausgeführt wird.
Im kompilierten Programm kannst du Makros nicht mehr nachträglich auf
Grund von Bit-Abfragen ändern.
Hier benötigst du also eine Funktion oder ein Makro mit if-Abfrage
(aber da würd ich erstmal eine Funktion bevorzugen).

von Chris (Gast)


Lesenswert?

Nachtrag zu meinem letzten Absatz:
Sorry, ich hab nicht "ersetzt" sondern "ist" gelesen. So hast du
natürlich Recht.
Nur musst du wie gesagt bedenken, dass Makros grundsätzlich zur
Compile-Zeit expandiert werden.

von Peter Dannegger (Gast)


Lesenswert?

Code mit Klammern ist definitiv besser lesbar und wartbar !

Man (und der Compiler) erkennt dann immer ganz genau, wo das if zuende
ist.

Hier mal ein Beispiel:

if( blabla )
  return
i++;

Und ich hab mir nen Wolf gesucht, warum das i++ immer ausgeführt
wurde.

Seitdem setze ich grundsätzlich die Klammern, da kann sowas nicht mehr
passieren.

So kriege ich jetzt schön die Fehlermeldung beim return ohne ;

if( blabla ){
  return
}
i++;


Peter

von OldBug (Gast)


Lesenswert?

>if( blabla )
>  return
>i++;

Ein Editor der ordentlich einrückt dürfte das so nicht!

Aber Du hast in meinen Augen schon recht, Peter: die Klammern machen
das ganze Konstrukt wesentlich übersichtlicher. Ich habe mir das auch
angewöhnt (gebranntes-Kind Effekt ;)...

von Jörg Wunsch (Gast)


Lesenswert?

Definitive Aussagen sollte man definitiv nicht treffen. ;-)

Ein Compiler, der return i++; nicht warnt, wenn ein return; erwartet
wird, ist aber auch nicht so det jelbe...  Entweder die Funktion gibt
einen int zurück, dann ist "return;" eine Warnung Wert, oder die
Funktion ist als void deklariert, dann sollte "return i++;" eine
Warnung bringen.

Es gibt für jedes Beispiel immer ein Gegenbeispiel.  Für völlig
übersichtliche Dinge finde ich überflüssige {} in einer if-Anweisung
eher hinderlich beim Lesen, meistens haben sie aber in der Tat Sinn
und fördern die Lesbarkeit.

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
Noch kein Account? Hier anmelden.