Datum:
Ist es erlaubt das Ergebnis eines Vergleichs als Zahl weiter zu verwenden? Beispiel: x = 2 + ( i == 4 ); ist x bei i = 4 dann 3 ? bzw x bei i = 1 dann 2 ? Sorry, bin noch am Anfang. Habe herumgegoogelt, aber nix gefunden. Könnte das für eine formatierte Ausgabe eines Strings am Display brauchen. LG Rudi
Datum:
Ja, ist erlaubt.
Datum:
Allgemein ist es besser so zu programmieren, das für andere das was man bezwecken will, offensichtlich ist. Ich würde dir empfehlen, den "?" Operator zu verwenden.
x = 2 + (i == 4 ? 1 : 0); |
Datum:
... schrieb: Ich würde dir empfehlen, den "?" > Operator zu verwenden. > >
> x = 2 + (i == 4 ? 1 : 0); > |
den kenne ich noch nicht. Sollte ich das auch so verstehen können, dass man statt 1 : 0 auch andere Werte einsetzen könnte? LG Rudi
Datum:
... schrieb: > x = 2 + (i == 4 ? 1 : 0); Das finde ich jetzt aber auch nicht unbedingt lesbarer. Dann lieber gleich:
if (i == 4) x = 3; else x = 2; |
oder
x = 2; if (i == 4) x++; |
Rudi D. schrieb: > ... schrieb: > Ich würde dir empfehlen, den "?" >> Operator zu verwenden. >> >>> x = 2 + (i == 4 ? 1 : 0); >> > den kenne ich noch nicht. > Sollte ich das auch so verstehen können, dass man statt 1 : 0 auch > andere Werte einsetzen könnte? Klar. Du könntest auch schreiben:
x = (i == 4 ? 3: 2); |
Rudi D. schrieb: > Könnte das für eine formatierte Ausgabe eines Strings am Display > brauchen. Denkst du da evtl. an sowas?
printf("Das Licht ist %s\n", licht == AN ? "an" : "aus"); |
Datum:
Nein. Aber so. Habe eine Zeile eines 2x16 LCD, möchte 3x einen String mit 5 Zeichen ausgeben. Ich beginne an Pos1., dann Pos6 und Pos11 So bleibt die letzte Position ungenützt, wäre aber für die Lesbarkeit besser.
lcd_set_cursor(2, (5*i + (i == 2))); lcd_write(str); |
So kann ich die 16. Position auch verwenden und die Lesbarkeit wird besser, falls der letzte String ein - Vorzeichen hat und direkt an den 2. String anschließen würde. Danke für die schnelle und zahlreiche Hilfe! LG Rudi
Datum:
nicht ohne Grund werden im MISRA standard implizite Typumwandlungen sehr skeptisch betrachtet und teilweise verboten. Auch wenn es der C-Standard zulässt. Meine Antwort: besser nicht. Es gibt zB den Fragezeichenoperator, der hier hilfreich und sauber arbeitet. Adib.
Datum:
Adib T. schrieb: > nicht ohne Grund werden im MISRA standard implizite Typumwandlungen sehr > skeptisch betrachtet und teilweise verboten. Auch wenn es der C-Standard > zulässt. > > Meine Antwort: besser nicht. > Es gibt zB den Fragezeichenoperator, der hier hilfreich und sauber > arbeitet. Welche implizite Typumwandlung meinst du? "(i == 4)" und "(i == 4 ? 1 : 0)" haben beide den gleichen Typ, nämlich int. Die zweite Variante ist in keiner Hinsicht irgendwie "sauberer". Die Wahl zwischen beiden ist ausschließlich eine Frage des persönlichen Geschmacks.
Datum:
Stefan Ernst schrieb: > "(i == 4)" und "(i == 4 ? 1 : 0)" haben beide den gleichen Typ, nämlich > int. Das möchte ich bezweifeln, erstere ist nämlich vom typ bool
Datum:
float schrieb: > Das möchte ich bezweifeln, erstere ist nämlich vom typ bool in (alten) c gibt es aber gar kein bool
Datum:
float schrieb: > Stefan Ernst schrieb: >> "(i == 4)" und "(i == 4 ? 1 : 0)" haben beide den gleichen Typ, nämlich >> int. > > Das möchte ich bezweifeln, erstere ist nämlich vom typ bool Falsch. Der Standard sagt explizit "The result has type int".
Datum:
Stefan Ernst schrieb: > Falsch. Der Standard sagt explizit "The result has type int". Die Frage ist ja immer welcher Standard. Bewegt sich der OP in C oder C++? :P
Datum:
Könnte es nicht auch Java oder Objective-C sein ;-)
Datum:
Fakt ist doch, das bei der Variante
x = 2 + ( i == 4 ); |
bei sehr vielen Programmierern Fragen über Typ und Wert-Definition im C-Standard auftauchen. Genau deshalb ist die Variante
x = 2 + ( i == 4 ? 1 : 0); |
besser. Da gibt es diese Fragen nicht und der Code ist trotzdem noch kurz und übersichtlich. @Rolf Ich bin der Meinung, das unnötig langer Code wie dein
if (i == 4) x = 3; else x = 2; |
auch nicht die Lesbarkeit fördert. Aber das hängt wohl vom persönlichen Geschmack ab.
Datum:
_\|/_ schrieb: > @Rolf > Ich bin der Meinung, das unnötig langer Code wie dein > if (i == 4) > x = 3; > else > x = 2; > auch nicht die Lesbarkeit fördert. Aber das hängt wohl vom persönlichen > Geschmack ab. Ja, das ist Ansichtssache. Aber in einer Zuweistung noch einen Vergleich zu verstecken, gefällt mir halt gar nicht, weder mit ?:, noch ohne. Wenn es ein Problem ist, wenn der Code eine Zeile oder zwei länger ist, dann sind entweder die Funktionen zu lang oder der Bildschirm zu klein. was? schrieb: > Stefan Ernst schrieb: >> Falsch. Der Standard sagt explizit "The result has type int". > > Die Frage ist ja immer welcher Standard. Bewegt sich der OP in C oder > C++? :P Bei C++ ist es bool. In C hätte ich das auch erwartet (bzw. _Bool), aber in der entsprechenden Passage findet man tatsächlich: Stefan Ernst schrieb: > "The result has type int". Oder entwas mehr davon: "The == (equal to) and != (not equal to) operators are analogous to the relational operators except for their lower precedence. Each of the operators yields 1 if the specified relation is true and 0 if it is false. The result has type int."
Datum:
_\|/_ schrieb: > Fakt ist doch, das bei der Variante >
> x = 2 + ( i == 4 ); > |
> bei sehr vielen Programmierern Fragen über Typ und Wert-Definition im > C-Standard auftauchen. Geb ich dir recht, auch wenn es nicht so sein sollte. > Genau deshalb ist die Variante >
> x = 2 + ( i == 4 ? 1 : 0); > |
> besser. Da gibt es diese Fragen nicht und der Code ist trotzdem noch > kurz und übersichtlich. Diese Version hat dann auch noch den Vorteil, dass * der Leser den ?: Operator kennen lernt (siehe weiter oben - "kannte ich noch gar nicht" * sie universeller mit allen möglichen Datentypen einsetzbar ist > Ich bin der Meinung, das unnötig langer Code wie dein So richtig interessant, wird das ganze dann, wenn man den ? Operator in einem Ausdruck mehrmals einsetzen kann. Ein
UpdateStatus( status == STAT_ERROR ? "Fehler" : "OK", msg == NO_MSG ? "" : msgCode, strcmp( User, "Admin" ) ? "****" : User->Password ); |
ersetzt man nicht so einfach mit if-else Ketten ohne dass sich das alles enorm in die Länge zieht.
Datum:
Karl Heinz Buchegger schrieb: >> Genau deshalb ist die Variante >>> x = 2 + ( i == 4 ? 1 : 0); >> > besser. Da gibt es diese Fragen nicht und der Code ist trotzdem noch >> kurz und übersichtlich. > > Diese Version hat dann auch noch den Vorteil, dass > * der Leser den ?: Operator kennen lernt oder den Nachteil, daß er damit genauso wenig anfangen kann wie mit der Variante ohne ?: und daß... _\|/_ schrieb: > bei sehr vielen Programmierern Fragen über Typ und Wert-Definition im > C-Standard auftauchen. Ich muß auch sagen, daß für mich "vergleich ? 1 : 0" irgendwie komisch aussieht, weil ich weiß, daß der Vergleich selbst ja schon genau die selben Ergebnisse zurückliefert. Das empfinde ich dann eher als unnötig lang, als die if-Anweisung. Karl Heinz Buchegger schrieb: > * sie universeller mit allen möglichen Datentypen einsetzbar ist Und vor allem eben auch mit beliebigen Werten statt nur mit 0 und 1.
Datum:
Rolf Magnus schrieb: > Karl Heinz Buchegger schrieb: >>> Genau deshalb ist die Variante >>>> x = 2 + ( i == 4 ? 1 : 0); >>> > besser. Da gibt es diese Fragen nicht und der Code ist trotzdem noch >>> kurz und übersichtlich. >> >> Diese Version hat dann auch noch den Vorteil, dass >> * der Leser den ?: Operator kennen lernt > > oder den Nachteil, daß er damit genauso wenig anfangen kann wie mit der > Variante ohne ?: und daß... Tja. Da kann ich ihm aber nicht helfen. Wer Möbel tischlern will kommt nicht umhin die dazu notwendigen Werkzeuge und deren Gebrauch zu erlernen. :-) > Ich muß auch sagen, daß für mich "vergleich ? 1 : 0" irgendwie > komisch aussieht, weil ich weiß, daß der Vergleich selbst ja schon > genau die selben Ergebnisse zurückliefert. Ist ein bischen so, wie
if( ( i > j ) == TRUE )
|
Datum:
Rolf Magnus schrieb: > Karl Heinz Buchegger schrieb: >> Diese Version hat dann auch noch den Vorteil, dass >> * der Leser den ?: Operator kennen lernt > > oder den Nachteil, daß er damit genauso wenig anfangen kann wie mit der > Variante ohne ?: und daß... > > _\|/_ schrieb: >> bei sehr vielen Programmierern Fragen über Typ und Wert-Definition im >> C-Standard auftauchen. > > Ich muß auch sagen, daß für mich "vergleich ? 1 : 0" irgendwie komisch > aussieht, weil ich weiß, daß der Vergleich selbst ja schon genau die > selben Ergebnisse zurückliefert. Puh! ganz schöne Diskussion für meine kleine Frage. Mir sagt die "? "Operatorschreibweise sehr zu, da sie für mich sehr übersichtlich ist. Wichtig war mir die Ergebnisse eines Vergleiches als Zahl zu verwenden, was ja bestätigt wurde. LG Rudi
Datum:
Rudi D. schrieb: > Wichtig war mir die Ergebnisse eines Vergleiches als Zahl zu verwenden, > was ja bestätigt wurde. In C hat jeder Ausdruck ein Ergebnis, welches weiterverwendet werden kann. Selbst eine Zuweisung! Daher kann man schreiben x = y = 0; Ein if verlangt in der Klammer keinen Vergleich, sondern einen arithmetischen Ausdruck, der entweder 0 oder nicht 0 als Ergebnis haben kann. Du kannst also schreiben if( x ) und testest damit x auf 0 (oder eben nicht 0) Der häufigste Regelfall mit einem Vergleichsoperator ... if( y > 5 ) ... ist also nichts anderes als ein Sonderfall eines bestimmten arithmetischen Ausdrucks, bei dem der Operator 0 oder 1 (und damit 'nicht-0') liefert, welches dann vom if als Entscheidungsgrundlage benutzt wird. Aber dem if ist es grundsätzlich völlig wurscht, was da in der Klammer steht. Da muss nur etwas sein, was entweder 0 oder nicht 0 ist. Das heißt aber auch: In C sind die Verlgeichsoperatoren ganz normale arithmetische Operatoren, so wie es + * / - ... auch sind. Und du erwartest ja schliesslich auch, dass du mit dem Ergebnis von 2 + 3 noch weiterrechnen kannst.
Datum:
Wie Du weisst beginne ich ja erst mit C. An sich habe ich das Buch von Franzis: C/C++ Referenz. Dort habe ich auch schon gefunden, dass das Ergebnis von bool'schen Operationen 0 oder nicht 0 sein kann. Aber auch 1 oder 0 . Bin noch etwas verwirrt darüber. Aber das schaffe ich selbst. Nur lerne ich nicht gut nur aus Büchern. Dort habe ich dann natürlich auch den ?: Operator gefunden. Aber erst als ich vom Forum darauf hingewiesen wurde. Dieses Forum ergänzt meinen Wissensaufbau ungemein. LG Rudi
Datum:
Rudi D. schrieb: > An sich habe ich das Buch von Franzis: C/C++ Referenz. Referenz? Das klingt eher nach Nachschlagewerk und ist damit vielleicht nicht das Beste zum Erlernen. > Dort habe ich > auch schon gefunden, dass das Ergebnis von bool'schen Operationen 0 oder > nicht 0 sein kann. > Aber auch 1 oder 0 . Bin noch etwas verwirrt darüber. Wo C einen boolschen Wert erwartet, wird 0 als false und alles andere als true interpretiert. Als Ergebnis einer boolschen Operation erzeugt C aber nur die Werte 0 und 1. Deshalb kann man einen beliebigen Zahlenwert mit doppelter Negation auf 0 oder 1 reduzieren: !!zahl
Datum:
Andreas B. schrieb: > Deshalb kann man einen beliebigen Zahlenwert mit doppelter Negation auf > 0 oder 1 reduzieren: !!zahl Das sind aber schon die höheren Weihen! Danke, An sich erkenne ich schon jetzt einige Vorteile gegenüber Assembler, wo ich natürlich auch einen Vorrat an nützlichen Routinen habe. Jetzt wo ich ein Projekt beginne, wo ich float brauche, ging es nicht anders als C zu verwenden. Aber auch z.B. das Makro ADCW erspart mir doch einiges gegenüber Assembler, wo ich das Hi and Lo Byte des ADC getrennt holen muss, falls 10 bit gebraucht werden. LG Rudi
Datum:
Andreas B. schrieb: > Rudi D. schrieb: >> An sich habe ich das Buch von Franzis: C/C++ Referenz. > > Referenz? Das klingt eher nach Nachschlagewerk und ist damit vielleicht > nicht das Beste zum Erlernen. Die Kritiken bei Amazon sind geteilter Meinung. Wobei mir die negativ-Kritiken sachlich fundierter erscheinen.
Datum:
Ich musste zwar hier lernen, dass bei if kein bool'scher Wert benötigt wird, aber ich habe hier noch einen interessanten Link: http://www.rn-wissen.de/index.php/Fallstricke_bei_... Es gibt da noch so eine empfohlene MISRA Regel: Rule 49 (advisory): Tests of a value against zero should be made explicit, unless the operand is effectively Boolean Nebenfrage: Gibt es bei der Verwendung von dem Typ bool aus <stdbool.h> gegenüber der Verwendung von uint8_t irgendein rechentechnischer Vorteil? Gruss, Adib.
Datum:
Adib T. schrieb: > Es gibt da noch so eine empfohlene MISRA Regel: > Rule 49 (advisory): > Tests of a value against zero should be made explicit, unless the > operand is effectively Boolean Es gibt etliche MISRA-Regeln. Deren konsequente Anwendung halte ich für Unfug, aber da gibt es verschiedene Meinungen. > > Nebenfrage: Gibt es bei der Verwendung von dem Typ bool aus <stdbool.h> > gegenüber der Verwendung von uint8_t irgendein rechentechnischer > Vorteil? Spätestens nach der Optimierung sollte es keinen mehr geben (davon abgesehen, daß man mit bool halt nicht rechnen kann). In der Regel ist das nicht das Wichtigste beim Programmieren.
Datum:
sebastian schrieb: > Ja, ist erlaubt. In C ist nur definiert daß FALSE == 0 ist TRUE muß nicht zwingend 1 sein. Das ist implementierungsabhängig. Ich könnte mir vorstellen daß auf einigen Prozessoren auch der Wert -1 optimaler ist als +1. Adib T. schrieb: > Nebenfrage: Gibt es bei der Verwendung von dem Typ bool aus <stdbool.h> > gegenüber der Verwendung von uint8_t irgendein rechentechnischer > Vorteil? Es gibt Prozessoren die einzelne Bits in boolschen Variablenbereichen speichern können. (8031 oder TC1797). Gruß Anja
Datum:
Anja schrieb: > sebastian schrieb: >> Ja, ist erlaubt. > > In C ist nur definiert daß FALSE == 0 ist TRUE muß nicht zwingend 1 > sein. Das ist implementierungsabhängig. Siehe Beitrag "Re: True =1 false =0 kann man damit rechnen?" . So wie es da steht, ist es in C definiert. In Beitrag "Re: True =1 false =0 kann man damit rechnen?" hab ich auch die etsprechende Passage der ISO-Norm zitiert. > Ich könnte mir vorstellen daß auf einigen Prozessoren auch der Wert -1 > optimaler ist als +1. Das spielt keine Rolle. Ein Vergleich gibt immer 0 oder 1 zurück. Wenn nicht, ist das ein Fehler im Compiler. > Adib T. schrieb: >> Nebenfrage: Gibt es bei der Verwendung von dem Typ bool aus <stdbool.h> >> gegenüber der Verwendung von uint8_t irgendein rechentechnischer >> Vorteil? > > Es gibt Prozessoren die einzelne Bits in boolschen Variablenbereichen > speichern können. (8031 oder TC1797). Paßt aber nur schwer mit ISO-C zusammen.
Datum:
Rolf Magnus schrieb: > Das spielt keine Rolle. Ein Vergleich gibt immer 0 oder 1 zurück. Wenn > nicht, ist das ein Fehler im Compiler. Wenn in einem boolean mal etwas Anderes als 0 oder 1 drin ist, kommt beim avr-gcc auch etwas Anderes raus. Ausprobieren und staunen...
Datum:
Marwin schrieb: > Rolf Magnus schrieb: > >> Das spielt keine Rolle. Ein Vergleich gibt immer 0 oder 1 zurück. Wenn >> nicht, ist das ein Fehler im Compiler. > > Wenn in einem boolean mal etwas Anderes als 0 oder 1 drin ist, kommt > beim avr-gcc auch etwas Anderes raus. Ausprobieren und staunen... Ausprobiert:
#include <stdbool.h> bool f(void) { bool v = 5; return v; } |
Bekommen:
00000000 <f>: 0: 81 e0 ldi r24, 0x01 ; 1 2: 08 95 ret |
Gestaunt: Der hat ja unrecht, der Marwin.
Datum:
Adib T. schrieb: > Nebenfrage: Gibt es bei der Verwendung von dem Typ bool aus <stdbool.h> > gegenüber der Verwendung von uint8_t irgendein rechentechnischer > Vorteil? Es kann sein, dass bei einer Maschine 8-Bit Daten zwar im Speicher weniger Platz verbrauchen, aber ansonsten im Umgang weniger effizient als 32- oder 64-Bit Daten sind. In dem Fall ist _Bool äquivalent zu uint_fast8_t und dieser Typ ist nicht identisch mit uint8_t sondern mit beispielsweise mit unsigned. Das dürfte bei RISC Prozessoren öfter der Fall sein, weil die in Registern mit 32/64-Bit Daten besser zurecht kommen als mit 8-Bit Daten. So beispielsweise bei IBM Power: sizeof(_Bool)==4.
Datum:
Andreas B. schrieb: > #include <stdbool.h> > > bool f(void) > { > bool v = 5; > > return v; > } das ist zu einfach, das durchschaut der compiler
#include <stdbool.h> bool f(void) { uint8_t v = 5; return (bool)v; } |
könnte besser sein.
Datum:
Nö.
#include <stdbool.h> #include <stdint.h> bool f(void) { uint8_t v = 5; return v; } |
00000000 <f>: 0: 81 e0 ldi r24, 0x01 ; 1 2: 08 95 ret |
Marwin (Gast) hat halt irgendeine Behauptung in den Raum gestellt und es nicht näher erläutert. Wahrscheinlich selber was falsch verstanden. Ohh, gleich noch ein Beispiel:
#include <stdbool.h> #include <stdint.h> bool f(uint8_t v) { return v; } |
00000000 <f>: 0: 91 e0 ldi r25, 0x01 ; 1 2: 88 23 and r24, r24 4: 01 f4 brne .+0 ; 0x6 <f+0x6> 6: 90 e0 ldi r25, 0x00 ; 0 8: 89 2f mov r24, r25 a: 08 95 ret |
Datum:
Adib T. schrieb: > Es gibt da noch so eine empfohlene MISRA Regel: > Rule 49 (advisory): > Tests of a value against zero should be made explicit, unless the > operand is effectively Boolean Das ist so eine Sache mit dem MISRA. Wen unfähige Programmierer loslegen, wird es nicht 'besser' oder sicherer, wenn sie sich dabei stupide an vorgekaute Regeln klammern, die sie nicht verstehen... Dieser Absatz im RN-Wiki jedenfalls ist grob falsch, auch der Autor dort hat das Grundproblem augenscheinlich nicht verstanden.
Datum:
Sven P. schrieb: > Dieser Absatz im RN-Wiki jedenfalls ist grob falsch, auch der Autor dort > hat das Grundproblem augenscheinlich nicht verstanden. Jetzt erst gesehen: Sehe ich genauso. Insbesondere ist mir jetzt auch nach 4 Minuten darüber grübeln nicht klar, was er mit seinen Alternativmakros eigentlich bezwecken will. IMHO ändern die gar nichts. Das Problem kriegt er in den Griff mit einer meiner Faustregeln. "In C ist weniger oft mehr." Und das steht im krassen Gegensatz zur MISRA Regel.
Datum:
A. K. schrieb: > So beispielsweise bei IBM Power: sizeof(_Bool)==4. PS: abhängig von der GCC-Version...
Datum:
Zu dem RN-Artikel: Er will dann doch zwei als boolsch zu verstehende Wert wieder mit Bitwise-Operatoren & verknüpfen. Eigentlich sollte er ja, um konsistent zu bleiben && verwenden und dann wäre auch niemand vom Resultat enttäuscht.
Datum:
Andreas B. schrieb: > Marwin (Gast) hat halt irgendeine Behauptung in den Raum gestellt und es > nicht näher erläutert. Wahrscheinlich selber was falsch verstanden. Du hast einfach nicht verstanden, was ich geschrieben habe. Aber statt nachzufragen wird gleich wieder ein persoenlicher Angriff rausgerotzt. In deinem Beispiel: bool v = 5; ist v natuerlich nie 5, damit ist das Kriterium nicht erfuellt. Dass du Peter II's Beispiel erst verfaelscht und dann schreibst, es ginge nicht, ist ziemlich frech. Funktioniert anscheinend auch unter MacOS X gut (avr-gcc habe ich gerade nicht zur Hand): // #include <stdio.h> union strange { char c; bool b; }; main() { strange value; value.c = 13; bool real = value.b; while( real) { real = !real; fprintf( stdout, "%d %d %d\n", value.c, value.b, real); } }
Datum:
Noname schrieb: > Zu dem RN-Artikel: > > Er will dann doch zwei als boolsch zu verstehende Wert wieder mit > Bitwise-Operatoren & verknüpfen. Eigentlich sollte er ja, um konsistent > zu bleiben && verwenden und dann wäre auch niemand vom Resultat > enttäuscht. Das weiß man ja eben nicht. Sollte das so sein, besteht da kein Problem. Sollte er aber eine Bitmaske prüfen wollen, geht das in die Hose. Genauso, wie es in die Hose geht, wenn er 'a' und 'b' implizit als Boolean interpretiert haben möchte:
int a = 0x0F; int b = 0xF0; if (a) puts("a ist wahr"); if (b) puts("b ist wahr"); if (a & b) puts("tja."); |
Datum:
Marwin schrieb: > Funktioniert anscheinend auch unter MacOS X gut (avr-gcc habe ich gerade > nicht zur Hand): Hier schreibst du den Wert als char in die Union hinein und liest sie als bool wieder aus:
value.c = 13; bool real = value.b; |
Das Verhalten von so etwas ist m.W. nicht durch den C-Standard abgedeckt. So darfst du dich nicht wundern, wenn anschließend seltsame Dinge passieren.
Datum:
Marwin schrieb: > union strange > { > char c; > bool b; > }; > > main() > { > strange value; > value.c = 13; > > bool real = value.b; Nehmen wir den einzigen massgeblichen Massstab zur Hand, dein ISO-C-Standard, dann sagt der sehr klar: Ab hier bist du im Nomansland der undefined behaviour. Ab dieser Stelle ist jegliches Verhalten trivialerweise 'richtig', weil nicht definiert ist, was eigentlich das einzig richtige Verhalten wäre. Das Verhalten von Unions ist nur dann definiert, wenn über denselben Member gelesen wird, über den auch geschrieben wurde. Ist das nicht der Fall -> undefined behaviour.
Datum:
Marwin schrieb: > Funktioniert anscheinend auch unter MacOS X gut (avr-gcc habe ich gerade > nicht zur Hand): > [...] Das blöde an diesem Beispiel ist halt, dass es unzulässig ist... Der Compiler verhält sich aber strenggenommen korrekt. Du schreibst die 13 in 'char value.c'. Nun hast du eine Zuweisung von 'bool value.b' nach 'bool real', beide sind vom Typ 'bool'. Es erfolgt also keine implizite Typenumwandlung. Das hat zwei Konsequenzen: (1) Nach C-Standard entsteht bei einer Typenumwandlung eines Skalar nach 'bool' genau dann eine Null (false), wenn der Skalar 0 ist, sonst entsteht eine 1 (§6.3.1.2.1). Es passiert hier aber keine Typenumwandlung, der Compiler kopiert einfach. Darf er ja auch. (2) Der Zugriff auf 'value.b' ist ausdrücklich unspecified, denn die Alternative 'b' ist nicht diejenige, die zuletzt beschrieben wurde. Das war nämlich 'c' gewesen (§J.1). Du hast in deinem Beispiel also über unspecified behaviour einen Wert in den Boolean geschmuggelt.
Datum:
Sven P. schrieb: > Du hast in deinem Beispiel also über unspecified behaviour einen Wert in den Boolean geschmuggelt. Ja natuerlich habe ich das. Und sowas passiert in der Praxis schneller, als man denkt - wenn man Booleans aus dem EEPROM oder von der seriellen Schnittstelle liest zum Beispiel. Zur Union noch eine Randbemerkung: Dafuer, dass das unspecified behaviour ist, wird es hier im Forum ziemlich oft empfohlen!
Datum:
Marwin schrieb: > Ja natuerlich habe ich das. Und sowas passiert in der Praxis schneller, > als man denkt - wenn man Booleans aus dem EEPROM oder von der seriellen > Schnittstelle liest zum Beispiel. Ja klar, und selbst wenn nicht, da kommt noch die Endianness. Aber all das wird mit MISRA auch nicht besser oder sicherer... > Zur Union noch eine Randbemerkung: Dafuer, dass das unspecified > behaviour ist, wird es hier im Forum ziemlich oft empfohlen! Du spielst da vermutlich auf den Bit-Zugriff in Ports an. Es ist richtig, dass auch das natürlich unspecified ist. Eine gute Empfehlung aber erwähnt dann auch, für welchen Compiler es gedacht ist :-)
Datum:
Marwin schrieb: > Zur Union noch eine Randbemerkung: Dafuer, dass das unspecified > behaviour ist, wird es hier im Forum ziemlich oft empfohlen! Es wird hier im Forum auch immer wieder empfohlen, LEDs ohne Vorwiderstand zu betreiben ;-) Unions zur Konvertierung von beliebigen Datentypen in ihre Byte-Reprä- sentation ist ein Hack, der in Ausnahmefällen akzeptiert werden kann, wenn es in einer Anwendung auf jeden Taktzyklus Laufzeit ankommt, sau- berere Methoden zu langsam sind und keine Portabilität gefordert ist. Dein Beispiel zeigt aber schön, dass "undefined behavior" nicht auf die böse Anweisung (das fehlerhafte Auslesen aus der Union) beschränkt sein muss, sondern dass es sich beliebig weit im Programmlauf fortpflanzen kann (durch die "verseuchte" Bool-Variable), obwohl man das nicht ohne weiteres erkennt. Insofern ist es ein lehrreiches Argument gegen Union- Wildereien.
Datum:
Marwin schrieb: > Zur Union noch eine Randbemerkung: Dafuer, dass das unspecified > behaviour ist, wird es hier im Forum ziemlich oft empfohlen! Das Problem ist, dass es oft keine wirklich gute Alternative dazu gibt, als die Krot zu schlucken, das man hier auf eigentlich verbotenem Terrain agiert. Zumal der Einsatz einer Union zur Zerlegung in Einzelbytes eine relativ harmlose Operation ist, weil es an keine Werteinterpretation gebunden ist. Solange man die richtige Reihenfolge einhält, hat man es nur mit Bytes zu tun. In dem einen Fall als Verbund von Bytes im anderen Fall als Einzelbytes. Aber im Grunde hast du recht: Ja, eigentlich ist das nicht koscher.
Datum:
es geht auch ohne union schief. bool b; memcpy( &b, "x", sizeof( b ) ); danach würde ich mich nicht mehr darauf verlassen, das eine berechnung mit b = 1 ist.
Datum:
Peter II schrieb: > es geht auch ohne union schief. Was im Grunde für unions gilt, gilt auch für Pointercasts, selbst wenn sie implizite Konvertierungen sind. In dem Moment, in dem du einen Pointer umcastest, ist die Sprachdefinition am Ende mit ihren Zusagen. > memcpy( &b, "x", sizeof( b ) ); memcpy nimmt als erstes Argument keinen bool* Du akzeptierst ja auch, dass
int a[sizeof(double)/sizeof(int)]; double b = 5.0; memcpy( &a, &b, sizeof(double) ); |
aus genau dem gleichen Grund "schief" gehen wird und in a[0] alles mögliche, nur nicht 5 drinnen stehen wird, ohne dass dir das für die weitere Programmierung schlaflose Nächte beschert.
Datum:
Am ende ist es doch aber egal wenn die Spec eingehalten wird. Bei bool output gilt: 0 ist false 1 ist true Bei input gilt: 0 ist false alles andere ist true Code der sich durch true == >1 verwirren lässt ist entweder schlecht oder extrem auf performance getrimmt und es daher nicht anders geht.
Datum:
klugscheißer schrieb: > Am ende ist es doch aber egal wenn die Spec eingehalten wird. Und das wird sie durch eine verseuchte bool-Variable ja nicht mehr. Unspecified ist ja auch nicht negativ zu verstehen. Es ist vielmehr einfach eine Feststellung seitens der ISO. Aus Sicht des Compilers sind solche Dinge meistens nicht unspecified. Sonst müsste der Compiler recht häufig einen Zufallsgenerator bemühen, darum ist der Compiler ja auch dokumentiert. Wenn ich mich dann an die Dokumentation des Compilers halte, ist doch alles in Ordnung. Ich weiß ja dann, dass es mitunter icht mehr portabel ist, aber unspecified ist es ja nicht mehr.
Datum:
> strange value; > value.c = 13; > > bool real = value.b; Wer an den Regeln vorbei schmutzige Dinge tut, muß damit rechnen, daß daß Ergebnis evtl. nicht wie erwartet funktioniert. Das war in C schon immer so, und da ist bool natürlich keine Ausnahme. Marwin schrieb: > Zur Union noch eine Randbemerkung: Dafuer, dass das unspecified > behaviour ist, wird es hier im Forum ziemlich oft empfohlen! Es ist nicht unspecified, sondern undefined. Das gibt es in ISO C beides, hat aber unterschiedliche Bedeutung. Ich weise übrigens jedesmal, wenn ich diese Empfehlung sehe, darauf hin, daß das nicht die ideale Methode ist. Natürlich ist ein Pointercast auch nicht wirklich portabler, aber ich sehe auch irgendwo nicht den Vorteil, erst noch extra einen neuen Datentyp definieren, dann eine zusätzliche Variable anlegen und in die dann den Wert reinschreiben und wieder rauslesen zu müssen. Das wäre mir schon viel zu umständlich. Ich finde es auch schlechter lesbar, als ein Pointercast. Sven P. schrieb: > Unspecified ist ja auch nicht negativ zu verstehen. Es ist vielmehr > einfach eine Feststellung seitens der ISO. Aus Sicht des Compilers sind > solche Dinge meistens nicht unspecified. Sonst müsste der Compiler recht > häufig einen Zufallsgenerator bemühen, darum ist der Compiler ja auch > dokumentiert. Das bezieht sich wohl auch eher auf undefined als unspecified behavior. Es ist aber nicht so, daß "undefined behavior" bedeutet, daß der Compiler dazu verpflichtet ist, ein unerwartetes Ergebnis zu produzieren, sondern daß er sich nicht darum kümmern muß, daß da irgendwas sinnvolles rauskommt und daß er z.B. bei Optimierungen gnadenlos davon ausgehen darf, daß der Programmierer sowas nicht macht. klugscheißer schrieb: > m ende ist es doch aber egal wenn die Spec eingehalten wird. > > Bei bool output gilt: > 0 ist false > 1 ist true > > Bei input gilt: > 0 ist false > alles andere ist true > > Code der sich durch true == >1 verwirren lässt ist entweder schlecht > oder extrem auf performance getrimmt und es daher nicht anders geht. Das gilt für integer-Typen, aber nicht für bool. Der Compiler darf davon ausgehen, daß bool niemals einen anderen Wert hat, da es ohne Umgehung der Regeln nicht möglich ist, in einen bool was anderes reinzuschreiben.
Datum:
Rolf Magnus schrieb: > Marwin schrieb: >> Zur Union noch eine Randbemerkung: Dafuer, dass das unspecified >> behaviour ist, wird es hier im Forum ziemlich oft empfohlen! > > Es ist nicht unspecified, sondern undefined. Das gibt es in ISO C > beides, hat aber unterschiedliche Bedeutung. Richtig, und der falsche Zugriff auf die Union ist unspecified. ISO/IEC 9899:TC2, Annex J.1 'Unspecified Behaviour', Strich 10. > Ich weise übrigens > jedesmal, wenn ich diese Empfehlung sehe, darauf hin, daß das nicht die > ideale Methode ist. Natürlich ist ein Pointercast auch nicht wirklich > portabler, aber ich sehe auch irgendwo nicht den Vorteil, erst noch > extra einen neuen Datentyp definieren, dann eine zusätzliche Variable > anlegen und in die dann den Wert reinschreiben und wieder rauslesen zu > müssen. Das wäre mir schon viel zu umständlich. Ich finde es auch > schlechter lesbar, als ein Pointercast. Das funktioniert aber nicht bei Bitfeldern. Diese Bitfelder mit dem avr-gcc waren eben diese Ausnhame, die ich erwähnte, die durchaus praktisch ist. Und sie ist wohldefiniert, wenn man die Anleitung des gcc gelesen hat.
Datum:
Sven P. schrieb: > Diese Bitfelder mit dem > avr-gcc waren eben diese Ausnhame, die ich erwähnte, die durchaus > praktisch ist. Und sie ist wohldefiniert, wenn man die Anleitung des gcc > gelesen hat. Lowlevel-Programmierung von Mikrocontrollern ist von Natur aus nicht portabel und oft ist man gezwungen, unportable Konstrukte zu verwenden, wie beispielsweise bei ISRs. Insofern geht es dabei manchmal eher um die Frage, ob der verwendete Compiler einem Sprachkonstrukt definierten Sinn einhaucht, als um formale Einhaltung des offiziellen Standards. Das ist natürlich kein Freibrief für Wildwuchs. Aber es entspricht der Philosophie von C: Es liegt in der Freiheit und Verantwortung des fähigen Programmierers, sich möglichst kein eigenes Grab zu schaufeln. Big Brother is not watching, man muss schon selber aufpassen.
Datum:
Sven P. schrieb: >> Es ist nicht unspecified, sondern undefined. Das gibt es in ISO C >> beides, hat aber unterschiedliche Bedeutung. > Richtig, und der falsche Zugriff auf die Union ist unspecified. > ISO/IEC 9899:TC2, Annex J.1 'Unspecified Behaviour', Strich 10. Hmm, ich war mir echt 100% sicher, gelesen zu haben, daß es undefined behavior ist, aber hab mich wohl geirrt. Mein Fehler. >> Ich weise übrigens >> jedesmal, wenn ich diese Empfehlung sehe, darauf hin, daß das nicht die >> ideale Methode ist. Natürlich ist ein Pointercast auch nicht wirklich >> portabler, aber ich sehe auch irgendwo nicht den Vorteil, erst noch >> extra einen neuen Datentyp definieren, dann eine zusätzliche Variable >> anlegen und in die dann den Wert reinschreiben und wieder rauslesen zu >> müssen. Das wäre mir schon viel zu umständlich. Ich finde es auch >> schlechter lesbar, als ein Pointercast. > Das funktioniert aber nicht bei Bitfeldern. Bitfelder nutze ich höchstens mal, um Speicher zu sparen, wenn ich viele sehr kleine Integers brauche, aber nie irgendwo, wo ich die in irgendwas anderes uminterpretieren müßte. Aber warum soll das denn eigentlich nicht bei Bitfeldern funktionieren? > Diese Bitfelder mit dem avr-gcc waren eben diese Ausnhame, die ich > erwähnte, die durchaus praktisch ist. Und sie ist wohldefiniert, wenn man > die Anleitung des gcc gelesen hat. ... und sich sicher ist, daß man den Code nie auf was anderem als gcc nutzen will.
Datum:
Karl Heinz Buchegger schrieb: > Marwin schrieb: > >> union strange >> { >> char c; >> bool b; >> }; >> >> main() >> { >> strange value; >> value.c = 13; >> >> bool real = value.b; > > Nehmen wir den einzigen massgeblichen Massstab zur Hand, dein > ISO-C-Standard, dann sagt der sehr klar: > Ab hier bist du im Nomansland der undefined behaviour. > Ab dieser Stelle ist jegliches Verhalten trivialerweise 'richtig', weil > nicht definiert ist, was eigentlich das einzig richtige Verhalten wäre. > > Das Verhalten von Unions ist nur dann definiert, wenn über denselben > Member gelesen wird, über den auch geschrieben wurde. Ist das nicht der > Fall -> undefined behaviour. GCC macht hier explizit eine Ausnahme: Man kann eine andere union-Komponente lesen als geschrieben und erhält das erwartete Ergebnis, siehe http://gcc.gnu.org/bugs/#nonbugs > Casting does not work as expected when optimization is turned on. > [...] > To fix the code above, you can use a union instead of a cast > (note that this is a GCC extension which might not work with > other compilers): Bitte reduzieren Sie die Anzahl der Zitatzeilen.
Datum:
Karl Heinz Buchegger schrieb: > In dem Moment, in dem du einen Pointer umcastest, ist die > Sprachdefinition am Ende mit ihren Zusagen. Nö, nicht komplett. Es gibt immerhin noch die Aliasing-Rules, nach denen zB ein char* (oder ein dazu kompatibler Pointer) ein Alias für alles sein kann. Ebenso kann A ein Alias für B sein wenn A und B kompatibel sind. Schon deutlich besser als "Eine mit den Zusagen" :-) In den Aliasing-Rules steht auch was von "Unions mit char", aber 100% klar wird mit das Spec-Kauderwelsch nicht...
Datum:
Rolf Magnus schrieb: > Hmm, ich war mir echt 100% sicher, gelesen zu haben, daß es undefined > behavior ist, aber hab mich wohl geirrt. Mein Fehler. Es läuft ja auch auf dasselbe hinaus. Verschieden ist lediglich, ob der Standard mögliche Alternativen vorgibt oder nicht. > Bitfelder nutze ich höchstens mal, um Speicher zu sparen, wenn ich viele > sehr kleine Integers brauche, aber nie irgendwo, wo ich die in irgendwas > anderes uminterpretieren müßte. Aber warum soll das denn eigentlich > nicht bei Bitfeldern funktionieren? Weil man Bitfelder nicht mit Zeigern adressieren kann. Die Variante mit Bitfeld macht es zum Beispiel möglich, einzelne Bits direkt anzusprechen, ohne dazu Bitpfriemelei zu betreiben. Manche Prozessoren haben bitadressierbaren Speicher, da gibts dann halt ein äquivalentes Compilerkonstrukt dafür. Beim avr-gcc mache ich das etwa so:
/* Bitwise access to char ports */ struct __attribute__ ((packed)) _bits_char_t { unsigned bit0: 1; unsigned bit1: 1; unsigned bit2: 1; unsigned bit3: 1; unsigned bit4: 1; unsigned bit5: 1; unsigned bit6: 1; unsigned bit7: 1; }; #define _BITWISE_CHAR(x, b) \ ( ((volatile struct _bits_char_t *) &(x))->bit ## b ) #define BITWISE_CHAR(x, b) \ _BITWISE_CHAR((x), b) #define LED BITWISE_CHAR(PORTD, 1) LED = 1; if (LED) ...; |
>> Diese Bitfelder mit dem avr-gcc waren eben diese Ausnhame, die ich >> erwähnte, die durchaus praktisch ist. Und sie ist wohldefiniert, wenn man >> die Anleitung des gcc gelesen hat. > > ... und sich sicher ist, daß man den Code nie auf was anderem als gcc > nutzen will. Joa, ganz generisch ginge das in dieser Form wohl nur mit Templates. Ansonsten hoffe ich, dass ein alternativer Compiler eine Konstruktion unterstützt, die ich in diesen Makros unterbringen kann :-)
Datum:
Sven P. schrieb: >> Aber warum soll das denn eigentlich nicht bei Bitfeldern funktionieren? > Weil man Bitfelder nicht mit Zeigern adressieren kann. Die einzelnen Elemente natürlich nicht, aber es geht ja eher um genau das, was du in deinem Beispiel machst - auf die einzelnen Bits eines uint8_t über einen Zeiger auf ein Bitfeld zugreifen. >>> Diese Bitfelder mit dem avr-gcc waren eben diese Ausnhame, die ich >>> erwähnte, die durchaus praktisch ist. Und sie ist wohldefiniert, wenn man >>> die Anleitung des gcc gelesen hat. >> >> ... und sich sicher ist, daß man den Code nie auf was anderem als gcc >> nutzen will. > Joa, ganz generisch ginge das in dieser Form wohl nur mit Templates. > Ansonsten hoffe ich, dass ein alternativer Compiler eine Konstruktion > unterstützt, die ich in diesen Makros unterbringen kann :-) Letzendlich bleibt ja auch noch die memcpy()-Variante, die von den unportablen Möglichkeiten immer noch die sauberste ist.