Hallo,
ich möchte einen Portpin mittels Defines zwischen den Zuständen
Input/Tristate und Output/High umschalten, d.h. ich Muss sowohl DDxn und
PortXn jeweils von 11 auf 00 umschalten.
Bisher habe ich für jede einzelne Operation ein "Reinschrift-Define".
#define I0 (PORTD |= (1<<PD4) )
#define I0_out (DDRD |= ( 1 << DDD4))
Kann ich diese beiden Operationen in ein einziges Define reinquetschen,
so dass ich im Code dann nurnoch z.B. "StromausgangIO;" schreibe und
dann automatisch die beiden CodeOperationen von oben ausgeführt werden?`
mfg
Das do{}while(0) mag jetzt im ersten Moment komisch aussehen hat aber
folgenden Vorteil man schreibt folgendes:
1
if(Condition)
2
tu_was;
führt zu:
1
if(Condition)
2
PORTD |= (1<<PD4);
3
DDRD |= ( 1 << DDD4);
Hier bei wird NUR PORTD |= (1<<PD4); je nach condition ausgeführt das
DDRD aber unabhängig von der if-Abfrage. Anders sieht es bei der do
while() schleife aus. Dort sind die Befehle in einem Befehlsblock
untergebracht und man umgeht diese kleine Falle (mit zT. sehr
frickeligen Fehlern). Ich hoffe ich habe den unterschied bzw. die
Begründung einigermaßen verständlich beschrieben.
Diese define-Funktions-Abkürzungs-Unsitte ist einfach Murks.
Ganz generell sind defines defines, und Funktionen Funktionen. Und dabei
sollte man möglichst auch bleiben. Und nur in ganz wenigen, speziellen
Ausnahmefällen ist es sinnvoll, das zu mischen. In diesem Fall nicht.
Da gehört das hier hin:
Stefan Ernst schrieb:> zu schreiben, und das ist doch "unnatürlich".
Dann schreibst du da ein Semikolon hin und gut ist.
Der do-while-Kram ist Schwachsinn.
mfg.
Thomas Eckmann schrieb:> Dann schreibst du da ein Semikolon hin und gut ist.
Der Ästhetik ist damit gedient, aber der Compiler mosert.
Weil der "if (cond) { stmts; }; else ..." nicht mag,
jedoch "if (cond) do{ stmts; }while(0); else ..." schon.
A. K. schrieb:> Der Ästhetik ist damit gedient, aber der Compiler mosert.> Weil der "if (cond) { stmts; }; else ..." nicht mag,> jedoch "if (cond) do{ stmts; }while(0); else ..." schon.
Und deswegen schreib' ich da auch kein Semikolon nicht hin.
mfg.
Thomas Eckmann schrieb:> Stefan Ernst schrieb:>> zu schreiben, und das ist doch "unnatürlich".> Dann schreibst du da ein Semikolon hin und gut ist.
Nicht ganz. Es ist eben nicht gut
> Der do-while-Kram ist Schwachsinn.
Leider ist er es nicht.
Aber ich muss Oliver recht geben.
Man sollte nicht versuchen Dinge in Makros zu quetschen, die in
Funktionen viel besser aufgehoben sind.
Die Variante mit dem do{}while(0) hat tatsächlich den Vorteil, daß sie
auch für den if-else-Fall funktioniert wenn man dort keine geschweiften
Klammern verwendet. Sollte man also eher als meinen Vorschlag verwenden,
wenn es denn mit den defines sein soll.
Die Variante als Funktion mit inline ist sicherlich nicht zu verachten.
Karl Heinz Buchegger schrieb:> Nicht ganz. Es ist eben nicht gut
Natürlich nicht.
>> Der do-while-Kram ist Schwachsinn.> Leider ist er es nicht.
Also bevor ich sowas da hinschreibe, lass' ich doch lieber das Semikolon
weg, was ich bei "defines" sowieso mache. Aber das muß ja jeder selber
wissen.
Karl Heinz Buchegger schrieb:> Aber ich muss Oliver recht geben.
Ich auch. Aber die Frage war ja nunmal anders gestellt. Und es geht ja
auch.
mfg.
Thomas Eckmann schrieb:> Also bevor sowas da hinschreibe, lass' ich doch lieber das Semikolon> weg, was ich bei "defines" sowieso mache.
Dann muss man aber wissen, dass es sich um ein Makro und keinen
Funktionsaufruf handelt. Deshalb ziehe ich es vor, Makros so zu
formulieren, dass sie Funktionen möglichst ähnlich sind.
A. K. schrieb:> Dann muss man aber wissen, dass es sich um ein Makro und keinen> Funktionsaufruf handelt.
Das erkenne ich bei mir an der Schreibweise. Hat den Vorteil, daß man
jedesmal auch wirklch genau weiss, dass es sich um ein Makro handelt.
Denn Makros können den Code auch mächtig aufblähen, was bei Funktionen
nicht der Fall ist.
mfg.
Stefan Ernst schrieb:> Genau, man wird gezwungenif (...)> tu_was> elsezu schreiben, und das ist doch "unnatürlich".
Nein, man kann zig ; irgendwo in Leere Zeilen im Code und ans Ende von
Zeilen schreiben - macht gar nichts aus.
a = a+1;;; funktioniert tadellos.
Nils S. schrieb:> Nein, man kann zig ; irgendwo in Leere Zeilen im Code und ans Ende von> Zeilen schreiben - macht gar nichts aus.
Aber nicht hinter eine geschlossene geschweifte Klammer, wenn's danach
aus einem umgreifenden Statement mit "else" oder "while" weiter geht.
Oliver schrieb:> Es geht aber darum, daß hinter jede Anweisung in C ein Semikolon gehört.> Und>> if(x)> BLA;> else> BLUBB;>> geht eben in die Hose.
Ja natürlich, if.... *;* ... else geht nunmal nicht, aber wieso sollte
man das machen? Man macht ja auch nicht if(x) { ... }; else { ... }
Ich denke ich stimme hier mit den meisten anderen überein, dass es eine
schlechte Idee ist für diesen Anwendungszweck überhaupt #defines zu
nehmen.
Ganz normale Funktion und fertig, meinetwegen noch "inline" davor
schreiben, obwohl der Compiler schon schlau genug sein sollte sowas
selbst zu entscheiden.
Nils S. schrieb:> Oliver schrieb:>> Es geht aber darum, daß hinter jede Anweisung in C ein Semikolon gehört.>> Und>>>> if(x)>> BLA;>> else>> BLUBB;>>>> geht eben in die Hose.>> Ja natürlich, if.... *;* ... else geht nunmal nicht, aber wieso sollte> man das machen?
Genau darum geht es doch
if(x)
BLA;
else
BLUBB;
ist
* für jemanden, der nicht weiß was sich hinter BLA bzw BLUBB versteckt
völlig natürlich. Und das kann auch ein und derselbe Programmierer
sein, wenn nur genug Zeit ziwschen der Makroerstellung und einer
notwendigen Programmänderung steckt.
* im Falle, dass ein Makro tatsächlich nur 1 Statement substitiert
#define BLA PORTC &= (1<<PC0)
#define BLUBB PORTC |= (1<<PC0)
muss es nämlich
if(x)
BLA;
else
BLUBB;
heissen! D.h. die Syntax in der Verwendung richtet sich plötzlich
danach, was konkret im Makrotext steckt!
> Man macht ja auch nicht if(x) { ... }; else { ... }
Natürlich nicht. Der Unterschied ist nur: Da sehe ich das Problem direkt
an der Stelle, die mir der Compiler anmeckert. Bei der Makrolösung sehe
ich das nicht unbedingt sofort. Geh in diverse Newsgroups. Ich wette
innerhalb von 2 Wochen tritt genau dieses Problem mindestens 4 mal auf,
dass der Frager mit einerm "else without matching if" dasteht und nicht
weiter weiß.
Es gibt nur 2 Lösungen
* entweder das ganze in eine Funktion verschieben
* oder das Makro so verpacken, dass es in der Substitution auf jeden
Fall als ein einziges Statement substituiert. Und genau deswegen
verpackt man alles in ein do - while Statement, das vom Optimizer
wieder rausgeschmissen wird.
Karl Heinz Buchegger schrieb:> Der Unterschied ist nur: Da sehe ich das Problem direkt> an der Stelle, die mir der Compiler anmeckert. Bei der Makrolösung sehe> ich das nicht unbedingt sofort
Aber immerhin meckert er das an. Schlimm wäre, wenn er es, wie diese
Fehlkonstrution "if(a=b)" durchwinken würde.
Trotzdem finde ich diese do-while-Konstruktion grausig, auch wenn sie
ihren Zweck hundertprozentig erfüllt.
mfg.
Thomas Eckmann schrieb:> Trotzdem finde ich diese do-while-Konstruktion grausig,
Oh. Da bin ich voll auf deiner Seite!
Ich finde überhaupt, dass man sich in C von solchen Makro-Hacks
distanzieren sollte. Im Endeffekt bringt es meistens nichts, wenn man
Makros zu extensiv für Dinge nutzt, für die sie nicht gedacht waren.
Thomas Eckmann schrieb:> wie diese> Fehlkonstrution "if(a=b)" durchwinken würde.
Das ist keine Fehlkonstruktion. Er weisst a den Inhalt von b zu. Nur was
genau er fürs if nimmt weiss ich nicht - 1/0 für "Erfolgreich
zugewiesen"/"habe nichts zurückzugeben" oder den Inhalt der Variable -
steht sicher irgendwo.
Tunlichst vermeiden und durchwinken tut der Compiler das aber trotzdem
nicht:
Nils S. schrieb:> Das ist keine Fehlkonstruktion. Er weisst a den Inhalt von b zu. Nur was> genau er fürs if nimmt weiss ich nicht
Den Inhalt von a nach der Zuweisung, mit != 0.
A. K. schrieb:> Den Inhalt von a nach der Zuweisung, mit != 0.
Okay, nun bin ich auch gleich etwas schlauer ;)
Ich hab vor kurzem erst sowas haufenweise gesehen. Ich glaube das war in
irgendeinem 6502/SID/C64/...-Emu-Code, den ich durchgesehen hab. Wenn
ich wieder sehe, zeig ichs mal, sieht ganz ganz böse aus beim
Compilieren.
Achja, ich vergaß:
-Wall immer einschalten, wer ohne compiliert ist faul. Wer sich nicht
durchringen kann, die warnungen gleich zu beheben, nimmt noch -Werror.
-Wextra hab ich auch meistens mit drin.
Da kommen dann solche "Konstrukte" gar nicht erst zustande.
Thomas Eckmann schrieb:> Gegen die Qualität deiner Beiträge kommt man natürlich schwer an.
Bisher haben sich noch nicht viele beschwert und weil Hansel X das
meint, juckt mich das nicht.
Wenn ichs nicht besser weiss, schreib ich das eben auch.
Nils S. schrieb:> Wenn ichs nicht besser weiss, schreib ich das eben auch.
Und wen interessiert das?
Und wer ist Hansel X?
Wollen wir jetzt beleidigen.
mfg.
>Schlimm wäre, wenn er es, wie diese>Fehlkonstrution "if(a=b)" durchwinken würde.
Es ist keine Fehlkonstruktion, aber hässlich und er winkt es nicht
einfach durch.
Nur was er genau damit macht, weiss/wusste ich nicht, da man sowas nicht
macht, siehe Satz weiter oben, Herr Oberlehrer.
Wollen tu ich gar nix.
In C ist dieses
a = b = c;
ausdrücklich erlaubt und wird als
a = ( b = c );
angesehen. a bekommt dabei den Wert den b durch die Zuweisung erhalten
hat. Achtung: Das ist nicht notwendigerweise identisch zu: a und b haben
den Wert von c!
"Sinnvoll" ist das zb
double x, y;
x = y = 0.0;
Um zu verstehen, warum man diese Operation überhaupt erlaubt hat, muss
man berücksichtigen, dass Optimizer zur Zeit als C entstand noch
weitgehend in den Kinderschuhen steckten. D.h. hier half man ganz
einfach dem Compiler besseren Code zu produzieren.
bei if(a=b)kann man genausogut schreiben if(b), der Wert der Operation
also a=b liefert den zu schreibenden Wert (in diesem Falle b), das ist
übrigens auch der Grund warum man folgendes machen kann:
1
int i,j,k,l;
2
i=j=k=l=0;
das die Makrogeschichte mit dem do{}while() nicht schön ist, ist klar,
dafür gibts inline. Ab und zu kann aber ein solches Makro schon sinnvoll
sein - aber ich habe dies bisher nur einmal gehabt, wo dies sinnvoll war
(dort durften die Register vom Compiler nicht angefasst werden und das
Makro enthielt dann entsprechende Anweisungen die Register auf einen
anderen Stack zu legen).
> da man sowas nicht macht, siehe Satz weiter oben
wenn man weiß, was man tut, kann man dieses Feature durchaus sinnvoll
einsetzen
1
while((c=getc())!='\n')
2
putc(c);
ist ein sinnvolles Konstrukt.
Die Alternative, ohne dieses 'Feature', lautet
1
c=getc();
2
while(c!='\n'){
3
putc(c);
4
c=getc();
5
}
und ist deutlich umständlicher zu schreiben. Man beachte, dass im ersten
der Aufruf für die Eingabefunktion nur einmal im Code vorkommt, im
zweiten Fall aber zweimal. D.h. auch wartungstechnisch ist die erste
Variante besser, weil es dort gar nicht möglich ist, unterschiedliche
Einlesefunktionen aus Versehen zu benutzen.
C ist eben eine Sprache, die man von der Pieke auf mit all ihren
Möglichkeiten lernen muss. Wer eine Lulutante braucht, ist bei C an der
falschen Adresse.
Klaus Wachtler schrieb:> Abgesehen davon kann man zwei Operationen auch mit dem Kommaperator zu> einer verbinden, ohne ein Semikolon dazwischen zu setzen.
Genau. Das, verbunden mit den hier diskutierten define-Funktions-Makros,
gewürzt mit ein paar Fragezeichenoperatoren und anderen grusligen
Überbleibseln aus der C-Lochkarten-Steinzeit bringt einen dann im
obfuscated c contest ganz weit nach vorne ;)
Oliver
Oliver schrieb:> Überbleibseln aus der C-Lochkarten-Steinzeit bringt einen dann im> obfuscated c contest ganz weit nach vorne ;)
:-)
Nicht .... wirklich.
Im OCCC muss man schon ein wenig mehr drauf haben.
Aber den Kommaoperator hätte man sich wirklich sparen sollen. Für den
kenn ich auch keine sinnvolle Anwendung. Der ist einfach nur ekelig.
Karl Heinz Buchegger schrieb:> wenn man weiß, was man tut, kann man dieses Feature> durchaus sinnvoll einsetzen
Das geht in Java auch, und kann wie du schriebst auch durchaus sinnvoll
eingesetzt werden.
Das eigentliche Problem ist, das der Compiler integer an der Stelle wo
ein boolean (den es ja so in C nicht gibt) benötigt wird akzeptiert.
Karl Heinz Buchegger schrieb:> Aber den Kommaoperator hätte man sich wirklich sparen sollen. Für den> kenn ich auch keine sinnvolle Anwendung. Der ist einfach nur ekelig.
Initialisierungen und inkrementieren in FOR Schleifen? ;)
Läubi .. schrieb:> Karl Heinz Buchegger schrieb:>> wenn man weiß, was man tut, kann man dieses Feature>> durchaus sinnvoll einsetzen> Das geht in Java auch, und kann wie du schriebst auch durchaus sinnvoll> eingesetzt werden.>> Das eigentliche Problem ist, das der Compiler integer an der Stelle wo> ein boolean (den es ja so in C nicht gibt) benötigt wird akzeptiert.
Die genau Syntax lautet
if( expression )
d.h. alles was in C eine expression (also ein Ausdruck der zu einem Wert
ausgewertet werden kann) ist, kann dort benutzt werden. Wobei natürlich
die von dir angesprochene Konvention gilt, dass alles was 0 ist, als
logisch FALSE gewertet wird.
Und auch eine Zuweisung ist in C eine expression die einen Wert liefert,
eben den Wert nach der Zuweisung.
In diesem Sinne sind auch Vergleiche in C ganz einfach nur Operatoren
wie es auch + und - sind, die einen Wert liefern.
i = 2*(a > b);
ist völlig legales C und eine direkte Konsequenz daraus.
Das hat Vorteile, hat aber auch Nachteile.
Klar hätte man auch eine boolean Expression fordern können
if( bool_expression )
hätte dann aber einen neuen Datentyp benötigt und für Pointer wieder
eine Sonderregel einführen müssen, wie ein Pointer auf einen bool
abgebildet wird, was an und für sich kein Beinbruch gewesen wäre.
Aber wie es eben so ist: Wenn man erst mal eine Sprachdefinition
draussen hat und die ersten Programme geschrieben hat, dann ändert man
die Syntax nicht mehr so gerne, wenn es nicht unbedingt sein muss bzw.
die Auswirkungen überschaubar sind. K&R konnten damals nicht wissen,
welchen Siegeszug C 20 Jahre später antreten wird. Sie brauchten einen
Nachfolger für B um ihr Unix neu zu implementieren.
Björn Cassens schrieb:> bei if(a=b)kann man genausogut schreiben if(b), der Wert der Operation> also a=b liefert den zu schreibenden Wert (in diesem Falle b)
Meinst du? Dann probier mal:
Läubi .. schrieb:> Karl Heinz Buchegger schrieb:>> Aber den Kommaoperator hätte man sich wirklich sparen sollen. Für den>> kenn ich auch keine sinnvolle Anwendung. Der ist einfach nur ekelig.> Initialisierungen und inkrementieren in FOR Schleifen? ;)
Oops. Genau!
Björn Cassens schrieb:> also a=b liefert den zu schreibenden Wert
den tatsächlich geschriebenen Wert.
Das ist ein Unterschied!
double a, b;
int c;
a = c = b = 3.5;
b hat den Wert 3.5
c hat den Wert 3
a hat den Wert 3.0 (!)
Karl Heinz Buchegger schrieb:> den tatsächlich geschriebenen Wert.
Manchmal nicht einmal den:
if (UART_DATA_REGISTER = 'x') ...
Hier hat der für die Bedingung verwendete Wert keinerlei Zusammenhang
mit dem geschriebenen Wert 'x', die übliche Funktion solcher Register
vorausgesetzt.
Zugegeben, dieses Beispiel ist etwas bös. ;-)
Karl Heinz Buchegger schrieb:> Oliver schrieb:>>> Überbleibseln aus der C-Lochkarten-Steinzeit bringt einen dann im>> obfuscated c contest ganz weit nach vorne ;)>> :-)>> Nicht .... wirklich.>> Im OCCC muss man schon ein wenig mehr drauf haben.>> Aber den Kommaoperator hätte man sich wirklich sparen sollen. Für den> kenn ich auch keine sinnvolle Anwendung. Der ist einfach nur ekelig.
Der ist genau für solche Fälle gedacht.
Wenn man ihn nun just da, wo man ihn sinnvoll nutzen könnte, ignoriert,
kann man sich natürlich schwerlich eine sinnvolle Anwendung denken :-)
Ich sehe nichts, was dagegen spricht, außer daß viele Leute ihn nicht
nehmen, warum auch immer.
Karl Heinz Buchegger schrieb:> C ist eben eine Sprache, die man von der Pieke auf mit all ihren> Möglichkeiten lernen muss. Wer eine Lulutante braucht, ist bei C an der> falschen Adresse.
... und dann die Möglichkeiten wieder ignorieren :-))
Klaus Wachtler schrieb:> Ich würde mich schwer wundern, wenn hier nicht der zugewiesene Wert> geliefert würde.
Das Ergebnis einer Zuweisung ist nicht der ursprünglich zugewiesene
Wert, sondern der Inhalt der linken Seite nach der Zuweisung. Und da die
linke Seite hier volatile sein dürfte, wird zur "Ergebnisermittlung" die
linke Seite tatsächlich gelesen ...
na, ich denke du weißt schon, worauf das jetzt hinausläuft. ;-)
Nachtrag:
Im Code generiert durch avr-gcc findet sich dieses Verhalten allerdings
nicht. Da wird eine solche Bedingung zu always-true optimiert.
Volatile-Bug vom GCC?
So ganz überzeugt bin ich noch nicht.
Im K&R lese ich: "... daß ein Zuweisungsoperator den zugewiesenen Wert
als Resultat liefert und in Ausdrücken verwendet werden kann..."
Das ist das Schöne an Standards....
Also der gcc schert sich nicht drum, sagst du (welchen Standard auch
immer du meinst).
Daraufhin habe ich mal mit dem Visual C/C++ (14.00.50727.762 for 80x86)
ein Stück übersetzt aus Neugier:
1
inta=0;
2
volatileintb=0;
3
volatileint*pb=&b;
4
5
a=b=32;
6
// wird zu:
7
// mov DWORD PTR _b$[ebp], 32 ; 00000020H
8
// mov ecx, DWORD PTR _b$[ebp]
9
// mov DWORD PTR _a$[ebp], ecx
10
11
12
a=*pb=48;
13
// wird zu:
14
// mov edx, DWORD PTR _pb$[ebp]
15
// mov DWORD PTR [edx], 48 ; 00000030H
16
// mov DWORD PTR _a$[ebp], 48 ; 00000030H
Bei a=b=32 wird offenbar der aktuelle Wert von b weiterverwendet, also
deine Variante.
Lustigerweise macht der MS-cl aber aus a=*pb=48 dann doch wieder das
Gegenteil.
-> ich glaube nicht, daß ich mich in Zukunft auf eines der beiden
verlassen werde :-)
Klaus Wachtler schrieb:> Also der gcc schert sich nicht drum, sagst du (welchen Standard auch> immer du meinst).
Nur dass wir uns nicht missverstehen, ich meinte das nur in Bezug auf
das "neu einlesen" bei volatile. Das "Ergebnis nicht ursprünglich
zugewiesener Wert, sondern Inhalt von linker Seite" passt schon.