Forum: Compiler & IDEs True =1 false =0 kann man damit rechnen?


von Rudi D. (rulixa)


Lesenswert?

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

von sebastian (Gast)


Lesenswert?

Ja, ist erlaubt.

von ... (Gast)


Lesenswert?

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.
1
x = 2 + (i == 4 ? 1 : 0);

von Rudi D. (rulixa)


Lesenswert?

... schrieb:
 Ich würde dir empfehlen, den "?"
> Operator zu verwenden.
>
>
1
> x = 2 + (i == 4 ? 1 : 0);
2
>

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

von Rolf M. (rmagnus)


Lesenswert?

... 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");

von Rudi D. (rulixa)


Lesenswert?

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

von Adib T. (adib_t)


Lesenswert?

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.

von Stefan E. (sternst)


Lesenswert?

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.

von float (Gast)


Lesenswert?

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

von Peter II (Gast)


Lesenswert?

float schrieb:
> Das möchte ich bezweifeln, erstere ist nämlich vom typ bool

in (alten) c gibt es aber gar kein bool

von Stefan E. (sternst)


Lesenswert?

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".

von was? (Gast)


Lesenswert?

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

von Mark B. (markbrandis)


Lesenswert?

Könnte es nicht auch Java oder Objective-C sein ;-)

von _\|/_ (Gast)


Lesenswert?

Fakt ist doch, das bei der Variante
1
x = 2 + ( i == 4 );
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.

von Rolf Magnus (Gast)


Lesenswert?

_\|/_ 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."

von Karl H. (kbuchegg)


Lesenswert?

_\|/_ schrieb:
> Fakt ist doch, das bei der Variante
>
1
> x = 2 + ( i == 4 );
2
>
> 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.

von Rolf Magnus (Gast)


Lesenswert?

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.

von Karl H. (kbuchegg)


Lesenswert?

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
1
  if( ( i > j ) == TRUE )

von Rudi D. (rulixa)


Lesenswert?

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

von Karl H. (kbuchegg)


Lesenswert?

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.

von Rudi D. (rulixa)


Lesenswert?

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

von Andreas B. (andreas_b77)


Lesenswert?

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

von Rudi D. (rulixa)


Lesenswert?

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

von Karl H. (kbuchegg)


Lesenswert?

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.

von Adib T. (adib_t)


Lesenswert?

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.

von Klaus W. (mfgkw)


Lesenswert?

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.

von Anja (Gast)


Lesenswert?

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

von Rolf M. (rmagnus)


Lesenswert?

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.

von Marwin (Gast)


Lesenswert?

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...

von Andreas B. (andreas_b77)


Lesenswert?

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:
1
#include <stdbool.h>
2
3
bool f(void)
4
{
5
  bool v = 5;
6
7
  return v;
8
}

Bekommen:
1
00000000 <f>:
2
   0:  81 e0         ldi  r24, 0x01  ; 1
3
   2:  08 95         ret

Gestaunt: Der hat ja unrecht, der Marwin.

von (prx) A. K. (prx)


Lesenswert?

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.

von Peter II (Gast)


Lesenswert?

Andreas B. schrieb:
> #include <stdbool.h>
>
> bool f(void)
> {
>   bool v = 5;
>
>   return v;
> }

das ist zu einfach, das durchschaut der compiler
1
 #include <stdbool.h>
2
 
3
 bool f(void)
4
{
5
  uint8_t v = 5;
6
7
  return (bool)v;
8
}

könnte besser sein.

von Andreas B. (andreas_b77)


Lesenswert?

Nö.
1
#include <stdbool.h>
2
#include <stdint.h>
3
4
bool f(void)
5
{
6
  uint8_t v = 5;
7
8
  return v;
9
}
1
00000000 <f>:
2
   0:  81 e0         ldi  r24, 0x01  ; 1
3
   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:
1
#include <stdbool.h>
2
#include <stdint.h>
3
4
bool f(uint8_t v)
5
{
6
  return v;
7
}
1
00000000 <f>:
2
   0:  91 e0         ldi  r25, 0x01  ; 1
3
   2:  88 23         and  r24, r24
4
   4:  01 f4         brne  .+0        ; 0x6 <f+0x6>
5
   6:  90 e0         ldi  r25, 0x00  ; 0
6
   8:  89 2f         mov  r24, r25
7
   a:  08 95         ret

von Sven P. (Gast)


Lesenswert?

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.

von Karl H. (kbuchegg)


Lesenswert?

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.

von (prx) A. K. (prx)


Lesenswert?

A. K. schrieb:

> So beispielsweise bei IBM Power: sizeof(_Bool)==4.

PS: abhängig von der GCC-Version...

von Noname (Gast)


Lesenswert?

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.

von Marwin (Gast)


Lesenswert?

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);
  }
}

von Sven P. (Gast)


Lesenswert?

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:
1
int a = 0x0F;
2
int b = 0xF0;
3
4
if (a) puts("a ist wahr");
5
if (b) puts("b ist wahr");
6
if (a & b) puts("tja.");

von Yalu X. (yalu) (Moderator)


Lesenswert?

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
  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.

von Karl H. (kbuchegg)


Lesenswert?

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.

von Sven P. (Gast)


Lesenswert?

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.

von Marwin (Gast)


Lesenswert?

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!

von Sven P. (Gast)


Lesenswert?

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 :-)

von Yalu X. (yalu) (Moderator)


Lesenswert?

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.

von Karl H. (kbuchegg)


Lesenswert?

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.

von Peter II (Gast)


Lesenswert?

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.

von Karl H. (kbuchegg)


Lesenswert?

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
  int a[sizeof(double)/sizeof(int)];
2
  double b = 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.

von klugscheißer (Gast)


Lesenswert?

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.

von Sven P. (Gast)


Lesenswert?

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.

von Rolf M. (rmagnus)


Lesenswert?

>   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.

von Sven P. (Gast)


Lesenswert?

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.

von (prx) A. K. (prx)


Lesenswert?

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.

von Rolf M. (rmagnus)


Lesenswert?

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.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

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.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

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...

von Sven P. (Gast)


Lesenswert?

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
  unsigned bit0: 1;
4
  unsigned bit1: 1;
5
  unsigned bit2: 1;
6
  unsigned bit3: 1;
7
  unsigned bit4: 1;
8
  unsigned bit5: 1;
9
  unsigned bit6: 1;
10
  unsigned bit7: 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 :-)

von Rolf Magnus (Gast)


Lesenswert?

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.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.