Hallo,
ich möchte meine Tasteneingabe und abfrage vereinfachen. Hierzu bietet
sich folgendes Makro an;
#define readPinHigh (P,bit) ((P&(1<<bit))
Das gleiche könnte aber auch mit einer Funktion realisiert werden;
char readPinHigh (char P, char bit)
{ P&(1<<bit);
_delay_ms(10); } // Tastenentprellung
(1) Welchen Vor/Nachteil hat das Makro gegenüber der Funktion aus meinem
Bsp?
(2) Wie können, in einem Makro, mehrere Befehle hintereinander
geschrieben werden? Bsp: _delay_ms(1); // dahinter
Gast
#3056679
Lies deine beiden Fragen nochmal durch. Die Antwort steckt da drin.
Gast
#3056696
> (2) Wie können, in einem Makro, mehrere Befehle hintereinander > geschrieben werden? Bsp: _delay_ms(1); // dahinter wenn man das unbedingt braucht, missbraucht man dazu eine do { ... } while(0) - Schleife, da diese sich syntaktisch in jedem Kontext wie ein (1) Statement verhält.
Gast
#3056746
Der Vorteil einer Funktion gegenüber dem Makro: du hast die Typsicherheit, der Compiler wird die ggf. eine Warnung ausgeben, wenn die Parameter nicht stimmen. Und du hast auch die Seiteneffekte vom Makro nicht. Bespiel:
1 | |
2 | |
3 | |
4 | |
5 | |
6 | |
7 | |
8 | |
9 | |
10 | |
Rate mal welche Ausgabe du hast ...
Gast
#3056753
Eine funktion heisst push-pop & parameter auf stack. Makro bedeutet der Code ist N-mal vorhanden. Um einen Pin zu toggeln nimmt man natuerlich das Makro, weils eh nur ein ASM statement ist.
i-Troll (c) schrieb: > Eine funktion heisst push-pop & parameter auf stack. Gähn. Function Inlining wurden vor ungefähr 50 Jahren erfunden. Seit dieser Zeit ist das kein Argument mehr.
Gast
#3056765
__Son´s Bersi__ schrieb: > (2) Wie können, in einem Makro, mehrere Befehle hintereinander > geschrieben werden? Bsp: _delay_ms(1); // dahinter In einer Zeile: #define readPinHigh(P,bit) ((P&(1<<bit)); _delay_ms(1); In zwei Zeilen: #define readPinHigh(P,bit) ((P&(1<<bit)); \ _delay_ms(1);
Gast
#3056822
Macros sollte man am besten ganz vermeiden. Ein gute optimierender Compiler macht heute das gleiche. Insbesondere Anfänger werden auch nur damit verwirrt.
DANKE! Bin gerade im Fachbuch über Makros gestolpert und habe das Thema so interpretiert, dass diese als "Allheilmittel" eingestzt werden können. Dem ist wohl nicht so. An wann ist es sinnhafte, mit Makros zu arbeiten? (Bitte für einen Anfänger verständlich formulieren)
__Son´s Bersi__ schrieb: > An wann ist es sinnhafte, mit Makros zu arbeiten? (Bitte für einen > Anfänger verständlich formulieren) Wenn das Makro nicht zu kompliziert ist und keine 'Nebenwirkungen' zu erwarten sind. Makros benutzt man in C zb um konstanten Werten einen 'Namen' zu geben.
1 | |
2 | |
3 | |
4 | |
5 | |
6 | |
7 | |
8 | |
9 | |
10 | |
11 | |
12 | |
soll sich mein Array vergrößern, dann genügt es das MAkro zu ändern und der Compiler passt mir den Rest des Programms daran an, weil er ja an allen Stellen die Textersetzung von ANZAHL_ELEMENTE durch den jeweiligen Zahlenwert macht. Das ist nicht nur eine Komfort-Funktion sondern hat auch etwas mit Programmsicherheit zu tun. Denn eines muss man auch mal akzeptieren: Wir Menschen machen dumme Fehler, indem wir Dinge vergessen. Makros kannst du auch benutzen, wenn es Sinn macht, den Programmtext zu vereinfachen, bzw. wenn du durch Einführung von Makronamen den Code semantisch besser verstehbar machen kannst. Im Prinzip kannst du ja deine Abfrage schreiben als if( PIND & ( 1 << PD0 ) ) das ist eine völlig legitime Schreibweise, um einen Pin abzufragen. Aber: es ist auch etwas unbefriedigend. Denn aus dem Code geht so erst mal nicht hervor, was die Absicht an dieser Stelle ist - der Code erzählt mir nichts ausser das hier offenbar ein Pin abgefragt wird. #define OK_KEY PD0 ... if( PIND & ( 1 << OK_KEY ) ) schon besser. Jetzt seh ich schon mal im Code, dass es offenbar etwas mit der Taste zu tun hat, die am Gehäuse mit 'OK' beschriftet ist. Das ist schon mal viel besser, weil es den Code in einen gewissen Zusammenhang mit seiner Funktion rückt. Aber ich kann noch weiter gehen #define KEY_PRESSED(x) (PIND & ( 1 << (x)) #define OK_KEY PD0 ... if( KEY_PRESSED( OK_KEY ) ) jetzt fängt die Sache an, interessant zu werden. Denn jetzt beginnt der Code, obwohl es immer noch der gleiche Code ist wie der, mit dem wir angefangen haben!, mir etwas zu erzählen! Wenn ich mir diese Codestelle ansehen, dann kann ich im Code selber lesen, was der Zweck an dieser Stelle ist. Zurück zur Frage: Makro oder Funktion Klar könnte man das ganze so auch mit Funktionen erreichen. Nur ist bei diesem Makro KEY_PRESSED nicht mit Nebenwirkungen zu rechnen. Daher ist (für mich) ein Makro an dieser Stelle ok. Aber wesentlich komplexer sollte der Makroinhalt dann nicht mehr werden! Alles was komplizierter ist, speziell wenn das x dann mehrmals im Makro vorkommt, ist in einer Funktion besser aufgehoben. Immer drann denken: Makros sind einfach nur Textersetzungen und der Teil des Compilers, der diese Ersetzung vornimmt, der kümmert sich nicht großartig um Regeln - der ersetzt einfach nur den Text! Und das kann manchmal überraschende Ergebnisse liefern, wenn man nicht genau hinsieht bzw. das Makro schlampig schreibt.
1 | |
2 | |
3 | |
4 | |
5 | |
6 | |
7 | |
8 | |
9 | |
10 | |
11 | |
12 | |
13 | |
14 | |
15 | |
16 | |
17 | |
18 | |
19 | |
20 | |
Makros werden vom Präprozessor behandelt, der im Grunde nur ein glorifizierter Texteditor ist, der den Text für den eigentlichen Compiler aufbereitet. Von C versteht dieser Präprozessor nicht viel. Für ihn ist dein Programm einfach nur ein Text, den er durch Befehle die trickreicherweise selbst im Text enthalten sind, modifiziert. Nicht mehr. Der eigentliche Compiler kriegt Makros nicht mehr zu Gesicht, daher kann der auch keine C-Analysen mehr damit machen. Wohingegen Funktionen selbstverständlich Bestandteil des Sprachumfangds von C sind und daher vom Compiler vollinhaltlich geprüft und durch die Analysen gejagt werden. Und das ist eigentlich der Hauptunterschied: Makros sind einfach nur Textersetzungen. Was immer sich durch die Textersetzung ergibt, das ergibt sich und interessiert den Präprozessor nicht weiter. Funktionen hingegen sind Bestandteil des Sprachkerns von C. Der Compiler kümmert sich darum, dass da alles mit rechten Dingen zu geht. Im Zweifelsfall lieber Funktionen bevborzugen. Es sei denn, du willst etwas erreichen, was mit C-Mitteln nicht machbar ist.
__Son´s Bersi__ schrieb: > (1) Welchen Vor/Nachteil hat das Makro gegenüber der Funktion aus meinem > Bsp? Das Makro hat hier überhaupt keinen Vorteil. Der einzige "Vorteil" von Makros ist, dass sie keine bestimmten Datentypen verlangen. Beispiel:
1 | |
Das funktioniert sowohl mit Integern aller Größen und Vorzeichen als auch mit float/double. Außerdem hat man syntaktisch mehr Möglichkeiten:
1 | |
Das funktioniert mit jeder Integer-Variable. Mit einer Funktion könnte man das nur für einen Variablentyp realisieren, auf den man einen Zeiger übergeben muss. Den Wert einer "by value" übergebenen Variable kann man von einer Funktion aus ja nicht ändern. Die Nachteile von Makros wurden schon genannt: Wenn darin die Operanden mehrfach vorkommen (wie im ersten Beispiel), kann das Nebeneffekte haben. Beispielsweise bei Aufrufen wie:
1 | |
Hier wird entweder funktion1() oder funktion2() zwei Mal aufgerufen, ohne dass es offensichtlich ist. Oder das klassische Beispiel von oben mit zahl++, das zu undefiniertem Verhalten führt. Das hat man mit Funktionen nicht. In Deinem Fall muss Du aber weder Werte im Makro ändern, noch brauchst Du verschiedene Datentypen, also nimm eine Funktion. Damit sie geinlint wird und es keine Namenskonflikte mit anderen Dateien gibt, solltest Du sie static definieren. Außerdem gibt es das Schlüsselwort "inline", mit dem Du Funktionen kennzeichnen kannst, die geinlint werden sollen. Und nicht vergessen: Wenn eine Funktion einen Wert zurückgeben soll, dann muss sie das mit return machen:
1 | |
2 | |
3 | |
4 | |
5 | |
6 | |
Noch eine Nebenbemerkung: Funktionen sollten immer genau eine Sache machen, und zwar die, die ihr Name suggeriert. Deine Funktion liest aber nicht nur den Pin, sondern sie vertrödelt auch noch 10 ms. Besser wäre also etwas wie:
1 | |
2 | |
3 | |
4 | |
5 | |
6 | |
7 | |
8 | |
9 | |
10 | |
11 | |
12 | |
13 | |
14 | |
15 | |
Hier macht die Funktion genau das, was sie verspricht: Sie gibt zurück, ob der Taster gedrückt wurde oder nicht. Wie sie das erreicht, ist ihre Sache. Wenn einem später mal eine bessere Methode einfällt, wie man Buttons entprellt, muss man nur die Funktion ändern und nicht den Code, der sie aufruft.
Gast
#3056911
__Son´s Bersi__ schrieb: > Bin gerade im Fachbuch über Makros gestolpert und habe das Thema so > > interpretiert, dass diese als "Allheilmittel" eingestzt werden können. Hat eine ehemaliger Kollege von mir als Allheilmittel verwendet und damit wunderbaren Spaghetti-Code erzeugt. Der hat es so exzessiv betrieben, dass man - um überhaupt noch eine Idee zu bekommen, was das Programm macht - das Ganze erst mal durch den Makro-Präprozessor jagen und sich hinterher die Ausgabe anschauen musste.
Fabian O. schrieb: > Der einzige "Vorteil" von > Makros ist, dass sie keine bestimmten Datentypen verlangen. Das ist kein Vorteil mehr, weil ... Fabian O. schrieb: > Mit einer Funktion könnte > man das nur für einen Variablentyp realisieren, auf den man einen Zeiger > übergeben muss. ... es doch geht. Dafür gibt es C++-templates, welche man auf eine Funktion anwendet. Ja, ich weiss, im Thread-Titel steht "C" :-) __Son´s Bersi__ schrieb: > Bin gerade im Fachbuch über Makros gestolpert und habe das Thema so > interpretiert, dass diese als "Allheilmittel" eingestzt werden können. Wie alt ist das Buch :-) Weiter oben wurde "static inline" erwähnt, für verschiedene Datentypen gibt es C++-templates. Auch die Multiline-Problematik und Stringification ist in der Template-Variante m. E. nach besser gelöst.
Als ich mit µC-Programmierung noch nich so weit war, hab ich in einem Programm auch mal Makros gesehen. Da ich in der Assembler-Programmierung vorher noch nichts von Makros gehört habe (meine Einzige Programmiererfahrung war PHP), hab ich damit ein bisschen gespielt. Ich habe damit ansich nur Zeitverzögerungen gemacht. wait_10us (inhalt: 10 nop's), wait_120us (inhalt: 12 wait_10us "aufrufe") und wait_200us (inhalt: 20 wait_10us "aufrufe"). Ich dachte halt, dass das quasi wie Funktionen funktioniert. Ergebnis war dann, dass ich brennen wollte, er aber meinte, das Programm passt nicht in den µC ;) Dann hab ich mir mal angeguckt, was ein Makro wirklich macht und dann wurde mir klar, dass mein ganzer Code übersät von nop's war ;) Roland H. schrieb: > ... es doch geht. Dafür gibt es C++-templates, welche man auf eine > Funktion anwendet. In C gibt es das ja meines wissens nicht. Aber gabs in C nicht die Möglichkeit Funktionen zu überlagern? Oder kam das auch erst bei C++?
Gast
#3059899
PittyJ schrieb: > Macros sollte man am besten ganz vermeiden. Ein gute optimierender > Compiler macht heute das gleiche. 12. Use macros instead of functions for tasks that generates less than 2-3 lines assembly code. Quelle: ATMEL AVR035
Gast
#3060343
Bartl schrieb: > Use macros instead of functions.. Die Quelle war Atmel, gelle? Ich schließe mich da dem Vor-Vorredner an: Macros braucht man selten bis überhaupt nicht und sollte sie daher besser vermeiden, da sie eine Fehlerquelle ersten Ranges sind. Und unser TO war eher auf Tastenabfragen und deren Entprellung aus: __Son´s Bersi__ schrieb: > // Tastenentprellung Tja, da ist ein Macro eigentlich genau so untauglich wie eine gleichartige Funktion, denn das saubere Erfassen von Tastendrücken, deren Entprellung und sowas wie Repetieren sind Aufgaben, die ein zeitliches Regime erfordern, also zumeist in einer von einem Systemtick getriebenen Interrupt-Routine abgehandelt werden müssen. Da ist etwas mehr Nachdenken angesagt als sich bloß mit der Frage nach Macro oder Funktion zu befassen. W.S.
Gast
#3060374
W.S. schrieb: > Die Quelle war Atmel, gelle? Ja und recht haben sie auch. Für die meisten 2-3 Assemblerzeilen Funktionen wird das Flash sparen, der auf einem kleinem µC kostbar sein kann. > Ich schließe mich da dem Vor-Vorredner an: Macros braucht man selten bis > überhaupt nicht und sollte sie daher besser vermeiden, da sie eine > Fehlerquelle ersten Ranges sind. ersten Ranges? Dann solltest du nochmal programmieren lernen. Ich kann mich nicht mehr an meinen letzten Makrofehler erinnern Ich finde, dass Makros besonders auf kleinen µC aus Flash- und Effizienz-Gründen (Bspw. Loopunrolling ist durch Makros einfach möglich) durchaus eine Berechtigung haben. PS: Dein Inlining funktioniert nur solange die Funktionen in der selben Datei sind. Sobald externe Dateien hinzukommen zerstört avrgcc die mögliche Performance durch sein striktes ABI.
Fallobst schrieb: > PS: Dein Inlining funktioniert nur solange die Funktionen in der selben > Datei sind. Sobald externe Dateien hinzukommen zerstört avrgcc die > mögliche Performance durch sein striktes ABI. Makros funktionieren auch nur, wenn sie in der gleichen Datei sind. Wenn man Inlines zusätzlich als "static" definiert, dann werden sie nur als Funktionen erzeugt, wenn sie auch so benötigt werden.
Gast
#3060548
A. K. schrieb: > Wenn man Inlines zusätzlich als "static" definiert, dann werden sie nur > als Funktionen erzeugt, wenn sie auch so benötigt werden. Schade nur, dass externe inlines nicht funktionieren. Damit wäre die externe Inline-Funktion wieder eine normale. Ein Beispiel zu dem dann entstehenden ABI-Overhead von avrgcc: Für eine min-Funktion, die eigentlich keine zusätzlichen Register benötigt, sichert avrgcc im worst-case trotzdem ca. 16 Register, da er die Funktion nicht kennt. Ergibt 64 verbrauchte Cpu-Zyklen + 64 Byte unnützer Flashverbrauch pro worst-case Situation! Für solche kleinen Funktionen sind und bleiben Markos in einer Headerdatei einfach sinnvoller (wie Atmel es auch vorschlägt).
us73 schrieb: > __Son´s Bersi__ schrieb: >> (2) Wie können, in einem Makro, mehrere Befehle hintereinander >> geschrieben werden? Bsp: _delay_ms(1); // dahinter > > In einer Zeile: > #define readPinHigh(P,bit) ((P&(1<<bit)); _delay_ms(1); Nur um das nochmal nachzuhalten: Das ist vermutlich genau das Fehlerträchtige Makro-Programmieren was hier von einigen angeprangert wird. Diese Makrodefinition hat mindestens drei Probleme: 1) Das Semikolon am Ende sorgt dafür, dass seine Verwendung die übliche Codestruktur durchbricht (und ggf. syntax-features des Editors durcheinanderbringt):
1 | |
2 | |
3 | |
2) Mehrere Befehle hintereinander ohne eine Klammerstruktur gehen bei z.B. if()-Statements ohne eigene Klammern kaputt:
1 | |
2 | |
dabei wird das _delay_ms(1) ausgeführt, unabängig vom Wahrheitswert von foo. 3) Die Verwendung der Argumente kann schieflaufen, da hier eine pure Textersetzung stattfindet:
1 | |
wird ersetzt zu ((P&(1<<foo ? 3 : 6)); _delay_ms(1); und aufgrund der Operator-Präzedenz wird das als (1 << foo) ? 3 : 6 ausgewertet, was eigentlich immer zu 3 evaluieren sollte. Korrekt wäre das Makro etwa so:
1 | |
Viele Grüße,
Simon
Fallobst schrieb: > Schade nur, dass externe inlines nicht funktionieren. Wie gut das aber externe Makros funktionieren! Dieser Vergleich zwischen Makros mit bekanntem Code und Funktionen mit unbekanntem Code ist doch völliger Unfug. Wenn der Compiler den Code einer Funktion nicht kennt, dann kann er sie nicht inlinen. Wenn der Compiler den Code eines Makros nicht kennt, dann auch nicht. Mit static verzierte Inline-Funktionen lassen sich ebensogut in Includes unterbringen wie Makros. Klar, external Inlines sind keine Alternative zu Makros. Aber wer zwingt dich denn eigentlich, die Inlines unbedingt external zu machen?
Gast
#3060801
Simon Budig schrieb: > Korrekt wäre das Makro etwa so: > #define readPinHigh(P,bit) do { (((P)&(1<<(bit))); _delay_ms(1); } while (0) DANKE !!!
Antwort schreiben
Bitte melde dich an, um einen Beitrag zu schreiben.