wie im Titel:
rechnet der preprocessor in C gcc (AVR) Multiplikation aus?
#define CONST1 10
#define CONST2 20
(CONST1 x CONST2)
oder
#define MUL (CONST1 x CONST2)
oder muss das der AVR zur Laufzeit rechnen?
Joachim B. schrieb:> rechnet der preprocessor in C gcc (AVR) Multiplikation aus?
nein, dafür ist er nicht zuständig
> oder muss das der AVR zur Laufzeit rechnen?
auch nein, das macht, wenn eingeschaltet, der Optimierer.
Peter II schrieb:> Joachim B. schrieb:>> rechnet der preprocessor in C gcc (AVR) Multiplikation aus?>> nein, dafür ist er nicht zuständig
In BASCOM ist das mal wieder super einfach:
Const Mul = Const1 * Const2 / Const3 ...
Ich wollte schon mit GCC anfangen, weil ihr das so liebt. Das werde ich
erst mal lassen. Da muss man ja anscheinend alles von Hand stricken.
also eigentlich wird der Wert CONST1 mit deinem definierten Wert ersetzt
und CONST2 mit dem anderen. des weiteren wird der wert MUL dann mit dem
entstehenden Ausdruck ersetzt also 10x20
willst du denn multiplizieren oder ein x dazwischenschreiben oder hast
du das nur zur verdeutlichung so geschrieben?
beste grüße
public
Hermann schrieb:> Peter II schrieb:>> Joachim B. schrieb:>>> rechnet der preprocessor in C gcc (AVR) Multiplikation aus?>>>> nein, dafür ist er nicht zuständig>> In BASCOM ist das mal wieder super einfach:> Const Mul = Const1 * Const2 / Const3 ...>> Ich wollte schon mit GCC anfangen, weil ihr das so liebt. Das werde ich> erst mal lassen. Da muss man ja anscheinend alles von Hand stricken.
Hm,
komischer Beitrag. Gibts dafür einen tieferen Sinn?
Bei deinem Beispiel. Wird das jetzt vom Compiler ausgerechnet oder zur
Laufzeit vom Controller? Und überhaupt, warum blinkt hier Mawins LED
rum? :)
Ach, zum Thema. Der Präprozessor rechnet gar nichts aus. Der macht nur
eine Textersetzung. Der Compiler rechnet rum.
Grüße,
Hermann schrieb:> In BASCOM ist das mal wieder super einfach:> Const Mul = Const1 * Const2 / Const3 ...>> Ich wollte schon mit GCC anfangen, weil ihr das so liebt. Das werde ich> erst mal lassen. Da muss man ja anscheinend alles von Hand stricken.
das kannst du genau so auch in C machen, mit #define definierst du dir
einen symbolischen Ausdruck. das schlüsselwort für Konstanten ist
Hermann schrieb:> Peter II schrieb:>> Joachim B. schrieb:>>> rechnet der preprocessor in C gcc (AVR) Multiplikation aus?>>>> nein, dafür ist er nicht zuständig>> In BASCOM ist das mal wieder super einfach:> Const Mul = Const1 * Const2 / Const3 ...>> Ich wollte schon mit GCC anfangen, weil ihr das so liebt. Das werde ich> erst mal lassen. Da muss man ja anscheinend alles von Hand stricken.
Kann man wirklich mittlerweile bei BASCOM mehr als eine Operation pro
Zeile schreiben? Ich kenne noch den Stand, daß man einen
Berechnungsausdruck in einzelne Operationen zerlegen mußte und pro Zeile
nur eine Berechnung möglich war. Also z.B.
1
Mul = Const1 * Const2
2
Mul = Mul / Const3
Das war eine von unzähligen Sachen, die mich unheimlich genervt haben...
Hermann schrieb:> Ich wollte schon mit GCC anfangen, weil ihr das so liebt. Das werde ich> erst mal lassen. Da muss man ja anscheinend alles von Hand stricken.
das versteht ich nicht, was musst du denn von Hand machen wenn es schon
der Compiler kann?
Der C-Complier ist ein fauler Hund. Er versucht so wenig wie möglich und
so spät wie möglich Code zu erzeugen.
Wenn er irgendwie mitkriegt, daß ein Ausdruck aus bereits bekannten
Werten besteht, rechnet er ihn aus und trägt nur das Ergebnis in den
Code ein.
Das muß nichtmal ein define oder ein const sein:
Peter Dannegger schrieb:> Der C-Complier ist ein fauler Hund. Er versucht so wenig wie möglich und> so spät wie möglich Code zu erzeugen.>> Wenn er irgendwie mitkriegt, daß ein Ausdruck aus bereits bekannten> Werten besteht, rechnet er ihn aus und trägt nur das Ergebnis in den> Code ein.
danke Peter,
ich habe 2 Geradengleichungen mit 2 Steigungen m (dY/dX) und trage die
als (double) getrennt in den Header, im Proggi ist die resultierende
Gleichung aber das Produkt der beiden "m", da die "m" aber separat
ermittelt werden würde ich die gerne jeweils eigenständig im #define
lassen, aber der avr soll die Mul auch nicht immer rechnen, ohne weitere
Untersuchungen kann ich dir also glauben das die MUL keine x Takte
verbraucht?
danke
Mathematische Ausdrücke werden vollständig optimiert, wenn sie aus
Konstanten bestehen (wie in den obigen Beispielen). Darauf kannst du
dich absolut verlassen.
Und wer es wirklich wissen will, schaut in den erzeugten Assembler Code.
Joachim B. schrieb:> ohne weitere Untersuchungen kann ich dir also glauben das die MUL> keine x Takte verbraucht?
Das ist eine grundlegende Compileroptimierung, bekannt als constant
propagation. Musst halt mal in die Doku deines Compilers schauen wie der
das handhabt. Oder du schreibst dir eben ein Testprogramm, lässt es in
beiden Varianten laufen, und stellst dich mit der Stoppuhr daneben. Oder
du vergleichst die Objektdateien der beiden Varianten, wenn sie gleich
sind wäre das schon ein gutes Indiz. Oder, wenn du tatsächlich ganz ohne
Untersuchungen auskommen möchtest, dann musst du Peter wohl einfach
glauben. ;-)
Peter Dannegger schrieb:> Glauben kannst Du nur dem Assemblerlisting.
da schaue ich nicht rein, dann glaube ich lieber dir,
Besucher schrieb:> Oder du schreibst dir eben ein Testprogramm, lässt es in> beiden Varianten laufen, und stellst dich mit der Stoppuhr daneben.
nö, wenn dann:
#define CONST1 (double)0.0055678
#define CONST2 (double)0.0123456
#define MUL (CONST1 * CONST2)
(double) a;
Port setzen
a=MUL;
Port rücksetzen
mit dem Oszi die Zeit begucken
if < 20 (Takte wurde vorher gerechnet)
else (Takte zur Laufzeit gerechnet)
npn schrieb:> Kann man wirklich mittlerweile bei BASCOM mehr als eine Operation pro> Zeile schreiben?
Nein, das kann man zur Laufzeit immer noch nicht. Das nervt mich auch.
Aber bei der Definition der Konstanten geht das.
Peter II schrieb:> das versteht ich nicht, was musst du denn von Hand machen wenn es schon> der Compiler kann?
Es macht einfach den Eindruck, dass bei C alles kompliziert ist. Wie
kommt es bloß, dass einer das Statement
PORTB = (PORTB & ~(1 << PB0)) | ((ACSR & (1 << ACO)) >> ACO) << PB0;
vorschlägt, was man das auch mit PORTB.0= ACSR.AC0 machen kann? ( siehe
hier)
Beitrag "Re: Ein Bit setzten"
Es scheint bei C-Programmierern eine Sucht zu sein, sich mit unlesbaren
Konstrukten zu überbieten.
Ich habe immer noch nicht die Antwort auf die erste Frage
herausgefunden, ob man wie in Bascom eine Konstante als mathematischen
Ausdruck hinschreiben kann - anscheinend nicht!
Das ist aber ganz praktisch, wenn man z.B. einen Programm-Takt mit einem
Zählerüberlauf machen will:
const Quarz = 4000000
const Prescale = 1024
const Takt = 20
const Timset = 256-Quarz/Prescale/Takt
In C geht das nach meinem jetzigen Verständnis nur mit
#Define Konstante Wert
Den Ausdruck muss man dann ins Programm schreiben. Ja, ich weiß, der
Compiler rechet das vorher. Es ist aber viel übersichtlicher, wenn man
die festen Berechnungen in die Konstanten auslagert.
Im Programm steht dann nur nach jedem Überlauf
Timer1 = Timset
Hermann schrieb:> das auch mit PORTB.0= ACSR.AC0 machen kann?
Das ist kein C, sondern eine Sprache, die nur so aussieht wie C.
Ein Bitfeldname kann keine Zahl sein.
> Ich habe immer noch nicht die Antwort auf die erste Frage> herausgefunden, ob man wie in Bascom eine Konstante als mathematischen> Ausdruck hinschreiben kann - anscheinend nicht!
Im Gegensatz zu Bascom kannst du das überall so schreiben, nicht
nur bei einer Konstante, sondern auch in Ausdrücken.
Deine Frage hattest du aber auf den Präprozessor formuliert, daher
hast du auch die Antworten dafür bekommen. Die von dir gewünschte
Funktionalität erledigt nicht der Präprozessor, sondern der Compiler
selbst.
> const Quarz = 4000000> const Prescale = 1024> const Takt = 20> const Timset = 256-Quarz/Prescale/Takt>> In C geht das nach meinem jetzigen Verständnis nur mit> #Define Konstante Wert
Jein.
C kennt zwar “const”, aber das sind erstmal syntaktisch nur Variablen,
die du nicht ändern darfst (im Gegensatz zu C++, was echte Konstanten
hat).
Bei deinen einfachen Dingen wirst du aber zwischen beiden keinen
Unterschied merken, daher kannst du durchaus sowas machen:
1
constlongintQuarz=4000000;/* long int, da int bei AVR nur bis 32767 geht */
2
constintPrescale=1000;
3
constintTakt=20;
4
constintTimset=256-Quarz/Prescale/Takt;
C hat halt Datentypen, die musst du schon angeben.
Jörg Wunsch schrieb:>> das auch mit PORTB.0= ACSR.AC0 machen kann?>> Das ist kein C, sondern eine Sprache, die nur so aussieht wie C.>> Ein Bitfeldname kann keine Zahl sein.
Da ich mir C noch nicht reingezogen habe, und keiner widersprochen hat,
musste ich das glauben:
Beitrag "Re: Ein Bit setzten"
Dann bleibt das in C eben umständlich.
Jörg Wunsch schrieb:> Bei deinen einfachen Dingen wirst du aber zwischen beiden keinen> Unterschied merken, daher kannst du durchaus sowas machen:> const long int Quarz = 4000000; /* long int, da int bei AVR nur bis> 32767 geht */> const int Prescale = 1000;> const int Takt = 20;> const int Timset = 256 - Quarz Prescale Takt;>> C hat halt Datentypen, die musst du schon angeben.
Ja, das ist doch super. Warum sagt das dem Fragesteller nicht gleich
einer. Ob nun Preprocessor oder Compiler ist doch egal. Und das mit den
Datentypen ist sowieso besser. Da fällt man bei Bascom öfter rein, weil
er aus einer Division ein Float macht.
Hermann schrieb:> Dann bleibt das in C eben umständlich.
Die Umständlichkeit haben wir Atmel zu verdanken. Sie hätten die
Bitnamen auch gleich passend vergeben können (haben sie dann beim
Xmega so gemacht), also statt
1
PORTB=1<<PB2;
(mit PB0 = 2) schreibt man
1
PORTB=PB0;
(mit PB0 = 0x04).
Da hätte allerdings der Assembler bei einem SBI-Befehl aus der
Bitmaske (0x04) die Bitnummer (2) für den Opcode ermitteln müssen.
Sollte auch Ende des letzten Jahrtausends aber durchaus im Bereich
der Möglichkeiten gelegen haben … die Chance haben sie aber leider
verpasst.
> Ja, das ist doch super. Warum sagt das dem Fragesteller nicht gleich> einer.
Weil seine Frage nicht so klang, als wäre es ihm genau darauf
angekommen.
> Ob nun Preprocessor oder Compiler ist doch egal.
Im Prinzip ja, aber warum fragt der dann nach dem Präprozessor?
Vermutlich ist es einfach nur ein gängiges Missverständnis, dass man
alles, was zur Compilezeit vor der eigentlichen Codeerzeugung passiert,
gern dem „Präprozessor“ anlastet. Der Präprozessor ist aber bei C
halt recht klar umrissen, und er macht weiter nichts als einfache
Textersetzungen. (Lediglich für bedingte Compilierung rechnet er
auch, um den Bedinungs-Ausdruck zu ermitteln.)
>> das auch mit PORTB.0= ACSR.AC0 machen kann?>Das ist kein C, sondern eine Sprache, die nur so aussieht wie C.
Nanu? Soweit ich den Standard verstehe, ist diese Art von Bitfeldern in
ISO/IEC 9899 6.7.2.1 definiert. Wieso ist das kein C?
Jörg Wunsch schrieb:> Yep, so geht's.
Ich fass es nicht!
Eine ganz einfache Frage und keiner merkt, dass man statt
#define einfach nur Const schreiben muss.
In dem anderen Thread gibt es endlose Konstrukte um ein Bit zu setzen,
dabei geht es mit einem einfachen Bitfeld.
C-Programmierer wollen eben nicht einfach denken. Da reicht ein falscher
Begriff und es gibt nur noch Missverständnisse.
C hat alle Konstruktionen, die sich in den letzten 40 Jahren ansammelten
immer noch beibehalten.
1972 hielten Kernighan und Ritchie so ein #define, oder auch die
Bitfummelelei mit |= für eine gute Idee.
Heutzutage finden viele const und Bitfelder besser. Und der ganze
Programmierstiel hat kaum noch was mit C von 1972 zu tun.
Bascom war da konsequent. Hat alles aus dem 1964er Basic rausgeworfen,
was nicht mehr unbedingt gebraucht wird.
Missverständnisse entstehen, weil es in C so viele unterschiedliche
Möglichkeiten gibt. Dazu kommt dann noch: Der eine redet über
Performance, der andere über Wartbarkeit und der dritte über
Portabilität.
Niemand schreibt in Bascom z.B. einen portablen USB-Treiber. Da können
die Probleme, über die hier diskutiert werden, gar nicht aufkommen.
Noch einer schrieb:> Dazu kommt dann noch: Der eine redet über> Performance, der andere über Wartbarkeit und der dritte über> Portabilität.
und deswegen bleibe ich trotz Arduinonutzung beim "72er Kernighan und
Ritchie" Code, kann den jederzeit nach Atmel Studio portieren auf
eigenen Schaltungen.
Klar könnte man anders, aber warum sollte man?
wenn doch -0s default eingestellt ist bei beiden Varianten mit gcc ein
PORTB |= (1<<3);
ein SBI generiert laut vieler Aussagen hier.
Jörg Wunsch schrieb:> Noch einer schrieb:>> Natürlich: PORTB.B0= ACSR.AC0>> Yep, so geht's.Hermann schrieb:> Ich fass es nicht!> Eine ganz einfache Frage und keiner merkt, dass man statt> #define einfach nur Const schreiben muss.>> In dem anderen Thread gibt es endlose Konstrukte um ein Bit zu setzen,> dabei geht es mit einem einfachen Bitfeld.
Das Problem dabei ist nur, dass in den Header-Files der AVR Libc diese
Bitfelder nicht deklariert sind. Es müsste sich also jemand hinsetzen
und diese Deklarationen für jedes I/O-Register und für jeden AVR-Typ
schreiben. Das ist bisher halt nicht passiert.
Hier im Forum wurden bereits viele andere alternative Markos diskutiert.
Das schöne bei C ist, dann man es sich so zurecht basteln kann, wie man
es gerne haben möchte.
Was ich viel hilfreicher finde, ist eine Besonderheit bei Xmega
Controllern: Die Special-Function Register haben alle immer die gleiche
Reihenfolge. Also alle Register von PORT A liegen direkt hintereinander
im Speicher, und die Register von Port B, C, D, E usw. haben genau die
gleiche Reihenfolge, wie bei Port A. Gleiches gilt für Serielle Ports,
Timer und alle sonstigen Funktionen, die mehrfach existieren.
Wegen dieser Eigenschaft wurde es möglich (und auch gemacht), die Ports
als Struktur zu definieren. Dann wiederum kann man Ports als
Funktionsargumete übergeben. Zum Beispiel so:
1
void main() {
2
sende(USARTC0,"Hallo");
3
sende(USARFE0,"Wer ist da?);
4
}
Die Funktion sende() kann dann auf alle Register des übergebenen
seriellen Ports zugreifen. Sie selbst ist jedoch (ohne eine zusätzliche
Zeile Code) so generisch geschrieben, dass sie mit jedem seriellen Port
aufgerufen werden kann.
> Es müsste sich also jemand hinsetzen> und diese Deklarationen für jedes I/O-Register> und für jeden AVR-Typ schreiben. Das ist bisher halt nicht passiert.
Was ganz sicher daran liegt, dass dieses Problem nicht wichtig ist.
Diese ewigen Diskussionen der Art "Meine Programmiersprache ist besser
als deine" ist meiner Meinung nach nur für Anfänger verlockend.
Erfahrenen Programmierern ist ziemlich wurscht, in welcher Sprache sie
programmieren.
Ich habe noch kein Projekt gesehen, das wegen der falschen Sprache
gescheitert ist.
Stefan Us schrieb:> Erfahrenen Programmierern ist ziemlich wurscht, in welcher Sprache sie> programmieren.
Erfahrene Programmierer können in verschiedenen Sprachen schreiben,
kennen aber auch die gewissen Vorteile gewisser Sprachen und bevorzugen
daher diese. Die meisten Programmierer, die die Vorteile von C++
wirklich kennen (und verstehen...) würden nicht C verwenden, auch wenn
sie es können.
Ist denn nicht auch das Hauptproblem an
1
PORTB.B0=1;// Daten Leitung 1
2
PORTB.B1=0;// Daten Leitung 2
3
PORTB.B3=1;// Takt Leitung
dass der Compiler nicht weiß welche der Anweisungen nacheinander und
welche gleichzeitig ausgeführt werden müssen/dürfen, und somit nicht
richtig optimieren kann? Die ersten beiden Anweisungen könnten zu einem
einzelnen Portzugriff optimiert werden, die letzten beiden aber nicht,
aber das weiß der Compiler nicht. In C mit volatile Bitfields wird hier
einfach gar nichts optimiert, aber das ist auch schlecht. DAS ist der
Vorteil der "umständlichen" Schreibweise mit Bitmasken, dass man die
ersten beiden Anweisugen zusammenfassen kann, weil man selber weiß, dass
es hier geht. Wie handhabt das den Bascom?
Stefan Us schrieb:> Ich habe noch kein Projekt gesehen, das wegen der falschen Sprache> gescheitert ist.
Dem würde ich zustimmen. Mit einer Ausnahme: ich habe noch kein
Java-Programm gesehen, das auch nur ansatzweise was taugen würde.
Markus F. schrieb:> ich habe noch kein Java-Programm gesehen, das auch nur ansatzweise was> taugen würde.
Macht Sinn, denn vielen Java Anwendungen sieht man nicht an, dass sie
Java sind. Viele Websiten basieren zB serverseitig auf Java, da sieht
man nix von. Es soll gerüchteweise ansatzweise brauchbare
Android-Anwendungen geben. Manche finden die Java Anwendung eclipse so
toll, dass sie viel Geld dafür ausgeben (in Form von zB Atollic True
Studio). Vom Code Generator STM32CubeMx sind auch einige begeistert. Und
es gibt natürlich auch ansatzweise brauchbare normale PC Java
Anwendungen wie yEd.
Aber das ist natürlich alles nur Beweis per Anekdote. Java ist eine sehr
gut nutzbare Sprache mit vielen Vorteilen gegenüber Bascom, C, C++.
Allerdings gilt natürlich wie immer, dass es für jede Sprache schlechte
Programmierer und Programme gibt.
Markus F. schrieb:> ich habe noch kein Java-Programm gesehen, das auch nur ansatzweise was> taugen würde.
Du benutzt nicht zufällig ein Android-Handy? ;-)
> dass der Compiler nicht weiß welche der Anweisungen nacheinander und> welche gleichzeitig ausgeführt werden müssen/dürfen,
doch, weiß er: Da PORTB volatile ist sind die einzelnen Anweisungen
keinesfalls gegeneinander zu optimieren — abgesehen vom den für .B0 und
.B1 evtl. gemeinsam benötigten Konstanten.
Wenn das Zeugs nicht volatile ist, kann zwar optimiert werden, aber
fehlendes volatile ist bestimmt nicht im Sinne des Erfinders ;-)
> richtig optimieren kann? Die ersten beiden Anweisungen könnten zu> einem einzelnen Portzugriff optimiert werden, die letzten beiden> aber nicht,
Wenn man das wirklich *WIRKLICH* will, dann muss das eben explizit
hingeschrieben werden (z.B. via old-school Maskierung). volatile ist
nun mal volatile, und das gilt selbst für C++.
> aber das weiß der Compiler nicht.
Doch. Er weiß — bzw. der Standard sagt, dass es nicht zu optimieren
ist.
@Markus
Fachlich hast du damit sicher Recht. Ich denke, Dr. Sommer wollte jedoch
hervorheben, dass diese Syntax zu schlecht optimiertem Code führt, wenn
man keine besonderen Klimmzüge drumherum baut.
Dass er damit Recht hat, konnten wir gestern oder vorgestern erst in
einem anderen Thread lesen. Dort hatte nämlich jemand genau damit ein
Problem, dass die Bits eines Ports nicht annähernd gleichzeitig gesetzt
wurden.
@Joachim B
Ich finde deinen Ansatz sehr gut: Mit Makros definiert man sich
sprechende Symbole, damit der Programmcode gut lesbar ist.
Joachim B. schrieb:> #define MUL (CONST1 x CONST2)>> oder muss das der AVR zur Laufzeit rechnen?
Der Operator "x" ist in C überhaupt nicht definiert. Der GCC wird damit
also höchst wenig anfangen können.
Michael schrieb:> Joachim B. schrieb:>> #define MUL (CONST1 x CONST2)>>>> oder muss das der AVR zur Laufzeit rechnen?>> Der Operator "x" ist in C überhaupt nicht definiert. Der GCC wird damit> also höchst wenig anfangen können.
Joachim hat ja nur einen Auschnitt seines Codes gepostet.
Selbstverständlich steht davor noch irgendwo die Zeile
Michael schrieb:> Der Operator "x" ist in C überhaupt nicht definiert. Der GCC wird damit> also höchst wenig anfangen können.
ich finde x hier aber lesbarer als * ich streite mich doch nicht hier
mit der Darstellung in FETTSCHRIFFT
Johann L. schrieb:> doch, weiß er: Da PORTB volatile ist sind die einzelnen Anweisungen> keinesfalls gegeneinander zu optimieren
Ja ich weiß. Deswegen schrieb ich ja, dass alles einzelne Zugriffe
werden, was natürlich aus der volatile Deklaration folgt. Dies ist
sprachlich natürlich korrekt, aber u.U. nicht das was man will, nämlich
effizient mehrere Bits auf einmal setzen.
Joachim B. schrieb:> ich finde x hier aber lesbarer als * ich streite mich doch nicht hier> mit der Darstellung in FETTSCHRIFFT
Dann pack den C-Code wie sich's gehört in die Code-Tags:
> dass der Compiler nicht weiß welche der Anweisungen nacheinander und> welche gleichzeitig ausgeführt werden müssen/dürfen, und somit nicht> richtig optimieren kann?
Viel schlimmer finde ich dass der c standard den bitorder (nicht
byteorder) nicht definiert. Somit ist diese art der verwendung von
bitfields undefiniert. Bei meinem Webserver Projekt hat mich das
debuggen und anschliessende entfernen der Bitfelder aufgrund einer
unerwarteten bitorder ewigkeiten gekostet.
Daniel A. schrieb:> Viel schlimmer finde ich dass der c standard den bitorder (nicht> byteorder) nicht definiert.
Wie sollte das funktionieren, wie hättest Du Dir sowas vorgestellt? Etwa
so daß der C-Standard verbindlich vorschreibt daß gefälligst alles
Little-Endian zu sein hat und somit alle Prozessorhersteller sich
entweder daran halten oder es eben niemals einen C-Compiler dafür geben
wird?
Bernd K. schrieb:> so daß der C-Standard verbindlich vorschreibt daß gefälligst alles> Little-Endian zu sein hat und somit alle Prozessorhersteller sich> entweder daran halten oder es eben niemals einen C-Compiler dafür geben> wird?
Wobei wir dann heute wohl über den Sinn und Unsinn der dann in C
definierten middle endian byte order diskutieren würden. Denn die an der
Verbreitung von C nicht unmassgeblich beteiligte PDP-11 war little
endian bei Bytes und big endian bei Worten (oder umgekehrt?). ;-)
Bernd K. schrieb:> Wie sollte das funktionieren
Nunja, wenn bitfelder über mehrere bytes gehen bin ich damit
einverstanden das dass compilerspezifisch ist. Aber bei sowas:
1
uniony{
2
uint8_tc;
3
structx{
4
unsigneda:4;
5
unsignedb:4;
6
};
7
};
Wäre es doch praktisch gewesen wenn die entscheidung, ob c&0x0F nun a
oder b ist nicht dem compiler überlassen worden wäre.
Daniel A. schrieb:> Nunja, wenn bitfelder über mehrere bytes gehen bin ich damit> einverstanden das dass compilerspezifisch ist.> Wäre es doch praktisch gewesen wenn die entscheidung, ob c&0x0F nun a> oder b ist nicht dem compiler überlassen worden wäre.
Das wäre ein seltsames Korsett geworden, wenn du dennoch eine Sprache so
definieren willst, dass sie auf verschiedenster Hardware problemlos
implementierbar ist. Zur Orientierung: In der Entstehungszeit von C
waren auch Maschinen mit Bitbreiten von 12, 36, 48 und 60 Bits recht
bekannt.
Nope, wenn dann konsequent oder garnicht. Wenn schon, dann sollte eine
Sprache eine exakte Definition der Darstellung ermöglichen. Ich meine
mich zu erinnern, dass Ada diese Feature enthält.
Daniel A. schrieb:> Wäre es doch praktisch gewesen wenn die entscheidung, ob c&0x0F nun a> oder b ist nicht dem compiler überlassen worden wäre.
Ja, Du hast Recht, ich war mit den Gedanken woanders. Wahrscheinlich
wäre es schon möglich gewesen beizeiten eine unmißverständliche Regeln
zu finden wie die Reihenfolge in Bitfeldern bei gegebener Endianness (so
viele verschiedene gibts ja nicht) jeweils verbindlich auszusehen hat
ohne irgendwelche Hardware zu benachteiligen. Aber der Zug ist wohl
abgefahren.
@ Daniel A. (daniel-a)
>Wäre es doch praktisch gewesen wenn die entscheidung, ob c&0x0F nun a>oder b ist nicht dem compiler überlassen worden wäre.
Ja, ist aber halt nicht so. Das Problem lässt sich aber mit
plattformspezifischen Definitionen lösen, das wird auch im wahren leben
so gemacht.
Falk Brunner schrieb:> #ifdef __AVR__> #ifdef __WIN__
#ifdef _GCC_
#ifdef _CLANG_
#ifdef _KEIL_
#ifdef _IAR_
#ifdef _MSC_
#ifdef _BORLAND_
#ifdef _WATCOM_
[...]
Nein, ich glaube für Sachen die weder im C-Standard noch im jeweiligen
ABI stehen sieht das einzig sinnvolle ifdef so aus:
1
#ifndef __IAR__
2
#error "Nein, Ihr habt gesagt IAR! Ich hab euch gewarnt, ihr wolltet es so!"
Daniel A. schrieb:> Wäre es doch praktisch gewesen wenn die entscheidung, ob c&0x0F nun a> oder b ist nicht dem compiler überlassen worden wäre.
Das ist aber völlig unabhängig von der Byte-Order, ob es a oder b ist.
Es ist ganz einfach nicht erlaubt.
Aus einer union darfst du nur dasjenige Feld lesen, welches zuletzt
beschrieben wurde.
Nase schrieb:> Es ist ganz einfach nicht erlaubt.>> Aus einer union darfst du nur dasjenige Feld lesen, welches zuletzt> beschrieben wurde.
Lass das Union weg (Nebenkriegsschauplatz) und übergib stattdessen das
struct an eine Funktion in einer lib die mit einem anderen Compiler
gebaut wurde. Selbes Problem.
Bernd K. schrieb:> Lass das Union weg (Nebenkriegsschauplatz) und übergib stattdessen das> struct an eine Funktion in einer lib die mit einem anderen Compiler> gebaut wurde. Selbes Problem.
Nicht wenn sich beide Compiler am gleichen ABI orientieren und das ABI
die Anordnung von Bitfeldern definiert. Das wäre der richtige Platz
dafür. Es spricht nichts dagegen, die Offenheit der Sprache C innerhalb
einer ABI Umgebung einzuschränken. Zum Problem wird es dann, wenn du den
Rahmen des ABI verlässt, z.B. bei Datenstrukturen auf Speichermedien
oder im Netz.
schrieb:> Dann pack den C-Code wie sich's gehört in die Code-Tags:
mache ich immer ABER irgendwie hatte ich das schon mal versucht und
klappt nicht immer,
TEST1
nu gehts, wer weiss was vorher anders war, ich hatte schon Beispiele da
klappte das trotz Codeansicht nicht, vermutlich wegen "" im Text
und hier verweigert das Forum auch "HTML" Code aus C Source Text.
Bernd K. schrieb:> Nase schrieb:>> Es ist ganz einfach nicht erlaubt.>>>> Aus einer union darfst du nur dasjenige Feld lesen, welches zuletzt>> beschrieben wurde.>> Lass das Union weg (Nebenkriegsschauplatz) und übergib stattdessen das> struct an eine Funktion in einer lib die mit einem anderen Compiler> gebaut wurde. Selbes Problem.
Oder caste eine variable x von type struct x folgendermassen *(unsigned
char*)&x, ist auch erlaubt.
Joachim B. schrieb:> Michael schrieb:>> Der Operator "x" ist in C überhaupt nicht definiert. Der GCC wird damit>> also höchst wenig anfangen können.>> ich finde x hier aber lesbarer als * ich streite mich doch nicht hier> mit der Darstellung in FETTSCHRIFFT
Lesbarer oder nicht, wenn du wissen willst, was der Compiler aus einem
bestimmten Stück Code macht, solltest du auch diesen Code hinschreiben
und nicht irgendwas anderes.
Daniel A. schrieb:> Viel schlimmer finde ich dass der c standard den bitorder (nicht> byteorder) nicht definiert. Somit ist diese art der verwendung von> bitfields undefiniert.
Ja, richtig. Bitfelder waren allerdings auch gar nicht dafür gedacht, so
verwendet zu werden.
A. K. schrieb:> Wobei wir dann heute wohl über den Sinn und Unsinn der dann in C> definierten middle endian byte order diskutieren würden. Denn die an der> Verbreitung von C nicht unmassgeblich beteiligte PDP-11 war little> endian bei Bytes und big endian bei Worten (oder umgekehrt?). ;-)
Das ist nicht die einzige Architektur.
https://en.wikipedia.org/wiki/Middle_endianBernd K. schrieb:> A. K. schrieb:>> und das ABI>> die Anordnung von Bitfeldern definiert.>> Welches ABI tut das?
Ich hab zwar noch kein ABI gelesen, aber rein logisch muß es das
eigentlich zwingend definieren. Immerhin sind ABIs dafür da, daß man
Code von Compilern, die das selbe ABI nutzen, zusammenlinken und dabei
ein korrekt funktionierendes Binary erzeugen kann. Dazu muß auch
definiert sein, wie Bitfelder implementiert sind.
Daniel A. schrieb:> Oder caste eine variable x von type struct x folgendermassen *(unsigned> char*)&x, ist auch erlaubt.
Streng genommen nicht. Die einzige Möglichkeit, die in C offiziell
erlaubt ist, ist, die Daten per memcpy in ein Byte-Array zu kopieren.
Rolf Magnus schrieb:> Lesbarer oder nicht, wenn du wissen willst, was der Compiler aus einem> bestimmten Stück Code macht, solltest du auch diesen Code hinschreiben> und nicht irgendwas anderes.
tut mir leid das ich dich überfordert habe, andere hatten es verstanden
;-)
Rolf Magnus schrieb:> Daniel A. schrieb:>> Oder caste eine variable x von type struct x folgendermassen *(unsigned>> char*)&x, ist auch erlaubt.>> Streng genommen nicht. Die einzige Möglichkeit, die in C offiziell> erlaubt ist, ist, die Daten per memcpy in ein Byte-Array zu kopieren.
Doch, bei char Typen ist es als Ausnahme erlaubt. ((un)signed) char darf
alles aliasen und darf keine Alignment Probleme haben. memcpy macht auch
nichts anderes als ein char array kopieren, und das ist korrektes C(++).
Dr. Sommer schrieb:> Doch, bei char Typen
mich nervt in allen Dialekten die gemischte Verwendung von signed und
unsigned char.
Für mich ist ein Char immer unsigned, aber das sieht nicht jeder so.
Bernd K. schrieb:> Welches ABI tut das?
Jedes.
Die Anordnung der Bits in einem Bit-Field ist nämlich nicht undefined,
sondern implementation-defined. Das bedeutet, dass es zwar der
konkreten Implementierung (hier also dem Compiler) überlassen ist, wie
sie es handhabt, aber sie muss die getroffene Wahl konsistent einhalten
und auch dokumentieren.
Damit kann man die IO-Register einer MCU in der entsprechenden
Headerdatei (die sowieso ABI-abhängig ist) durchaus als Bit-Fields
beschreiben.
Dr. Sommer schrieb:> Doch, bei char Typen ist es als Ausnahme erlaubt. ((un)signed) char darf> alles aliasen und darf keine Alignment Probleme haben. memcpy macht auch> nichts anderes als ein char array kopieren, und das ist korrektes C(++).
Stimmt. Hab nochmal genauer reingeschaut in die C99-Definition. Neuere
hab ich nicht, aber da wird das gleich sein. Ich hatte mich auf diese
Passage bezogen:
"The value may be copied into an object of type unsigned char [n] (e.g.,
by memcpy); the resulting set of bytes is called the object
representation of the value. Values stored in bit-fields consist of m
bits,
where m is the size specified for the bit-field. The object
representation is the set of m bits the bit-field comprises in the
addressable storage unit holding it. "
Klang für mich so, dass man die Daten immer kopieren muss und nicht
direkt zugreifen darf. Aber wo anders steht:
"An object shall have its stored value accessed only by an lvalue
expression that has one of the following types:
[...]
— a character type."
Das sagt es also sehr eindeutig.
Joachim B. schrieb:> Dr. Sommer schrieb:>> Doch, bei char Typen>> mich nervt in allen Dialekten die gemischte Verwendung von signed und> unsigned char.>> Für mich ist ein Char immer unsigned, aber das sieht nicht jeder so.
In C ist es nicht definiert, ob er unsigned ist oder nicht. Konsistenter
wäre signed. Letztendlich ist char auch nur ein Integer-Typ, und alle
anderen Integer-Typen sind auch signed, wenn nichts anderes dran steht.
Für Text ist es sowieso egal. Nur wenn man es als kleinen Integer
benutzen will, muss man explizit unsigned davor schreiben, wenn man das
will. Müßte man bei allen anderen Integer-Typen aber auch. Lediglich
wenn man explizit einen vorzeichenbehafteten kleinen Integer haben will,
muß man anders als bei den anderen Typen das signed explizit davor
schreiben.
Rolf Magnus schrieb:> In C ist es nicht definiert, ob er unsigned ist oder nicht. Konsistenter> wäre signed.
das hätte ich gerne begründet, ein Char welches negativ werden könnte?
Wenn man an ASCII denkt 7-bit 0-127 wäre das denkbar, aber negative
Zeichen, da mag ich nicht mitdenken.
Ich finde es auch unlogisch wenn ich 100 Miese auf dem Konto habe das
ich einen Hunderter rüberschieben muss um wieder auf Null zu sein.
Noch schlimmer im Bus, 5 Leute drin, 10 steigen aus und es müssen wieder
5 rein damit der leer ist.
Joachim B. schrieb:> das hätte ich gerne begründet, ein Char welches negativ werden könnte?
Konsistenter meint: ohne explizite Vorzeichenangabe alles signed.
Aus historischen Gründen sind chars ohne Angabe ziemlich oft signed,
weil sich das bei der PDP-11 anbot und viele ASCII-Fans darin weniger
Probleme mit der Portierung von Unix-Programmen sahen.
@ Joachim B. (jar)
>> In C ist es nicht definiert, ob er unsigned ist oder nicht. Konsistenter>> wäre signed.
Ja.
>das hätte ich gerne begründet, ein Char welches negativ werden könnte?
Hat er das nicht geschrieben?
"Letztendlich ist char auch nur ein Integer-Typ, und alle
anderen Integer-Typen sind auch signed, wenn nichts anderes dran steht."
>Wenn man an ASCII denkt 7-bit 0-127 wäre das denkbar, aber negative>Zeichen, da mag ich nicht mitdenken.>Ich finde es auch unlogisch wenn ich 100 Miese auf dem Konto habe das>ich einen Hunderter rüberschieben muss um wieder auf Null zu sein.
Was ist daran unlogisch?
>Noch schlimmer im Bus, 5 Leute drin, 10 steigen aus und es müssen wieder>5 rein damit der leer ist.
Haha!
???
Rolf Magnus schrieb:> Für Text ist es sowieso egal.
Nö.
Wenn man z.B. ASCII-Umlaute nach LCD wandeln will, muß man erst nach
unsigned casten.
Oder wenn man ein Protokoll hat, was Text und Binärwerte enthält.
Unsigned char für Text hätte viele Fallgruben vermieden.
A. K. schrieb:> Peter Dannegger schrieb:>> Unsigned char für Text hätte viele Fallgruben vermieden.>> Eine ordentliche Programmiersprache zu verwenden auch. ;-)
Richtig. Deshalb verwenden "echte Programmierer" auch Fortran ;) .
Ein char ist nun einmal ein Zeichen, und ein Zeichen ist weder positiv
noch negativ, sondern eben einfach nur ein Zeichen.
Die Problematik mit dem signed und unsigned entsteht dadurch, dass
in C ein char implizit in ein int konvertiert werden kann. In den
meisten anderen Programmiersprachen gibt es diese Möglichkeit nicht.
Wenn man dort den Code eines Zeichens benötigt (bspw. in den von Peter
genannten Fällen), muss die Konvertierung explizit (in Basic, Pascal,
Python und Haskell bspw. mit der Funktion ord) erfolgen. Diese
Konvertierung liefert wie gewünscht eine nichtnegative Zahl. Für 8-Bit-
Integer-Werte (signed und unsigned) gibt es dort spezielle Datentypen,
die nicht char o.ä. heißen, sondern bspw. byte, Int8 oder Word8.
Auch wenn in C kein Zwang dazu besteht, ist es in meinen Augen guter
Stil, trotzdem zwischen Zeichen und 8-Bit-Integern zu unterscheiden. Das
bedeutet konkret:
- Für Zeichen wird der Typ char verwendet, für Strings entsprechend
char [].
- Für 8-Bit-Integer wird signed char und unsigned char oder int8_t
und uint8_t verwendet.
- Die Konvertierung eines Zeichens in ihren Zeichencode erfolgt
explizit, d.h. mit einem Cast nach unsigned char oder uint8_t.
- Bei Strings erfolgt die Konvertierung entsprechend mit einem Cast nach
unsigned char * oder uint8_t *.
Durch die Konvertierungen mag etwas mehr Schreibarbeit entstehen, es
wird dadurch aber – zusammen mit der Unterscheidung bei den Datentypen –
sofort klar, wo mit Texten und wo mit Zahlen hantiert wird.
Joachim B. schrieb:> Rolf Magnus schrieb:>> In C ist es nicht definiert, ob er unsigned ist oder nicht. Konsistenter>> wäre signed.>> das hätte ich gerne begründet, ein Char welches negativ werden könnte?>> Wenn man an ASCII denkt 7-bit 0-127 wäre das denkbar, aber negative> Zeichen, da mag ich nicht mitdenken.
Und positive Zeichen sind für dich logischer? Ich denke dabei nicht an
ASCII. Ich denke einfach daran, daß es Text ist, und Text ist weder
vorzeichenbehaftet, noch vorzeichenlos. Das ganze Konzept ergibt für
Text überhaupt keinen Sinn. Nun haben Prozessoren aber intern in der
Regel keinen eigenen Datentyp für Text. Man behilft sich daher mit einem
Integer-Typ, um die einzelnen Zeichen eines Strings zu speichern. Wie
dieser interne Integer-Typ aussieht, kann mir dabei aber eigentlich egal
sein, da ich ihn eh nicht zum Rechnen benutze, sondern nur um die Daten
zu speichern.
> Ich finde es auch unlogisch wenn ich 100 Miese auf dem Konto habe das> ich einen Hunderter rüberschieben muss um wieder auf Null zu sein.
Nunja, wenn du mit negativen Zahlen ganz allgemein auf Kriegsfuß stehst,
ist das aber eine andere Sache.
Peter Dannegger schrieb:> Rolf Magnus schrieb:>> Für Text ist es sowieso egal.>> Nö.> Wenn man z.B. ASCII-Umlaute nach LCD wandeln will, muß man erst nach> unsigned casten.
Ja. Diesen Cast macht man an genau einer Stelle, nämlich da, wo es an
die Hardware übergeben wird.
> Unsigned char für Text hätte viele Fallgruben vermieden.
Was Fallgruben vermieden hätte, wäre, wenn man die Trennung zwischen
Typen für Text und Typen zum Rechnen sauber durchgezogen hätte, wie es
bei einigen (den meisten?) anderen Sprachen der Fall ist.
Beispiel Pascal. Da kann man mit char nicht rechnen, sondern nur Text
speichern. Es gibt aber auch noch einen Typ byte, mit dem man rechnen
kann, den man dafür aber nicht für Text verwenden kann. Es gibt aber
Möglichkeiten, zwischen den beiden zu konvertieren. So sieht man im Code
auch immer ganz klar und sauber, wofür eine Variable gedacht ist.
Siehe z.B. http://wiki.freepascal.org/Char
Da interessiert sich dann auch keiner mehr dafür, ob bei char der
darunterliegende Integer nun mit oder ohne Vorzeichen ist, weil man gar
keine Möglichkeit hat, das überhaupt zu erkennen.
Bernd K. schrieb:> Was ist: "The layout of bit-fields within an aggregate> is defined by the appropriate language binding."? Wo finde ich dieses> Dokument?
Sorry, habs gefunden. Selbes Dokument Kapitel 7, (Am Beispiel ARM32,
http://infocenter.arm.com/help/topic/com.arm.doc.ihi0042e/IHI0042E_aapcs.pdf
dieses Kapitel hab ich anfangs übersehen, andere Architekturen bin ich
jetzt zu faul zu suchen).
Also ist es tatsächlich bis aufs letzte i-Tüpfelchen genau definiert.
Das allzuschnelle "ist aber undefined!!!elf" das manche Kollegen immer
wie aus der Pistole geschossen verkünden bei solchen Themen (genauso wie
die Sache mit den Unions) ist also vollkommen überzogen wenn nicht gar
unangebracht.
Naja, du musst unterscheiden zwischen Dingen wie hier (bei denen du
innerhalb einer MCU-Familie und dort innerhalb eines ABIs bleibst)
und allgemeingültiger Portabilität über viele verschiedene Maschinen.
Letzteres wäre bspw. ein Anwendungsfall, wenn man das SCSI-Protokoll
maschinenunabhängig implementieren will (wie es ja bspw. auch bei
USB mass storage devices benutzt wird). Das geht halt mit
Bit-Fields nicht „aus der Dose raus“. Entweder musst du durch ein
configure (etc.) feststellen lassen, wie die Details der konkreten
Implementierung tatsächlich aussehen, oder aber du implementierst
es eben gleich ohne Bit-Felder (mit klassischen Bitmasken). Meist
macht man dann letzteres.
Rolf Magnus schrieb:> Und positive Zeichen sind für dich logischer?
ja, 0-255 wie in der erweiterten (PC ASCII Tabelle)
Rolf Magnus schrieb:> Nunja, wenn du mit negativen Zahlen ganz allgemein auf Kriegsfuß stehst,> ist das aber eine andere Sache.
jau, seit ich nur noch (in) positiv (e) (Kontostände) denke gehts
mir besser,
da juckt es mich auch nicht das ich als Bürger rechnerisch
(Staats)Schulden hätte oder habe.
Für mich ist ein char kein Zeichen, sondern ein 8-Bit integer.
Den kann man entweder als genau das oder als Index in eine ASCII- (oder
eine beliebige andere Zeichen-) Tabelle verwenden, um damit ein
Textzeichen zu kodieren (und genau das machen die entsprechenden
Textausgabe-Funktionen).
Und weil's nun mal einfacher ist, diesen Tabellenindex von 0 bis 255
laufen zu lassen anstatt von -128 bis 127, hat char (zumindest
"intuitiv") gefälligst unsigned zu sein.
Freundlicherweise ist das auch auf den meisten Plattformen so (Power ist
die einzige, die mir bislang untergekommen ist, die das - warum auch
immer - anders macht).
Markus F. schrieb:> hat char (zumindest> "intuitiv") gefälligst unsigned zu sein.
meine Rede! etwas weiter höher.
A. K. schrieb:> In Linux auf x86 PC sind chars signed.
eben das nervt.
Markus F. schrieb:> Für mich ist ein char kein Zeichen, sondern ein 8-Bit integer.
Für diese Verwendung hat die Welt (leider erst vor reichlich 15 Jahren)
die Datentypen uint8_t und int8_t geschaffen.
Wer heutzutage immer noch “char” schreibt, wenn er einen kleinen
Integer haben will, ist einfach selbst dran schuld.
Jörg Wunsch schrieb:> Für diese Verwendung hat die Welt (leider erst vor reichlich 15 Jahren)> die Datentypen uint8_t und int8_t geschaffen.>> Wer heutzutage immer noch “char” schreibt, wenn er einen kleinen> Integer haben will, ist einfach selbst dran schuld.
ach echt?
und wenn ich eine Var uint8_t txt[] oder unsigned char nenne und strcmp
oder strcpy nutzen will muss ich fast immer casten, gefühlt ist sich
nicht mal der gcc (AVR Studio, LCC32, Arduino) einig wann was gilt. Klar
gibts für alles ne Lösung aber trotzdem nerven Fehlermeldungen bezüglich
signed und unsigned.
Joachim B. schrieb:> wenn ich eine Var uint8_t txt[] oder unsigned char nenne und strcmp oder> strcpy nutzen will muss ich fast immer casten
Willst du nun kleine Ganzzahlen haben (uint8_t / int8_t) oder Zeichen
(strcmp / strcpy)? Da solltest du dir schon mal einig werden.
Normalerweise gibt es zwischen beiden eine Import- und eine
Export-Schnittstelle, beispielsweise zwischen dem Datenregister der
UART (oftmals uint8_t) und der internen Zeichendarstellung (char).
An dieser Stelle gehört der Typecast hin, und nicht wild kreuz und
quer durch den Code.
Alle meckern hier über C, aber dass man eben zwischen Ganzzahlen und
Zeichen sauber trennen muss statt rumzuschlampen, dass wollen die
Meckerer dann auch wieder nicht wahrhaben … wie oben schon geschrieben
wurde, alle anderen Sprachen trennen sauber zwischen den beiden und
erzwingen einen geordneten Übergang (chr() / ord() etc.).
Jörg Wunsch schrieb:> Alle meckern hier über C, aber dass man eben zwischen Ganzzahlen und> Zeichen sauber trennen muss statt rumzuschlampen,
ich meckere nicht, du verstehst offensichlich mein Problem nicht, macht
aber nix.
Jetzt habe ich einen Grund zum meckern.....(vielleicht liegts am java,
vieleicht am Preprozessor
mytools.h
(i2c_test_flags&(1<<RTC_3231))?DEBUG_PRINTLN(F(" DS3231 RTC ist gestellt")):DEBUG_PRINTLN(F(" DS1307 RTC ist gestellt"));
setup.ino: In function 'void setup()':
setup:250: error: expected primary-expression before ';' token
mir ist schon klar warum, nervt trotzdem :-)
Lösung oder gibts ne bessere?
1
#ifdef DEBUG
2
(i2c_test_flags&(1<<RTC_3231))?Serial.println(F(" DS3231 RTC ist gestellt")):Serial.println(F(" DS1307 RTC ist gestellt"));
3
#endif
ich gebe ja zu das ich kein Infomatiker oder genialer Progger bin.
Joachim B. schrieb:> ich meckere nicht, du verstehst offensichlich mein Problem nicht, …
Dein Problem scheint mir zu sein, dass du [u]int8_t auch dann
benutzt, wenn es eben char sein müsste.
Ein Array voller Bytes (uint8_t) übergibt man nicht an strcmp(),
das hat keinen Sinn. Ist das Array aber voller Text, dann sollte
es eben auch als "char" deklariert sein.
> Lösung oder gibts ne bessere?
Schwierig. Normalerweise könnte man sich mit einem do{}while(0)
behelfen als Makroerweiterung im nicht-DEBUG-Fall, aber im Falle des
Fragezeichenoperators geht das natürlich nicht, denn der erwartet ja
keine Anweisungen, sondern Ausdrücke. Hier wiederum würde es
genügen, im nicht-DEBUG-Fall den Makro einfach zu 0 zu erweitern, aber
das passt natürlich an vielen anderen Stellen nicht.
Ist ein arger Fall von Missbrauch des ternären Operators. ;-)
Warum benutzt du den ?:-Operator, wenn dich die Rückgabewerte der
Operanden gar nicht interessieren? Schreib stattdessen ganz klassisch
einfach eine If-Else-Anweisung:
Jörg Wunsch schrieb:> Dein Problem scheint mir zu sein, dass du [u]int8_t auch dann> benutzt, wenn es eben char sein müsste.>> Ein Array voller Bytes (uint8_t) übergibt man nicht an strcmp(),> das hat keinen Sinn. Ist das Array aber voller Text, dann sollte> es eben auch als "char" deklariert sein.
vieleicht habe ich mich verschrieben,
Tatsache bleibt, das ich bei Text und LIB Funktionen ab und an mit den
Definitionen zusammen rassel
Ich definiere Text (für mich logisch) unsigned char, die Funktionen
erwarten ein char.
Anderes Beispiel:
Ich verwende ein Array
char text[41];
und die Var ist für mich ein Pointer auf erste Element
text -> (char *)&text[0]
manchmal klappt strcpy(neu, text);
manchmal meckert der Compiler und es klappt nur mit:
strcpy(neu, (char *)text);
weil text ein Array of char ist und kein Pointer
beobachte ich gerade einen Wandel oder warum ist das so?
Klar bemühe ich mich sauber zu programmieren, klappt nicht immer, aber
immer Array of char zu Pointer casten ist auch irgendie
Zeitverschwendung.
Joachim B. schrieb:> Ich definiere Text (für mich logisch) unsigned char, die Funktionen> erwarten ein char.
Diese deine Logik ist eben die Unlogik: Text solltest du als “char”
deklarieren, ganz ohne “signed” oder “unsigned”.
Text ist alles das, bei dem man mit den Elementen nichts rechnen
will, also eben das beliebte "Hello, world!", das Ergebnis eines
sprintf(buf, "Die Zahl ist %d", 42) etc. pp. Der konkrete Datentyp,
in den die einzelnen Zeichen hier verpackt werden, interessiert
eigentlich den Programmierer gar nicht; die entsprechenden Objekte
müssen halt nur groß genug sein, die gewünschten Zeichen aufzunehmen
und durch die Bibliotheksfunktionen durchzuschieben.
Da man mit den Textelementen nichts rechnet, ist es auch völlig
schnuppe, ob sie nun als Ganzzahlen eine Vorzeicheninterpretation
hätten oder nicht.
> manchmal meckert der Compiler und es klappt nur mit:>> strcpy(neu, (char *)text);
Das möchte ich als konkreten Fall (in einem separaten Thread) sehen.
So, wie du es geschrieben hast, gibt es keinen Grund für den Cast.
> weil text ein Array of char ist und kein Pointer
Felder und Zeiger sind natürlich nicht identisch, aber ein Feld wird,
wenn man es irgendwo in einem Ausdruck oder als Argument einer
Funktion benutzt, stets implizit in einen Zeiger gewandelt.
Der wesentliche Unterschied ist dabei immer: wo wird der Speicher
alloziert? Ein Zeiger alloziert keinen, sondern zeigt nur auf etwas,
für das jemand anders schon Speicher bereitgestellt haben muss.
Jörg Wunsch schrieb:> Aber auch hier muss er in der Makro-Ersetzung den do{}while(0)-Trick> benutzen:> #define DEBUG_PRINT(x) do { } while (0)
Nicht unbedingt. Den Do-While-Trick braucht man nur für function-like
Makros, die eine Sequenz mehrerer Anweisungen enthalten, in diesem Fall
also überhaupt nicht.
@ Yalu X. (yalu) (Moderator)
>einfach eine If-Else-Anweisung:
Eben. Aber auch wenn es korrektes C ist, würde ich JEDEM empfehlen,
IMMER Klammern zu setzen.
1
if(i2c_test_flags&(1<<RTC_3231)){
2
DEBUG_PRINTLN(F(" DS3231 RTC ist gestellt"));
3
}else{
4
DEBUG_PRINTLN(F(" DS1307 RTC ist gestellt"));
5
}
Warum? Die Leute von ID-Software (DOOM & Co) machen das auch so, also
muss was dran sein ;-)
Im Ernst, damit ist die Gefahr deutlich kleiner, sich ins Knie zu
schießen, weil man DENKT; eine nachfolgende Anweisung wäre noch im if()
Zweig drin. Ausserdem lässt sich der Code leichter lesen und problemlos
um weitere Anweisungen erweitern.
Falk Brunner schrieb:> Die Leute von ID-Software (DOOM & Co) machen das auch so, also muss was> dran sein
Wobei deren Coding Styleguide schon eher ungewöhnlich ist. TABs
meidet man aufgrund der weitreichenden Differenzen, ob sie nun für
8 Zeichenpositionen (klassischer DEC-Terminal-Standard) oder nur für
4 (typischer Windows-Default) stehen, am besten komplett. Seit die
Editoren das Ein- und Ausrücken übernehmen (also seit 25 Jahren, wenn
man Emacs nutzt :) ist das keine Bequemlichkeitseinbuße mehr, und das
bisschen mehr Platz macht das Kraut auch nicht fett.
Die meisten Styleguides, die ich kenne, empfehlen auch nicht so viele
Leerzeichen bei Klammerung, sondern halten sich eher an das natürliche
Schriftbild, wie man es auch im Textsatz nutzen würde:
1
a+(b*c);
Ich gebe zu, dass ich bei kurzen, überschaubaren if-Anweisungen schon
auch mal ohne Klammern arbeite:
1
if(foo)
2
print("Foo!");
3
else
4
do_nonfoo_action();
aber ich kann auch mit Umgebungen leben, in denen der Styleguide
stets geschweifte Klammern zu sehen wünscht. ;-)
Falk Brunner schrieb:> if (i2c_test_flags&(1<<RTC_3231)) {> DEBUG_PRINTLN(F(" DS3231 RTC ist gestellt"));> } else {> DEBUG_PRINTLN(F(" DS1307 RTC ist gestellt"));> }
ja is klar, aber ob Klammer oder nicht, ich wette folgendes wird auch
nicht funktionieren, aber aus anderen Gründen:
1
(i2c_test_flags&(1<<RTC_3231))?{DEBUG_PRINTLN(F(" DS3231 RTC ist gestellt"))}:{DEBUG_PRINTLN(F(" DS1307 RTC ist gestellt"))};
hattest du dir überhaupt mal die Fehlermeldung angesehen?
setup.ino: In function 'void setup()':
setup:250: error: expected primary-expression before ';' token
der vermisst im ? das erste ;
Ich denke ich habe wenig Ahnung von Programmierung, du doch so viel
mehr:
Beitrag "Re: Arduino zu empfehlen?"
aber egal da du so gut bist in Programmieren teste ich das mal:
klappt wie ich dachte nicht!
setup.ino: In function 'void setup()':
setup:208: error: expected primary-expression before '{' token
setup:208: error: expected `:' before '{' token
setup:208: error: expected primary-expression before '{' token
setup:208: error: expected `;' before '{' token
setup:454: error: expected `}' at end of input
setup:454: error: expected `}' at end of input
setup:454: error: expected `}' at end of input
setup:454: error: expected `}' at end of input
wer ist nun der Schuldige?
java oder der preprozessor?
würde der Preprozessor einfach nur seine Aufgabe erledigen gäbe es kein
Problem sondern ne reine Textersetzung, ich tippe auf die doofe Arduino
IDE, wie schrieb ein Mod hier mal, er habe noch keine funktionierende
Java Anwendung gesehen.
@Joachim B. (jar)
>hattest du dir überhaupt mal die Fehlermeldung angesehen?
Sicher. Aber ich muss doch nicht auf jeden Beitrag antworten.
>setup.ino: In function 'void setup()':>setup:250: error: expected primary-expression before ';' token>der vermisst im ? das erste ;
Nö, einen primären Ausdruck VOR dem ;
>Ich denke ich habe wenig Ahnung von Programmierung,
Ja, wenig.
> du doch so viel>mehr:>Beitrag "Re: Arduino zu empfehlen?"
;-)
>aber egal da du so gut bist in Programmieren teste ich das mal:
Dazu bräuchte man mal den VOLLSTÄNDIGEN Quelltext.
Deine Fragmente allein nützen rein gar nichts.
>wer ist nun der Schuldige?
Zu 99% das Wesen vor der Tastatur ;-)
>java oder der preprozessor?
Weder noch.
>würde der Preprozessor einfach nur seine Aufgabe erledigen gäbe es kein
Tut er. Dort steckt der normale, aktuelle avr gcc drunter.
>Problem sondern ne reine Textersetzung, ich tippe auf die doofe Arduino>IDE,
Nö, die ruft nur den avr gcc auf, am Quelltext macht die rein gar
nichts.
> wie schrieb ein Mod hier mal, er habe noch keine funktionierende>Java Anwendung gesehen.
Jaja, all doof ausser mir.
Joachim B. schrieb:> ob Klammer oder nicht
Es ging in den Ausführungen über die geschweiften Klammern um eine
if-Anweisung, nicht um deinen Missbrauch des ternären Operators.
Der ternäre Operator ist ja zuweilen wirklich sinnvoll, aber er
wird eben innerhalb eines Ausdrucks benutzt, um einen Wert zu
ermitteln. Deine print-Anweisungen ermitteln aber gar keine Werte,
sondern du willst damit ja eine Aktion ausführen lassen.
Was er ganz gewiss nicht ist, ist ein allgemeiner Ersatz für die
if-Anweisung.
Joachim B. schrieb:> wie schrieb ein Mod hier mal, er habe noch keine funktionierende Java> Anwendung gesehen.
Das war übrigens kein Moderator, sondern Markus F.
Jörg Wunsch schrieb:> Es ging in den Ausführungen über die geschweiften Klammern um eine> if-Anweisung, nicht um deinen Missbrauch des ternären Operators.
bei #define DEBUG funktioniert, dann ist das klar
ohne #define DEBUG bleibt nicht mehr viel von
Joachim B. schrieb:> bin eingerostet
'n Tropfen Öl hilft manchmal Wunder. :-))
Manchmal ist es übrigens hilfreich, sich die Ausgabe nach dem
Präprozessing anzusehen. Das geht, indem man (bei sonst gleichen
-D- und -I-Optionen) dem Compiler ein -E statt des -c mit auf den
Weg gibt, und ggf. nach -o eine andere Ausgabedatei benennt. Wenn
du in der Arduino-IDE irgendwie an die Compiler-Kommandozeile
rankommst, die er live ausführt, copy&paste in ein Terminalfenster,
dort dann die Zeile entsprechend editieren und ausführen.
Jörg Wunsch schrieb:> Wenn> du in der Arduino-IDE irgendwie an die Compiler-Kommandozeile> rankommst,
puh, andere Baustelle, ich bin ja schon froh das ich in der IDE die LST
und HEX gefunden hatte (das läuft i.d.R hinterm Rücken des Users ab,
aber ich muss mir ab und an den verbrauchten RAM ansehen um ungefähr
abzuschätzen ob ich zuviel belegt habe und nix mehr für den Stack
überbleibt.)
Jörg Wunsch schrieb:> Stimmt, eine leere Anweisung (nur ein Semikolon) in den beiden Zweigen> der if-Anweisung sollte natürlich zulässig sein.
...werden aber teilweise angewarnt. Also doch do/while oder (void)0.
Jörg Wunsch schrieb:> Der ternäre Operator ist ja zuweilen wirklich sinnvoll,Jörg Wunsch schrieb:> Was er ganz gewiss nicht ist, ist ein allgemeiner Ersatz für die> if-Anweisung.
ist aber auch schön einzeilig :-)
ich mag den, kurz und lesbar, aber wie wir heute sahen auch ein
Fallstrick.
Daniel A. schrieb:> Damit währe die schlimmste variante wohl gefunden.
danke muss ich nicht haben.
Falk Brunner schrieb:> Wie man Probleme mit dem ? Operator löst, die man ohne ihn nie hätte . .
stimmt, ich finde den nun m.E. trotzdem gut.
diese if else Konstrukte für mögliche Einzeiler nerven mich halt.
Immer dran denken, jeder Jeck ist anders.
da gruselt mich das eher:
Beitrag "Wie die Adresse einer "#define - Konstanten" ermitteln?"
Joachim B. schrieb:> diese if else Konstrukte für mögliche Einzeiler nerven mich halt.
Es steht dir frei, auch eine If-Else-Anweisung in eine einzelne Zeile zu
schreiben. Ich persönlich würde das aber nicht tun. Ebenso würde ich
deinem Rattenschwanz
1
(i2c_test_flags&(1<<RTC_3231))?DEBUG_PRINTLN(F(" DS3231 RTC ist gestellt")):DEBUG_PRINTLN(F(" DS1307 RTC ist gestellt"));
durch zwei Umbrüche etwas mehr Struktur geben:
1
(i2c_test_flags&(1<<RTC_3231))
2
?DEBUG_PRINTLN(F(" DS3231 RTC ist gestellt"))
3
:DEBUG_PRINTLN(F(" DS1307 RTC ist gestellt"));
wenn es denn unbedingt der ?:-Operator sein muss.
Zur Verwendung diese Operators schließe ich mich der Aussage von Jörg
an:
Jörg Wunsch schrieb:> Der ternäre Operator ist ja zuweilen wirklich sinnvoll, aber er> wird eben innerhalb eines Ausdrucks benutzt, um einen Wert zu> ermitteln.
Ich benutze ihn auch nur für kurze Teilausdrücke in den beiden Zweigen
und auch nur für Ausdrücke ohne Nebeneffekte. Dein obiges Beispiel
verletzt gleich alle drei dieser Bedingungen, ist also für mich eine
ganz klare Anwendung des If-Else-Konstrukts, obwohl ich sonst auch eher
ein "Zeilensparer" bin.
Aber da hat natürlich jeder seinen eigenen Geschmack :)
Joachim B. schrieb:> vieleicht habe ich mich verschrieben,>> Tatsache bleibt, das ich bei Text und LIB Funktionen ab und an mit den> Definitionen zusammen rassel>> Ich definiere Text (für mich logisch) unsigned char, die Funktionen erwarten ein
char.
Also mit anderen Worten: char ist dafür vorgesehen, aber du nimmst
stattdessen lieber unsigned char und regst dich dann darüber auf, daß du
überall nach char casten mußt. Das ist dann das, was für mich unlogisch
klingt. Ist ungefähr so, als ob ich mit dem Auto immer nur rückwärts
fahre und mich dann über dieses beschwere, weil es so langsam ist und
ich davon immer Nackenschmerzen bekomme.
Ich hab's aber durchaus schon öfters gesehen, daß Leute der Meinung
sind, Text sei eine vorzeichenlose Zahl. Ist es aber nicht. Es ist
Text, und mit Text rechnet man nicht, PUNKT
Die Regel wurde schon genannt und ist doch eigentlich ganz einfach. Ich
verstehe nicht, warum so viele damit nicht klar kommen:
char ist für Text.
[un]signed char ist für kleine Integer.
Bei der Übergabe von Zeichen an eine low-level-Funktion oder ein
HW-Register muß ggf. konvertiert werden.
Und das ist alles. Wenn man das beachtet, hat man mit char überhaupt
kein Problem.
> Anderes Beispiel:> Ich verwende ein Array> char text[41];> und die Var ist für mich ein Pointer auf erste Element>> text -> (char *)&text[0]>> manchmal klappt strcpy(neu, text);>> manchmal meckert der Compiler und es klappt nur mit:>> strcpy(neu, (char *)text);>> weil text ein Array of char ist und kein Pointer>> beobachte ich gerade einen Wandel oder warum ist das so?
Warum das bei dir so ist, weiß ich nicht. Ein Array kann praktisch immer
wie ein Zeiger auf sein erstes Element verwendet werden. Der Code ist
ohne Cast vollkommen korrekt, also wird dein Beispiel nicht dem echten
Code entsprechen, gegen den der Compiler was hatte.
Ich finds schon komisch, was daran denn so schwer zu verstehen ist, daß
#define nur eine Textersetzung ist.
Wenn eine Fehlermeldung kommt, braucht man doch nur den Text direkt
einzusetzen und schon sieht man den Fehler.
Beim for(;;) kann man die 3 Ausdrücke leer lassen.
Beim ?: müssen aber alle 3 Ausdrücke existieren.
Ein simples:
Peter Dannegger schrieb:> Ein simples:
ich wusste das DU die Lösung weisst, kann ich mir ja die PN sparen, 1000
Dank.
LG jar
PS das es eine reine Textersetzung ist wusste ich, auch das dann nur ? :
; übrig bleibt undd damit den Fehler provoziert auch.
Auf deine Lösung bin ich nicht gekommen und ich war wohl nicht der
einzige der nicht diese Lösung fand obwohl ja hier alle anderen besser
proggen können als ich.
Joachim B. schrieb:> Auf deine Lösung bin ich nicht gekommen und ich war wohl nicht der> einzige der nicht diese Lösung fand obwohl ja hier alle anderen besser> proggen können als ich.
Du meinst die Sache mit der 0 im #else-Zweig?
Diese Möglichkeit hat doch Jörg weiter oben schon genannt:
Jörg Wunsch schrieb:> Hier wiederum würde es genügen, im nicht-DEBUG-Fall den Makro einfach> zu 0 zu erweitern
Yalu X. schrieb:> Du meinst die Sache mit der 0 im #else-Zweig?>> Diese Möglichkeit hat doch Jörg weiter oben schon genannt:
jetzt wo du es sagst :-)
Jörg Wunsch schrieb:> Schwierig. Normalerweise könnte man sich mit einem do{}while(0)> behelfen als Makroerweiterung im nicht-DEBUG-Fall, aber im Falle des> Fragezeichenoperators geht das natürlich nicht, denn der erwartet ja> keine Anweisungen, sondern Ausdrücke. Hier wiederum würde es> genügen, im nicht-DEBUG-Fall den Makro einfach zu 0 zu erweitern,
hat er aber gut versteckt und keine code Tags benutzt.
OK es sind (mindestens) 2 Progger die definitiv besser sind als ich
Wenn du jetzt aber DEBUG_PRINT außerhalb einer ?:-Fallunterscheidung
benutzt
1
DEBUG_PRINT("Hallo");
steht da nach der Makroexpansion
1
0;
Das ist zwar legal, aber unschön und veranlasst den Compiler zur Warnung
1
statement with no effect
sofern man die Warnungen mit -Wall aktiviert hat (was man eigentlich
immer tun sollte).
Um diese Warnung zu umgehen, könnte man die 0 noch in void casten:
1
#define DEBUG_PRINT(x) (void)0
Dann bekommt man aber eine Fehlermeldung, wenn man den Rückgabewert von
DEBUG_PRINT (der ja definitiv existiert) doch irgenwann einmal auswerten
möchte.
Glaub's mir: Die Fallunterscheidung mit ?: ist hier einfach fehl am
Platz. Nimm ein if-else, und alles wird gut.
Yalu X. schrieb:> Glaub's mir: Die Fallunterscheidung mit ?: ist hier einfach fehl am> Platz. Nimm ein if-else, und alles wird gut.
oder anders, ist schon gut weil jetzt aktuell if/else
ich dachte ich könnte zurück zu ? : ;