Gibt es dazu eine Möglichkeit die folgenden 2 Zeilen unter einer
Definition zusammen zu fassen?
Beim folgenden zeit mir der Compiler keine Fehler an, bin mir aber nicht
sicher, ob das so korrekt ist.
Jens schrieb:> Beim folgenden zeit mir der Compiler keine Fehler an, bin mir aber nicht> sicher, ob das so korrekt ist.#define LED_PORT_CLK();> __HAL_RCC_GPIOA_CLK_ENABLE(); __HAL_RCC_GPIOB_CLK_ENABLE();
Ich würde mal sagen, dass LED_PORT_CLK(); mit
__HAL_RCC_GPIOA_CLK_ENABLE(); definiert wird und danach das
__HAL_RCC_GPIOB_CLK_ENABLE(); ausgeführt wird.
Jens schrieb:> #define LED_PORT_CLK(); __HAL_RCC_GPIOA_CLK_ENABLE();> #define LED_PORT_CLK_2(); __HAL_RCC_GPIOB_CLK_ENABLE();
Sicher dass das da genau so steht mit dem "();" ??
Jens schrieb:> #define LED_PORT_CLK(); __HAL_RCC_GPIOA_CLK_ENABLE();> __HAL_RCC_GPIOB_CLK_ENABLE();
Ist so halb korrekt (das "();" ist natürlich Quatsch). Stell dir vor was
passiert wenn man so etwas schreibt:
1
if(irgendwas)
2
LED_PORT_CLK();
Daraus wird dann
1
if(irgendwas)
2
__HAL_RCC_GPIOA_CLK_ENABLE();
3
__HAL_RCC_GPIOB_CLK_ENABLE();
Wahrscheinlich nicht das was man will! Es gibt einen Standard-Trick der
alle Fälle dieser Art abdeckt:
1
#define LED_PORT_CLK do { __HAL_RCC_GPIOA_CLK_ENABLE(); __HAL_RCC_GPIOB_CLK_ENABLE(); } while (0)
Das funktioniert in allen if/else/Schleifen-Konstrukten. Allerdings geht
es auch noch viel besser mit einer Funktion:
1
inlinevoidLED_PORT_CLK(){
2
__HAL_RCC_GPIOA_CLK_ENABLE();
3
__HAL_RCC_GPIOB_CLK_ENABLE();
4
}
Erspart den ganzen Ärger mit dem Präprozessor und der Textersetzung, ist
aber genauso effizient. Wenn man 100% sicher gehen möchte, dass das
"inline" auch wirklich wirkt, kann man noch
"__attribute__((always_inline))" hinzufügen. Da aber das Ein/Ausschalten
von Takten wahrscheinlich sowieso nur 1x beim Programmstart passiert,
ist dessen Geschwindigkeit dann auch egal und das Sparen von
Programmspeicher ist hier sinnvoller, weshalb man das "inline" auch
komplett lassen kann. Wozu hier also überhaupt Makros genutzt werden ist
daher ohnehin fraglich.
PS: Bezeichner und Makro-Namen welche mit zwei Unterstrichen oder
Unterstrich+Großbuchstabe anfangen, wie z.B.
"__HAL_RCC_GPIOA_CLK_ENABLE" sind in C der C-Standard-Bibliothek
vorenthalten, und somit hier falsch. Ändern kann man das in der
Cube-Bibliothek eher nicht, aber man sollte solche Fehler nicht
nachmachen...
Jens schrieb:> Gibt es dazu eine Möglichkeit die folgenden 2 Zeilen unter einer> Definition zusammen zu fassen?
Natürlich. Ein Define geht immer bis zum Zeilenende.
Man sollte aber Seiteneffekte beachten, z.B. wenn es in einem if ohne {}
aufgerufen wird. Daher sind Klammern zu empfehlen.
Sebastian R. schrieb:> Jens schrieb:>> Beim folgenden zeit mir der Compiler keine Fehler an, bin mir aber nicht>> sicher, ob das so korrekt ist.#define LED_PORT_CLK();>> __HAL_RCC_GPIOA_CLK_ENABLE(); __HAL_RCC_GPIOB_CLK_ENABLE();>> Ich würde mal sagen, dass LED_PORT_CLK(); mit> __HAL_RCC_GPIOA_CLK_ENABLE(); definiert wird und danach das> __HAL_RCC_GPIOB_CLK_ENABLE(); ausgeführt wird.
Hmm, wenn __HAL_RCC_GPIOB_CLK_ENABLE(); danach ausgeführt wird, könnte
ich es im h-file genauso in die nächste Zeile schreiben, sprich so:
#define ist ja nicht so schwer zu verstehen
Als erstes solltes Du mal über das Semikolon nachdenken/nachlesen
Und wenn Du unsicher bist, was der Präprozessor mit
Deinen #define macht ... kannst Du es mit #ifdef testen
Wobei ich prinzipiell nicht kapiere, warum man für diese LED_PORT_CLK
noch ein extra Define braucht. Ich schreibe da genau die zwei HAL
Aufrufe in die Initialisierung, und muss nicht noch hinter dem
LED_PORT_CLK suchen, wenn ich wissen will, welcher Takt da nun letztlich
verwendet wird...
Jens schrieb:> Ich möchte meine Skills ein wenig erweitern, was C
sehr gut!
> und die Optimierung und Minimierung anbelangt.
Es ist gut, wenn Du Dich mit C, seinen Konstrukten und Möglichkeiten
auseinander setzt.
Was Du da aber betreibst nennt man Obfuscierung und hat mit Optimierung
oder Minimierung nichts zu tun.
Du kannst z.B. 100 Zeilen hinter einen #define verstecken, aber gut ist
das nicht.
Jens schrieb:> Ich möchte meine Skills ein wenig erweitern, was C und die Optimierung> und Minimierung anbelangt.>> Ich fange einmal beim Einfachsten an.#define LED_PORT_CLK();> __HAL_RCC_GPIOA_CLK_ENABLE();> #define LED_PORT_CLK_2(); __HAL_RCC_GPIOB_CLK_ENABLE();> Gibt es dazu eine Möglichkeit die folgenden 2 Zeilen unter einer> Definition zusammen zu fassen?
Was genau versprichst du dir als Ergebnis davon?
Niklas G. schrieb:> Ist nicht ein gewöhnlicher Funktionsaufruf einfacher, sauberer,> fehlersicherer?
Der wird vom Compiler bei so simplem Zeug sowieso inline aufgelöst.
Lothar M. schrieb:> Der wird vom Compiler bei so simplem Zeug sowieso inline aufgelöst.
Eben. Daher kann man es auf Code-Ebene mit Funktionen besser
strukturieren.
Lothar M. schrieb:> Ich hab da auch noch eine überraschende Möglichkeit:
Huch!
Was ist dass denn für eine Syntax, was bewirkt das Komma an dieser
Stelle?
Niklas G. schrieb:> man (kann) es auf Code-Ebene mit Funktionen besser strukturieren.
Ja.
Jens schrieb:> Ich möchte meine Skills ein wenig erweitern, was C und die Optimierung> und Minimierung anbelangt.
Dafür ist der Präprozessor in diesem Fall der verkehrte Platz, weil er
nur eine Textersetzung vornimmt. Dein Code wird also weder schneller
noch kleiner.
Stefanus F. schrieb:> Was ist dass denn für eine Syntax, was bewirkt das Komma an dieser> Stelle?
Das ist der Komma-Operator. Ausdrücke, welche per Komma hintereinander
gehängt werden, werden alle ausgewertet (d.h. Nebeneffekte ausgeführt),
aber nur der Rückgabewert des letzten Ausdrucks dann zurückgegeben. Das
braucht man eigentlich eher selten (hauptsächlich bei fold-expressions
in C++17 :) ), ist aber für fiese Makro-Konstruktionen bei welchen
mehrere Dinge auf einmal ausgeführt werden sollen beliebt. Wie gesagt:
Eine Funktion ist hier sehr wahrscheinlich am besten.
Niklas G. schrieb:> Ausdrücke, welche per Komma hintereinander> gehängt werden, werden alle ausgewertet
Danke für die Erklärung. Unfassbar dass ich das nach so vielen Jahren
noch nie gesehen/gelernt habe.
Stefanus F. schrieb:> Unfassbar dass ich das nach so vielen Jahren> noch nie gesehen/gelernt habe.
Wahrscheinlich doch, und zwar wenn Du in einer for-Schleife mehr als nur
die Schleifenvariable initialisierst.
Stefanus F. schrieb:
>> Unfassbar dass ich das nach so vielen Jahren>> noch nie gesehen/gelernt habe.Nop schrieb:> Wahrscheinlich doch, und zwar wenn Du in einer for-Schleife mehr als nur> die Schleifenvariable initialisierst.
Habe ich auch noch nie gesehen.
Hallo Niklas
Ich verwende nun die Definition mit dem do und while ausgeführt.
Niklas G. schrieb:> Jens schrieb:>> #define LED_PORT_CLK(); __HAL_RCC_GPIOA_CLK_ENABLE();>> #define LED_PORT_CLK_2(); __HAL_RCC_GPIOB_CLK_ENABLE();>> Sicher dass das da genau so steht mit dem "();" ??
In diesem Fall nicht, wenn das mit #ifdef's nicht funktioniert..
>> Jens schrieb:>> #define LED_PORT_CLK(); __HAL_RCC_GPIOA_CLK_ENABLE();>> __HAL_RCC_GPIOB_CLK_ENABLE();>> Ist so halb korrekt (das "();" ist natürlich Quatsch).
Das habe ich nun verstanden, aber:
> Stell dir vor was passiert wenn man so etwas schreibt:if (irgendwas)> LED_PORT_CLK();Daraus wird dannif (irgendwas)> __HAL_RCC_GPIOA_CLK_ENABLE();> __HAL_RCC_GPIOB_CLK_ENABLE();Wahrscheinlich nicht das was man will!
Abgesehen davon, dass ich dann ein Problem habe mit den ifdef's, aber
wieso eigentlich nicht? Wieso ist das so falsch oder Quatsch? Das ist
ziemlich das, was ich wollte.
> Allerdings geht> es auch noch viel besser mit einer Funktion:inline void LED_PORT_CLK() {> __HAL_RCC_GPIOA_CLK_ENABLE();> __HAL_RCC_GPIOB_CLK_ENABLE();> }> "__attribute__((always_inline))" hinzufügen.
Meinst du so?
1
__attribute__((always_inline))voidLED_PORT_CLK(){
2
>__HAL_RCC_GPIOA_CLK_ENABLE();
3
>__HAL_RCC_GPIOB_CLK_ENABLE();
4
>}
D Wozu hier also überhaupt Makros genutzt werden ist
> daher ohnehin fraglich.
Naja, die Ports können frei gewählt werden.
Wenn es mal Port C oder D sein soll, möchte ich nicht, dass man es
direkt in den fertigen Funktionen herum hantiert. Ich möchte auch, dass
man mit __HAL_RCC_GPIOA_CLK_ENABLE(); zB einen Port CLK für eine LED
eingeschaltet hat. Deshalb der Textersatz.
Vielen Dank übrigens auch für die vielen Kommentare von den anderen hier
im Forum.
Jens schrieb:> In diesem Fall nicht, wenn das mit #ifdef's nicht funktioniert..
Was für #ifdef's?
Jens schrieb:> Wieso ist das so falsch oder Quatsch?
Weil man als Leser nicht sieht was passiert.
Das hier
1
if(irgendwas)
2
LED_PORT_CLK();
sieht so aus wie "Wenn Bedingung erfüllt, schalte LED-Ports ein.". Es
wird aber
1
if(irgendwas)
2
__HAL_RCC_GPIOA_CLK_ENABLE();
3
__HAL_RCC_GPIOB_CLK_ENABLE();
daraus, d.h. "Wenn Bedingung erfüllt, wirt GPIOA aktiviert, und GPIOB
wird immer aktiviert, egal ob Bedingung erfüllt oder nicht". Schlimmer
wird es mit Schleifen:
1
while(irgendwas)
2
LED_PORT_CLK();
Sieht so aus als würden beide LED-Ports wiederholt eingeschaltet.
Tatsächlich wird aber nur der 1. wiederholt eingeschaltet, und der 2.
genau einmal. Okay, Ports wird man nicht in Schleifen einschalten, aber
du wolltest ja lernen wie man es allgemein besser macht.
Jens schrieb:> Meinst du so?
Nein so:
Jens schrieb:> Naja, die Ports können frei gewählt werden.
Können sie in der Funktion auch.
Jens schrieb:> Wenn es mal Port C oder D sein soll, möchte ich nicht, dass man es> direkt in den fertigen Funktionen herum hantiert.
Aber mit fertigen Makros? Um aufrufenden Code ist es überhaupt kein
Unterschied! Du kannst die LED_PORT_CLK-Funktion doch beliebig ändern,
um andere Ports einzuschalten.
Jens schrieb:> Ich möchte auch, dass> man mit __HAL_RCC_GPIOA_CLK_ENABLE(); zB einen Port CLK für eine LED> eingeschaltet hat. Deshalb der Textersatz.
Verstehe ich nicht. Dieses Makro ist doch vorgegeben, da kannst du
ohnehin nichts mit machen.
Niklas G. schrieb:> Jens schrieb:>> In diesem Fall nicht, wenn das mit #ifdef's nicht funktioniert..>> Was für #ifdef's?>
Das Beispiel, welches weiter oben der Walter K. erwähnt hat. Das
funktioniert mit Token am Ende des defines nicht.
>> Das hierif (irgendwas)> LED_PORT_CLK();sieht so aus wie "Wenn Bedingung erfüllt, schalte> LED-Ports ein.". Es> wird aberif (irgendwas)> __HAL_RCC_GPIOA_CLK_ENABLE();> __HAL_RCC_GPIOB_CLK_ENABLE();daraus, d.h. "Wenn Bedingung erfüllt, wirt> GPIOA aktiviert, und GPIOB> wird immer aktiviert, egal ob Bedingung erfüllt oder nicht". Schlimmer> wird es mit Schleifen:while (irgendwas)> LED_PORT_CLK();Sieht so aus als würden beide LED-Ports wiederholt> eingeschaltet.> Tatsächlich wird aber nur der 1. wiederholt eingeschaltet, und der 2.> genau einmal. Okay, Ports wird man nicht in Schleifen einschalten, aber> du wolltest ja lernen wie man es allgemein besser macht.>
Oh mann, ich möchte nicht wissen, an wieviele Dinge ich beim Schreiben
eines Programm nicht gedacht habe, wenn ich das so lese. Von dieser
Sicht aus habe ich das nie gesehen.
Ich habe nun eine always inline Funktion erstellt, welcher ich ein
struct übergebe, die die zu aktivierenden Gruppen enthält.
schreibst, wird beim Aufruf des Makros am Anfang ein ";" ausgegeben. Das
heißt z.B. bei
1
if(bla)
2
LED_PORT_CLK();
kommt
1
if(bla)
2
;__HAL_RCC_GPIOA_CLK_ENABLE();;
heraus. Das heißt also die Bedingung wird komplett ignoriert und der
Befehl wird immer ausgeführt. Das ist ziemlich sicher nicht das was du
willst! Das Semikolon am Ende ist ebenfalls ungünstig, weil du das Makro
so nicht mit dem Komma-Operator verbinden kannst o.ä. . Also wenn schon:
Jens schrieb:> Von dieser> Sicht aus habe ich das nie gesehen.
Das ist aber eben genau das, was man bei der Verwendung von
Präprozessor-Makros immer beachten muss. Daher sind die so gefährlich.
Wann immer möglich, sollte man deswegen lieber Funktionen oder
Konstanten benutzen. In C++ geht das auch dank Tempates deutlich besser.
Es kommen auch noch mehr Gefahren hinzu, z.B. mehrfache Auswertung von
Argumenten, Namens-Kollisionen, schlecht erkennbare Parameter-Typen, ...
Jens schrieb:> Ich habe nun eine always inline Funktion erstellt, welcher ich ein> struct übergebe, die die zu aktivierenden Gruppen enthält.
Okay? Warum war das struct beim Makro nicht nötig?
Also
Die sind alle genau gleich zu verwenden, via "#define LED_PORT_CLK()",
nur dass bei letzterem weniger Fehler passieren können. Wo ist hier ein
struct nötig? Und das "inline"+"always_inline" ist wie gesagt vermutlich
eher unnötig, weil man hier wahrscheinlich eher auf Platz als auf
Geschwindigkeit optimieren würde.
Nop schrieb:> Und dann gibt's noch das beliebte do-while-0-Idiom:
Das haben wir doch bereits festgestellt:
Niklas G. schrieb:> Es gibt einen Standard-Trick der> alle Fälle dieser Art abdeckt:
Threads ganz lesen...
Niklas G. schrieb:> Das haben wir doch bereits festgestellt:
Stimmt, aber nicht, wieso das auch das Semikolon-Problem bei if/else
löst, welches eine einfache Klammerung mit {} hätte.
Nop schrieb:> Stimmt, aber nicht, wieso das auch das Semikolon-Problem bei if/else> löst, welches eine einfache Klammerung mit {} hätte.
Ja, das wurde an anderen Stellen schon genug aufgedröselt. Daher hatte
ich das ausgelassen...
Stefanus F. schrieb:> Nop schrieb:>> Wahrscheinlich doch, und zwar wenn Du in einer for-Schleife mehr als nur>> die Schleifenvariable initialisierst.> Habe ich auch noch nie gesehen.
Ist aber ganz praktisch für Einzeiler ;-)
Im Interrupt habe ich die h-Datei eingebunden und verwende dort die
Inline Funktion.
#include "kalkulation.h"
berechne_Differenz();
Ich bekomme aber immer wieder die Fehlermeldung
xxx\xxx.axf: Error: L6218E: Undefined symbol berechne_Differenz
(referred from stm32f0xx_it.o).
Aber warum?
Niklas Gürtler schrieb:> Jens schrieb:>> Aber warum?>> Inline Funktionen müssen in dem Header, genau wie Makros.
Vielen Dank ;)
Ich habe für heute eine letzte Frage:
Ich habe zu C nun ein paar Bücher gelesen, aber auf solche Fragen, die
ich heute gestellt habe, wurde in den Büchern, die ich bisher gelesen
habe, nie näher eingegangen.
Nun die Frage ist, woher ihr diese Zusatzinformationen habt und welche
Bücher mir endlich auch mal das Kleingedruckte beibringen.
Ich meine was der Compiler so tut und wie er das und jenes handhabt
steht doch in keinem C-Buch. Wenn doch, würde ich gerne wissen, wie das
Buch heisst oder der Link im Internet.
Einen schönen Abend noch.
Jens schrieb:> Ich habe für heute eine letzte Frage:> Ich habe zu C nun ein paar Bücher gelesen, aber auf solche Fragen, die> ich heute gestellt habe, wurde in den Büchern, die ich bisher gelesen> habe, nie näher eingegangen.
ein oder ein paar Bücher reichen nicht, du musst schon ALLE lesen und
wirst nie alles erfahren!
> Ich meine was der Compiler so tut und wie er das und jenes handhabt> steht doch in keinem C-Buch. Wenn doch, würde ich gerne wissen, wie das> Buch heisst oder der Link im Internet.
DAS BUCH gibt es nie, es gibt auch nicht nur einen Compiler, es gibt
auch nicht nur EIN C sondern K&R C89 C99 und was weiss ich noch wie
viele Abwandlungen oder Erweiterungen.
Spare das Geld für viele Bücher, K&R ist schon mal ein Anfang und lerne
durch machen oder in Foren.
Joachim B. schrieb:> DAS BUCH gibt es nie, es gibt auch nicht nur einen Compiler, es gibt> auch nicht nur EIN C sondern K&R C89 C99 und was weiss ich noch wie> viele Abwandlungen oder Erweiterungen.
Dem kann man nur zustimmen aber...
Joachim B. schrieb:> ... K&R ist schon mal ein Anfang ...
davon würde ich abraten. K&R kennt noch nicht einmal
Funktionsprototypen, ich würde dringend etwas jüngeres empfehlen... ;)
M.A. S. schrieb:> Joachim B. schrieb:> DAS BUCH gibt es nie, es gibt auch nicht nur einen Compiler, es gibt> auch nicht nur EIN C sondern K&R C89 C99 und was weiss ich noch wie> viele Abwandlungen oder Erweiterungen.>> Dem kann man nur zustimmen aber...>> Joachim B. schrieb:> ... K&R ist schon mal ein Anfang ...>> davon würde ich abraten. K&R kennt noch nicht einmal> Funktionsprototypen, ich würde dringend etwas jüngeres empfehlen... ;)
Woher wisst ihr dann, was welcher Compiler macht?
Oder warum zB bei einem#define berechne(); _makro()
der Compiler bei Benutzung von berechne();
; makro; daraus macht?
Jens schrieb:> Woher wisst ihr dann, was welcher Compiler macht?
Teilweise steht es in der Dokumentation des Compilers. Im Zweifelsfall
kann man sich vom Compiler ein Assembler-Listing erstellen lassen, das
man anschliessend analysiert.
Die Zwischenstufe der durch Makros modifizierten Quelltexte, mit denen
der eigentliche Compiler gefüttert wird, kann man sich mit der gcc
Option -E ausgeben lassen.
Jens schrieb:> Woher wisst ihr dann, was welcher Compiler macht?
eigentlich ist das am Ende egal.
Es gibt für C eine kurze und bindende Vorschrift, was ein Compiler tun
soll. Für den Anfang nimm die kleinste Norm, "ANSI-C".
https://de.wikipedia.org/wiki/C_(Programmiersprache)#ANSI_C
Was dort beschrieben ist, setzt heute praktisch jeder Compiler um, die
paar Veränderungen seither stören für einen ersten Eindruck nicht.
Irgendwelche "Tricks" bewegen sich meist im dort gesteckten Rahmen.
Und sonst halt die http://c-faq.com/ für die wichtigsten und witzigsten
Effekte
A. S. schrieb:> Jens schrieb:> Woher wisst ihr dann, was welcher Compiler macht?>> eigentlich ist das am Ende egal.> Es gibt für C eine kurze und bindende Vorschrift, was ein Compiler tun> soll. Für den Anfang nimm die kleinste Norm, "ANSI-C".> https://de.wikipedia.org/wiki/C_(Programmiersprache)#ANSI_C>> Was dort beschrieben ist, setzt heute praktisch jeder Compiler um, die> paar Veränderungen seither stören für einen ersten Eindruck nicht.>> Irgendwelche "Tricks" bewegen sich meist im dort gesteckten Rahmen.> Und sonst halt die http://c-faq.com/ für die wichtigsten und witzigsten> Effekte
Danke soweit