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 ?
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
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.)
Ok, Wenn ich Euch recht verstehe, sollte das dann so aussehen? #define stop {PORTC &= ~0xF0, PORTD &= ~0x0F} ?
> 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).
s/Geringen/Geringsten Nachtrag: Mach lieber ein do {} while(0) drumherum, wie Jörg vorgeschlagen hat.
ich werd mal beides probieren. Das Komma war natürlich falsch, es sollte ein ";" (Semikolon) sein.
> Das Komma war natürlich falsch, es sollte ein ";" (Semikolon) sein.
Wie gesagt, ein Komma geht an der Stelle auch. :)
Welchen Vorteil hat die "do ... while(0)"-Konstruktion gegenüber einem normalen {}-Klammernpaar? ----, (QuadDash).
> 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.
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).
> 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.
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).
> ... 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.)
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
> 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.
und was ist mit #define MACRO(a); { tuasmit((a)); tuwasanderesmit((a)); } dann wird doch auch der ";" ersetzt, oder nicht?
Nö, aber das ; nach MACRO(a) ist überflüssig Makros sind reine Ersetzungen!!!
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?
>Makros sind reine Ersetzungen!!!
Eben, alles vor dem ersten Leerzeichen wird ersetzt, also auch der
";"
@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
> 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).
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.
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
>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 ;)...
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.