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
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.
... schrieb:> x = 2 + (i == 4 ? 1 : 0);
Das finde ich jetzt aber auch nicht unbedingt lesbarer. Dann lieber
gleich:
1
if(i==4)
2
x=3;
3
else
4
x=2;
oder
1
x=2;
2
if(i==4)
3
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:
1
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?
1
printf("Das Licht ist %s\n",licht==AN?"an":"aus");
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.
1
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
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.
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.
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
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".
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 sehr vielen Programmierern Fragen über Typ und Wert-Definition im
C-Standard auftauchen. Genau deshalb ist die Variante
1
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
1
if (i == 4)
2
x = 3;
3
else
4
x = 2;
auch nicht die Lesbarkeit fördert. Aber das hängt wohl vom persönlichen
Geschmack ab.
_\|/_ 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."
> 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>
1
> x = 2 + ( i == 4 ? 1 : 0);
2
>
> 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
1
UpdateStatus(status==STAT_ERROR?"Fehler":"OK",
2
msg==NO_MSG?"":msgCode,
3
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.
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.
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
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
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.
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
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
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
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.
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_der_C-Programmierung#TRUE_ist_nicht_1
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.
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.
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
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.
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...
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:
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.
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:
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.
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.
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.
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);
}
}
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:
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:
1
value.c=13;
2
3
boolreal=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.
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.
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.
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!
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 :-)
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.
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.
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.
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
1
inta[sizeof(double)/sizeof(int)];
2
doubleb=5.0;
3
4
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.
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.
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.
> 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.
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.
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.
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.
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.
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...
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:
1
/* Bitwise access to char ports */
2
struct__attribute__((packed))_bits_char_t{
3
unsignedbit0:1;
4
unsignedbit1:1;
5
unsignedbit2:1;
6
unsignedbit3:1;
7
unsignedbit4:1;
8
unsignedbit5:1;
9
unsignedbit6:1;
10
unsignedbit7:1;
11
};
12
13
#define _BITWISE_CHAR(x, b) \
14
( ((volatile struct _bits_char_t *) &(x))->bit ## b )
15
16
#define BITWISE_CHAR(x, b) \
17
_BITWISE_CHAR((x), b)
18
19
20
#define LED BITWISE_CHAR(PORTD, 1)
21
22
23
LED=1;
24
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 :-)
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.