Forum: PC-Programmierung C (MinGW): Unerwartete Bool-Auswertung


von Walter T. (nicolas)


Lesenswert?

Hallo zusammen,

ich habe gerade einen Quelltext, der Teil eines größeren Projekts ist. 
Der Schnipsel, dessen Ergebnis mich erstaunt, ist:
1
    bool r = This->Main.softLimitsRequest;
2
    bool s = This->Ctrl.softLimitsEnabled;
3
4
    int8_t a = ( (r != 0)<<2 ) | ( (s != 0)<<1 ) | 1;
5
    printf("\n0x%x", a);

Die Konsolenausgabe lautet für den ARM-GCC erwartungsgemäß, je nach 
Füllung der beiden Variaben r und s 0x1, 0x3, 0x5 usw. Beim MinGW lautet 
die Konsolenausgabe 0x9, 0xd, 0xf. Ich versuche gerade, die Logik 
dahinter zu verstehen. Welche Regel erlaubt dem Compiler, das 
unerwartete Ergebnis zu erzeugen?

P.S.: Ich kann der Ergebnis nur mit einer Zuweisung von r und s aus dem 
struct reproduzieren. Mit einem Literal kommt immer das Erwartete 
heraus.

P.P.S.: Mit dem "klassischen" Idiom
1
    int8_t a = ( (!!r)<<2 ) | ( (!!s)<<1 ) | 1;
kommt das gleiche unerwartete Ergebnis reproduzierbar heraus.

:
von Frank K. (fchk)


Lesenswert?

Das Problem ist, dass Du Dich daraf verlässt, dass true nach 1 
evaluiert. Das ist offensichtlich in dem struct nicht der Fall.

Mach doch
1
int8_t a = ((r) ? 4 : 0) | ( ((s) ? 2 : 0) | 1;

Das ist sicherer.

fchk

von Oliver S. (oliverso)


Lesenswert?

Abgesehen davon, daß %x einen unsigned-Wert erwartet, und der sicherlich 
akademisch interessanten Frage, warum der Effekt auftritt, bleibt vor 
allem die entscheidende Frage: Warum in aller Welt schreibt man solchen 
Code? Bist du im Nationalteam zur obfuscated-C-Olympiade?

Oliver

von nfet (Gast)


Lesenswert?

Frank K. schrieb:
> Das Problem ist, dass Du Dich daraf verlässt, dass true nach 1
> evaluiert.

Wenn es mit einem standardkonformen C99 Compiler kompiliert wird, dann 
darf er das auch.

von mh (Gast)


Lesenswert?

Wie wäre es mit einem minimalen, compilierbaren Beispiel, das den Fehler 
enthält?

von Walter T. (nicolas)


Lesenswert?

mh schrieb:
> Wie wäre es mit einem minimalen, compilierbaren Beispiel, das den Fehler
> enthält?

Ich denke, es wäre sehr schön. Leider gelingt es mir nicht, das 
Verhalten vom konkreten Projekt zu lösen. Ich kann es in eine isolierte 
Funktion bringen, aber sobald diese in einer anderen Quelltext-Datei 
steht, ist das Verhalten nicht mehr das oben gezeigte.

Beitrag #5973530 wurde von einem Moderator gelöscht.
von mh (Gast)


Lesenswert?

Walter T. schrieb:
> mh schrieb:
>> Wie wäre es mit einem minimalen, compilierbaren Beispiel, das den Fehler
>> enthält?
>
> Ich denke, es wäre sehr schön. Leider gelingt es mir nicht, das
> Verhalten vom konkreten Projekt zu lösen. Ich kann es in eine isolierte
> Funktion bringen, aber sobald diese in einer anderen Quelltext-Datei
> steht, ist das Verhalten nicht mehr das oben gezeigte.

Wie sollen wir dir dann helfen? Aber dann ist das Problem wohl das 
restliche Projekt?!?

von Walter T. (nicolas)


Lesenswert?

mh schrieb:
> Wie sollen wir dir dann helfen?

Schritt 1: Ist das beobachtete Verhalten wirklich ein Fehler, oder nur 
eine sehr spitzfindige Auslegung des C-Standards? Um das festzustellen, 
sollte der sichtbare Schnipsel ausreichen.


Ist es ein Fehler, werde ich wohl mehr Mühe darauf verwenden müssen, 
entweder den Fehler im Bugtracker zu finden, und/oder isolierbares 
Beispiel zu bauen. Der Workaround für meinen Zweck ist schon fertig.

Ist es "nur" eine unerwartete Interpretation, besteht der nächste 
Schritt wohl in der Entwicklung einer sauberen, noch kugelsicherren 
Variante.

Beitrag #5973551 wurde von einem Moderator gelöscht.
von Walter T. (nicolas)


Lesenswert?

Nachtrag: Ich weiß natürlich, welche Grundregel der obengenannte 
Quelltext verletzt. Das ändert aber nichts an der Frage nach der 
Korrektheit.

von mh (Gast)


Lesenswert?

Walter T. schrieb:
> Ist das beobachtete Verhalten wirklich ein Fehler,
Wenn die Werte/Typen in Ordnung sind, mit denen r und s initialisiert 
werden, ist der Schnipsel so in Ordnung.

Walter T. schrieb:
> oder nur eine sehr spitzfindige Auslegung des C-Standards?
Hast du selbst mal in den Standard geschaut? Die Teile, die hier 
relevant sind, beziehen sich fast nur auf "Conversions" und 
"Expressions" und sind relativ schnell  überfliegbar.

Walter T. schrieb:
> Um das festzustellen, sollte der sichtbare Schnipsel ausreichen.
Die Antwort ist dann aber auch nur für schnipselspezifische Bedingungen 
gültig.

von Walter T. (nicolas)


Lesenswert?

Nachtrag2: Der Schnipsel läßt behält auch in anderen Quelltext-Dateien 
sein beobachtetes Verhalten. Den Wechsel zum erwarteten Verhalten zeigt 
er erst dann, wenn stdbool.h nicht mehr im include tree ist.

von mh (Gast)


Lesenswert?

Walter T. schrieb:
> Nachtrag2: Der Schnipsel läßt behält auch in anderen Quelltext-Dateien
> sein beobachtetes Verhalten. Den Wechsel zum erwarteten Verhalten zeigt
> er erst dann, wenn stdbool.h nicht mehr im include tree ist.

Und wie soll Quelltext, der bool enthält, compilieren, wenn stdbool.h 
nicht includiert wird?

von nfet (Gast)


Lesenswert?

mh schrieb:
> Und wie soll Quelltext, der bool enthält, compilieren, wenn stdbool.h
> nicht includiert wird?

Da hätten wir das Problem wohl gefunden. Vielleicht hilft es auch, wenn 
der Compiler Aufruf gezeigt wird inklusive Version des Compilers.

von Brausebär (Gast)


Lesenswert?

Ich kann dein Ergebnis mit dem GCC ("gcc (Debian 8.3.0-6) 8.3.0") 
nicht nachvollziehen (Minimalbeispiel):
1
#include <stdint.h>
2
#include <stdio.h>
3
#include <stdbool.h>
4
5
6
volatile bool r;
7
volatile bool s;
8
9
int main()
10
{
11
    int x;
12
    
13
    scanf("%d", &x);
14
    r = (x != 0) ? true : false;
15
    scanf("%d", &x);
16
    s = (x != 0) ? true : false;
17
    
18
    int8_t a = ( (r != 0)<<2 ) | ( (s != 0)<<1 ) | 1;
19
    printf("0x%x\n", a);
20
    
21
    return 0;
22
}

Wenn in der restlichen Software mit ähnlichen "Tricks" gesegnet ist, 
darf ich mal vermutend unterstellen, dass s und r nicht korrekte 
boolsche Werte (1,0) enthalten.

von loeti2 (Gast)


Lesenswert?

Hatte ich auch schon mal mit C++ (PC, MS Visual Studio):

Bool-Werte von binärem Stream gelesen, in der Speicherzelle stand ein 
Wert ungleich 0/1 und der doofe Debugger hat alles ungleich 0 als true 
angezeigt.

If/Else ging, aber Verknüpfungen wie &&, || lieferten Unsinn.

Hat ne Weile gedauert bis ich es gefunden habe, natürlich zuerst an 
einen Compilerbug geglaubt ;-)

von Dr. Sommer (Gast)


Lesenswert?

Frank K. schrieb:
> Das Problem ist, dass Du Dich daraf verlässt, dass true nach 1
> evaluiert.

Der Vollständigkeit halber: In C++ ist das so. Wenn ein bool nach int 
konvertiert wird - was durch den Vergleich mit 0 forciert wird - kommt 
immer 1 oder 0 raus. Das (r != 0) ist in C++ sinnlos redundant und das 
gleiche wie "r".

Es klingt jedenfalls stark danach als würde im struct etwas verkehrtes 
stehen und die Annahmen des Compilers, dass da nur 1/0 stehen kann, 
verletzt werden.

von Arc N. (arc)


Lesenswert?

Dr. Sommer schrieb:
> Frank K. schrieb:
>> Das Problem ist, dass Du Dich daraf verlässt, dass true nach 1
>> evaluiert.
>
> Der Vollständigkeit halber: In C++ ist das so. Wenn ein bool nach int
> konvertiert wird - was durch den Vergleich mit 0 forciert wird - kommt
> immer 1 oder 0 raus. Das (r != 0) ist in C++ sinnlos redundant und das
> gleiche wie "r".
>
> Es klingt jedenfalls stark danach als würde im struct etwas verkehrtes
> stehen und die Annahmen des Compilers, dass da nur 1/0 stehen kann,
> verletzt werden.

Nur darf der Compiler das nicht annehmen, sondern sollte sich an den 
Standard halten...
C99 6.5.9 3 "The == (equal to) and != (not equal to) operators are 
analogous to the relational operators except for their lower 
precedence.93) Each of the operators yields 1 if the specified relation 
is true and 0 if it is false. The result has type int."
C++2017 8.5.9 6 "... each of the operators shall yield true if the 
specified relationship is true and false if it is false". Hinzukommen 
dann die "Shift Operators" 8.5.7, die die "Integral Promotions" 
vorschreiben und in 7.6 definiert sind. 7.6 6 "A prvalue of type bool 
can be converted to a prvalue of type int, with false becoming zero and 
true becoming one".

http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/n4659.pdf

von Theor (Gast)


Lesenswert?

@ Walter

Welches sind denn nun die Versionen des verwendeten AVR-GCC und des 
MinGW? Ist ha schon danach gefragt worden, aber bisher keine Antwort.

Und gibt es denn keinerlei Warnungen beim Complieren? Sicherheitshalber 
mal gucken, ob alle Warnungen eingeschaltet sind. Beim GCC sollte das 
-wall sein. Beim MinGW weiß ich es nicht.

Hast Du gemeint, dass im Quellcode nicht mehr "#include <stdbool.h> 
steht, als Du sagetest, es sei nicht mehr "im include tree ist"? Wie 
genau hast Du stdbool.h entfernt?

Das der Code funktioniert, falls stdbool nicht mehr includes wird (falls 
das so zu verstehen ist) ist ein sehr merkwürdiges Phänomen, meine ich. 
Naja. Was mir so alles merkwürdig vorkommt... :-)

Erst mal gucken, was die Versionen sind und ob es Warnungen gibt und wie 
das mit der H-Datei nun genau lief.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Walter T. schrieb:
> Schritt 1: Ist das beobachtete Verhalten wirklich ein Fehler

Ich würde es erstmal für einen solchen halten.

Meiner Meinung nach ist sogar bereits der Test der bools auf != 0 
unnötig, denn für den Typ bool ist garantiert, dass er nur die Werte 0 
und 1 haben kann. Das müsste ich allerdings bei Interesse selbst nochmal 
gegenlesen.

mh schrieb:
> Und wie soll Quelltext, der bool enthält, compilieren, wenn stdbool.h
> nicht includiert wird?

Das frage ich mich allerdings auch … man könnte natürlich noch _Bool 
statt bool benutzen, dann braucht man den Header nicht. Da _Bool im 
implementation namespace liegt, ließ sich dieser Typ in C99 konfliktfrei 
allgemein hinzufügen, während bool eigentlich im application namespace 
ist, und daher nur offenbart wird, wenn man <stdbool.h> inkludiert.

von Theor (Gast)


Lesenswert?

Hm. Dreht es sich nicht vielmeher um die Frage, ob das Ergebnis eines 
Vergleichs "!=", "==" etc, grundsätzlich entweder 0 oder nicht 0 ist?

Falls ja, wäre es doch egal, ob bool nun 0 und 1 oder 0 und >0 ist oder 
nicht.

Das Probelm ist ja anscheinend dass MinGW bei "!=", "==" usw. für true 
eben auch Werte über 1 als Ergebnis verwendet.


Oder verstehe ich das Problem gerade nicht?

von Theor (Gast)


Lesenswert?

Ooops. Zu wenig Kaffe. :-)

Es sollte heissen:

Dreht es sich nicht vielmeher um die Frage, ob das Ergebnis eines
Vergleichs "!=", "==" etc, grundsätzlich entweder 0 oder 1 oder 0 oder 
nicht 0 ist?

Es ist doch egal, ob bool nun 0 und 1 oder 0 und >0 ist oder
nicht. Hauptsache es lässt sich in integer umwandeln.

Das Problem ist ja anscheinend dass MinGW bei "!=", "==" usw. für true
eben auch Werte über 1 als Ergebnis verwendet.


Oder verstehe ich das Problem gerade nicht?

von Theor (Gast)


Lesenswert?

Au weia. Doch schon sehr spät.

Ich hoffe man kann ahnen, was ich meine, aber ich will Euch nicht mit 
noch mehr Korrekturen auf die Nerven gehen und lese heute nur noch mit.

Sorry.

von rbx (Gast)


Lesenswert?

Walter T. schrieb:
> Ist es ein Fehler

Glaube ich eher nicht, s.o.
Die Zahlen dürfen oder sollen nicht negativ werden, diesen Anspruch muss 
man irgendwo im Code wiederfinden.
Welche Genauigkeit tatsächlich benötigt wird, kann man nur im 
Gesamtzusammenhang/Lebensraum erkennen.

von Yalu X. (yalu) (Moderator)


Lesenswert?

Walter T. schrieb:
>     bool r = This->Main.softLimitsRequest;
>     bool s = This->Ctrl.softLimitsEnabled;
>
>     int8_t a = ( (r != 0)<<2 ) | ( (s != 0)<<1 ) | 1;
>     printf("\n0x%x", a);

Stehen diese Zeilen wirklich direkt hintereinander?

Oder stehen die ersten beiden und die letzten beiden jeweils in
verschiedenen Übersetzungseinheiten?

Walter T. schrieb:
> Den Wechsel zum erwarteten Verhalten zeigt
> er erst dann, wenn stdbool.h nicht mehr im include tree ist.

Evtl. gibt es in deiner Software zwei verschiedene bool-Typen: Den aus
stdbool.h und zusätzlich einen, der mit

1
#define bool char

o.ä. definiert wurde.

Sind die Variablen r und s bei ihrer Initialisierung oder Zuweisung vom
Typ char, wird der zugewiesene Wert nicht auf 0/1 reduziert.

Sind sie hingegen bei der Abfrage r!=0 und s!=0 (in einer anderen
Übersetzungseinheit) vom Typ bool aus stdbool.h, könnte der Compiler den
Vergleich mit 0 wegoptimieren, da dieser bei ordnungsgemäßer Verwendung
des bool-Typs ohne Wirkung ist. Gleiches gilt für die Verwendung von !!.

Beides zusammen führt dazu, dass im Ausdruck ((r!=0)<<2)|((s!=0)<<1)|1
die Teilausdrücke r!=0 und s!= von 0 und 1 verschiedene Werte haben
können, was das von dir beobachtete unerwartete Ergebnis liefert.

Was wird denn mit

1
  printf("r=%d s=%d\n", r, s);

ausgegeben?

PS: Vielleicht hast du in deinem Programm auch ganz einfach irgendwo
einen Array-Überlauf o.ä., der r und s mit nicht bool-konformen Werten
überschreibt.

: Bearbeitet durch Moderator
von Walter T. (nicolas)


Lesenswert?

Guten Morgen,

ich habe den Fehler gefunden. Es war ein grober Fehler meinerseits. An 
irgendeiner, völlig anderen Stelle, wird eine boolesche Variable (nennen 
wir sie b0) innerhalb eines Structs nicht initialisiert. Warum das keine 
Warnung gegeben hat, muß ich noch suchen. Diese Variable hat beim Start 
den Wert 2. Im Debugger leider nicht zu sehen, aber ein printf() gibt 
bei b0 0x2 und bei !b0 0x3 aus.

Die Variable This->Main.softLimitsRequest aus dem obigen Schnipsel 
entsteht aus logischen Verknüpfungen, in denen eine Quelle b0 ist. Alle 
Zuweisungen in andere boolesche Variablen, logische Verknüpfungen und 
das !!x-Idiom hindern nicht die Fortpflanzung des unerlaubten Werts.

Für mich bedeutet das:
 1. Nein, es ist kein Kompilerfehler (deswegen ist die Frage nach der 
Version wohl hinfällig). Nur eine fehlende Warnung, trotz -Wall, 
-Wpedantic. Bei einem unitialisierten Wert hat der Compiler leider das 
absolute Recht, jeden Blödsinn zu machen.

 2. Die Erkenntnis, daß die Guards der Form !!x und x!=0 bei booleschen 
Variablen hinfällig sind. Sie werden ohnehin wegoptimiert 
(Optimierungsstufe ist -O1).

 3. Ich brauche einen neuen Weg, die derart weitschweifige Fortpflanzung 
eines solchen Fehlers künftig zu verhindern. Static assertions werden 
vermutlich genauso wirkungslos sein. Ob normale Assertions nützen, oder 
einfach wegoptimiert werden, muß ich im Laufe des Tages mal 
ausprobieren.

Danke für die Diskussion!




P.S.:

Yalu X. schrieb:
> Stehen diese Zeilen wirklich direkt hintereinander?

Ja, der Schnipsel ist unmittelbar über die Zwischenablage kopiert.

: Bearbeitet durch User
von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Walter T. schrieb:
> An irgendeiner, völlig anderen Stelle, wird eine boolesche Variable
> (nennen wir sie b0) innerhalb eines Structs nicht initialisiert.

Puh, dass das so eine böse Auswirkung hat, ist natürlich interessant.

Andererseits, gut zu wissen, dass es eben kein Compilerfehler ist – und 
eigentlich auch gut zu wissen, dass der Compiler selbst bei solchen 
„Angst-Pessimierungen“ wie “<bool> != 0 ” oder “!!<bool>” keinen 
pessimierten Code generiert.

> Warum
> das keine Warnung gegeben hat, muß ich noch suchen.

Das wäre allerdings wirklich interessant.

Beitrag #5973935 wurde von einem Moderator gelöscht.
von Bernd K. (prof7bit)


Lesenswert?

Jörg W. schrieb:
> auch gut zu wissen, dass der Compiler selbst bei solchen
> „Angst-Pessimierungen“ wie “<bool> != 0 ” oder “!!<bool>” keinen
> pessimierten Code generiert.

Was ist dann aber wenn jemand folgenden Ausdruck schreibt:

kaputte_boolvariable ? 1 : 0

Wird er das auch optimistisch wegoptimieren?

: Bearbeitet durch User
von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Bernd K. schrieb:
> Wird er das auch optimistisch wegoptimieren?

Warum denn nicht? Ist doch letztlich nichts anderes als die anderen 
Ausdrücke.

von Bernd K. (prof7bit)


Lesenswert?

Jörg W. schrieb:
> Bernd K. schrieb:
>> Wird er das auch optimistisch wegoptimieren?
>
> Warum denn nicht? Ist doch letztlich nichts anderes als die anderen
> Ausdrücke.

Na dann sieht man aber alt aus wenn man wirklich mal solche Daten in 
einer bool-Variablen bekommt. Egal was ich mache, der Compiler könnte 
sich auf jederzeit den Standpunkt stellen daß das was da als bool 
hereinkam eh nur 1 oder 0 sein kann und optimiert alles weg was das 
hätte sicherstellen können.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Bernd K. schrieb:
> Na dann sieht man aber alt aus wenn man wirklich mal solche Daten in
> einer bool-Variablen bekommt.

Ja, natürlich.

Man kann das aber nicht ohne weiteres „bekommen“. Neben dem hier 
gefundenen Fall einer nicht initialisierten Variablen könnte es maximal 
über einen Typecast da hin gekommen sein. Typecasts sollte man natürlich 
immer nur mit der notwendigen Vorsicht und möglichst selten benutzen.

Ansonsten ist es ja gerade der Sinn dieses Datentyps, dass er nur die 
Werte 0 und 1 annehmen kann.

von A. S. (Gast)


Lesenswert?

Walter T. schrieb:
> 2. Die Erkenntnis, daß die Guards der Form !!x und x!=0 bei booleschen
> Variablen hinfällig sind. Sie werden ohnehin wegoptimiert

Ist das so? Ist !!a nicht immer 0 oder 1? Wann genau?
In C oder C++?
Wenn a bool ist, oder auch int?

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

A. S. schrieb:
> Ist das so?

Ja, aber du hast es erstens völlig aus dem Zusammenhang gerissen und 
zweitens nicht richtig gelesen. Gehe also einfach nochmal zurück auf 
"Los!", und starte von vorn mit dem Thread.

von mh (Gast)


Lesenswert?

Bernd K. schrieb:
> Na dann sieht man aber alt aus wenn man wirklich mal solche Daten in
> einer bool-Variablen bekommt.

Vielleicht ist jeder dieser Wege undefined behaviour?

von nfet (Gast)


Lesenswert?

A. S. schrieb:
> Ist das so? Ist !!a nicht immer 0 oder 1?

In einem korrekten Programm schon. Aber bei undefined behavior kann eben 
alles passieren. Das Lesen einer nicht initialisierten Variablen ist 
undefined behavior.

von Bernd K. (prof7bit)


Lesenswert?

Jörg W. schrieb:
> Bernd K. schrieb:
>> Na dann sieht man aber alt aus wenn man wirklich mal solche Daten in
>> einer bool-Variablen bekommt.
>
> Ja, natürlich.
>
> Man kann das aber nicht ohne weiteres „bekommen“.

Naja, ich denke so solche Fälle wie ich schreibe Code der gegen anderen 
Code gelinkt wird und irgendwo bekomme ich von dort ein verschmutztes 
bool übergeben, als bool deklariert. Dann liegt es völlig außerhalb 
meiner Macht aber es wird so aussehen als ob mein eigener Code irgendwo 
tief im innern irgendwo Mist baut, das ist das Meterial aus dem 
Debugging-Albträume gemacht sind.

von Walter T. (nicolas)


Lesenswert?

Jörg W. schrieb:
> Neben dem hier
> gefundenen Fall einer nicht initialisierten Variablen könnte es maximal
> über einen Typecast da hin gekommen sei

Kennst Du eine Variante, wie man das provozieren kann, ohne daß der 
Compiler das mitbekommt? Könnte man den Fehler selbst triggern, wäre es 
auch kein Problem, Gegenmaßnahmen zu testen.

(Ich habe es noch nicht probiert, da gerade am falschen Rechner - 
vielleicht geht der naheliegende Weg mit Zeigern oder unions.)

: Bearbeitet durch User
von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Bernd K. schrieb:
> Naja, ich denke so solche Fälle wie ich schreibe Code der gegen anderen
> Code gelinkt wird und irgendwo bekomme ich von dort ein verschmutztes
> bool übergeben, als bool deklariert.

Wenn der andere Code so schlampig ist, dann kannst du doch auch 
sämtliche anderen Fälle von undefined behaviour nicht ausschließen.

Wenn du das wirklich fürchten musst, dann sollte es eben zu dem 
Fremdcode gar kein "bool" als Übergabewert geben. Ich würden in solchen 
Fällen allerdings den Fremdcode komplett in Frage stellen.

Walter T. schrieb:
> Kennst Du eine Variante, wie man das provozieren kann

Nö.

Walter T. schrieb:
> vielleicht geht der naheliegende Weg mit Zeigern oder unions

Zeiger würden ja in die Typecast-Geschichte reinlaufen. Ja, damit kann 
man sicher x-beliebige Varianten von undefined behaviour provozieren.

: Bearbeitet durch Moderator
von Bartholomäus (Gast)


Lesenswert?

Walter T. schrieb:
> Kennst Du eine Variante, wie man das provozieren kann, ohne daß der
> Compiler das mitbekommt? Könnte man den Fehler selbst triggern, wäre es
> auch kein Problem, Gegenmaßnahmen zu testen.

nicht ausprobiert, könnte mir aber std::memcpy bzw. memcpy mit int->bool 
vorstellen.

von Peter D. (peda)


Lesenswert?

Im Zweifel hat der Compiler immer recht.
Der Compiler ist ja nicht blöd. Jede Zuweisung zu bool wandelt er in 0 
oder 1 um. Daher darf er bei der Auswertung davon ausgehen, daß diese 
immer nur 0 oder 1 sind. Aus seiner Sicht überflüssige Konvertierungen 
schmeißt er alle weg.
Wenn Du ihm von hinten durch die Brust ins Auge abweichende Werte 
reinschreibst, kann der Compiler doch nichts dafür.
Der Compiler ist sogar recht intelligent. In der Regel werden Variablen 
öfter gelesen, als geschrieben. Daher ist die Konvertierung bei jedem 
Schreibzugriff effizienter, als bei jedem Lesezugriff. Und bei jedem 
Schreiben zur Compilezeit sowieso.
Hier mal mit dem AVR-GCC:
1
bool b, b1;
2
uint8_t u;
3
4
bool faa(void)
5
{
6
  b = 8;                                // wird zu 1 (true)
7
  92:  81 e0         ldi  r24, 0x01  ; 1
8
  94:  80 93 e0 00   sts  0x00E0, r24
9
  98:  80 91 e2 00   lds  r24, 0x00E2
10
  9c:  81 11         cpse  r24, r1
11
  9e:  81 e0         ldi  r24, 0x01  ; 1
12
  return u;
13
}
14
  a0:  08 95         ret
15
16
000000a2 <foo>:
17
18
bool foo(void)
19
{
20
  u = 8;
21
  a2:  88 e0         ldi  r24, 0x08  ; 8
22
  a4:  80 93 e2 00   sts  0x00E2, r24
23
  return b;
24
}
25
  a8:  80 91 e0 00   lds  r24, 0x00E0
26
  ac:  08 95         ret
27
28
000000ae <fuu>:
29
30
bool fuu(void)
31
{
32
  *(uint8_t*)(void*)&b1 = 7;            // ausgetrickst
33
  ae:  87 e0         ldi  r24, 0x07  ; 7
34
  b0:  80 93 e1 00   sts  0x00E1, r24
35
  return !!(bool)((uint8_t)b);          // alles überflüssig
36
}
37
  b4:  80 91 e0 00   lds  r24, 0x00E0
38
  b8:  08 95         ret

von Theor (Gast)


Lesenswert?

Bemerkenswert! (Ich frage mich, warum mir das noch nicht passiert ist).

Vielleicht sollten wir doch mal versuchen einen Testcode hinzuschreiben.

Jedenfalls:

Widerspricht das aber nicht doch dem Standard?

Falls der Compiler, - soweit ja zurecht -, annimmt, dass bool immer zu 0 
oder 1 evaluiert und also Vergleiche mti 0 und nicht 0 wegoptimiert weil 
sie redundant sind, dann bleibt doch aber noch die Regel, dass 
Vergleiche immer zu 0 oder 1 evaluieren - müssen.

Und diese Regel würde der Compiler doch verletzen, wenn er zwar den 
Vergleich wegoptimiert aber nicht dafür sorgt, dass der resultierende 
Wert , genau 0 oder 1 ist. Er optimiert ja nicht nur den Vergleich weg 
sondern gleichzeitig die Umwandlung des Vergleichsresultats in integer.

Seht Ihr das anders?


P.S. Muss mal in den Standards nachsehen. Deswegen wäre die 
Compilerversion doch interessant, weil sich daraus auch ergibt, welcher 
Standard anzuwenden ist.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Theor schrieb:
> Seht Ihr das anders?

Ja.

Der Compiler darf mit Fug und Recht davon ausgehen, dass die Variable 
bereits entweder den Wert 0 oder 1 hat, daher kann er den ganze anderen 
Kokolorus getrost wegwerfen.

Dass die Variante im vorliegenden Fall eben nicht 0 oder 1 war, ergab 
sich ja lediglich als Folge einer Aktion, die explizit als undefined 
behaviour markiert ist. Wenn wir Compiler dazu bringen wöllten, alle 
diese Fälle nun auch noch jedesmal auf Plausibilität zu testen, dann 
würde sich die Fraktion der Assemblerprogrammierer völlig zu Recht 
wieder drüber beklagen, wie lahmarschig der vom Compiler generierte Code 
doch sei.

: Bearbeitet durch Moderator
von mh (Gast)


Lesenswert?

Theor schrieb:
> Seht Ihr das anders?
Ja!

Theor schrieb:
> P.S. Muss mal in den Standards nachsehen. Deswegen wäre die
> Compilerversion doch interessant, weil sich daraus auch ergibt, welcher
> Standard anzuwenden ist.
Ich weiß nicht wie es bei K&R aussieht, aber ab C89 sollte sich da 
nichts mehr geändert haben.

von Theor (Gast)


Lesenswert?

Jörg W. schrieb:
> [,,,]
> Dass die Variante im vorliegenden Fall eben nicht 0 oder 1 war, ergab
> sich ja lediglich als Folge einer Aktion, die explizit als undefined
> behaviour markiert ist.
> ]...]

Falls Du damit sagen, willst, dass aus undefined behavior eben undefined 
behavior folgt, überzeugt mich das. OK.


Naja. Bisher habe ich wohl entweder mehr Glück als Verstand gehabt oder 
umgekehrt. :-)

von Peter D. (peda)


Lesenswert?

Theor schrieb:
> Naja. Bisher habe ich wohl entweder mehr Glück als Verstand gehabt

Du wirst wohl keine Schweinereien à la (void*) in Deinem Code haben und 
alle Warnungen à la:
"x.c:35: warning: 'x' is used uninitialized in this function"
beseitigt haben.
Bei sauberem Code muß man keine Angst vor solchen Fehlern haben.

von Theor (Gast)


Lesenswert?

@ Jörg

Hach. Ich weiß nicht. Daran gefällt mir doch was nicht.

Ich will Dir keinesfalls auf die Nerven gehen oder rummeckern. Und ich 
wei0 (oder glaube mich zu erinnern), dass Du am GCC mitarbeitetst und 
daher vermutlich mehr in der Materie steckst, als ich. Aber vielleicht 
hast Du ja etwas Gedudld und magst nochmal antworten.

Das mit dem undefined behavior ist an sich unstrittig.
Aber was ist mit der Regel das Vergleiche immer entweder zu 0 oder zu 1 
evaluieren. (Siehe das Zitat aus C99 hier: 
Beitrag "Re: C (MinGW): Unerwartete Bool-Auswertung")
Diese Regel dürfte doch eigentlich durch die Optimierung nicht verletzt 
werden. Oder doch? Ergibt sich das aus dem Standard?

von Walter T. (nicolas)


Lesenswert?

Peter D. schrieb:
> Schweinereien à la (void*)

Was ist an (void *) eine Schweinerei? Gibt es eine bessere Möglichkeit, 
ungenutzte Variablen in einer Funktion zu behandeln?

von Theor (Gast)


Lesenswert?

Peter D. schrieb:
> Theor schrieb:
>> Naja. Bisher habe ich wohl entweder mehr Glück als Verstand gehabt
>
> Du wirst wohl keine Schweinereien à la (void*) in Deinem Code haben

Schon, aber extrem selten. Ich caste auch Buffer-Zeiger in 
Strukturzeiger und umgekehrt. Aber gaaaaanz vorsichtig. :-)

> und
> alle Warnungen à la:
> "x.c:35: warning: 'x' is used uninitialized in this function"
> beseitigt haben.

Ja- Das auch.

> [,,,]

von Theor (Gast)


Lesenswert?

Hmm.

Dennoch sticht der Einwand von Jörg, dass man, wollte man auch im 
optimierten Fall, das Ergebnis von entweder 0 oder 1 erzwingen, gerade 
diese Optmierungen dann effektiv nie machen dürfte.
Unglücklich, aber folgerichtig.

Hat sich erledigt, Jörg. Danke.

Beitrag #5974074 wurde von einem Moderator gelöscht.
von mh (Gast)


Lesenswert?

Theor schrieb:
> Ich caste auch Buffer-Zeiger in
> Strukturzeiger und umgekehrt.

Das ist in Ordnung solange du nicht den Strukturzeiger dereferenzierst, 
sonst UB.

Beitrag #5974118 wurde vom Autor gelöscht.
von Walter T. (nicolas)


Angehängte Dateien:

Lesenswert?

Ich nähere mich gerade dem Minimalbeispiel. Die Datei im Anhang wird 
ohne Warnung kompiliert, nutzt aber die Variable b3 vor der 
Initialisierung.

Auf Anhieb kann ich keine weitere Zeile löschen, ohne daß der Compiler 
merkt, dass State.b3 uninitialisiert bleibt.

Jetzt habe ich erst einmal ein Mittagessen verdient, dann schaue ich 
weiter.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Ich habe mal dein "intmath.h" ersetzt durch:
1
#include <stdint.h>
2
#include <stdlib.h>
3
4
typedef int exit_t;

Damit compiliert es unabhängig von deiner Umgebung.

clang schafft es es bis Version 8 nicht, eine Warnung zu generieren. 
GCC 5 ebenfalls nicht, GCC 8 wirft eine Warnung:
1
foo.c: In function 'foo':
2
foo.c:77:36: warning: 'State.b3' is used uninitialized in this function [-Wuninitialized]
3
     printf("test: 0x%x --\n", State.b3);
4
                               ~~~~~^~~

Es gibt also Licht am Ende des Tunnels. :)

: Bearbeitet durch Moderator
von Peter D. (peda)


Lesenswert?

Theor schrieb:
> Aber was ist mit der Regel das Vergleiche immer entweder zu 0 oder zu 1
> evaluieren. (Siehe das Zitat aus C99 hier:
> Beitrag "Re: C (MinGW): Unerwartete Bool-Auswertung")

Arc N. schrieb:
> Nur darf der Compiler das nicht annehmen, sondern sollte sich an den
> Standard halten...

Der Compiler trifft keine Annahmen, denn er ist kein Mensch. Er geht 
schlichtweg davon aus, daß das, was er einmal reingeschrieben hat, auch 
genauso zurück gelesen werden kann. Für die korrekte Funktion ist es 
unerheblich, ob er beim Schreiben nach bool konvertiert oder erst beim 
Lesen der bool-Variablen.

Wenn einer am Compiler vorbei einen Bool falsch setzt, dann ist er auch 
ganz allein dafür verantwortlich. Ursächlich für das Fehlverhalten ist 
also nicht der Compiler, sondern der Programmierer mit seiner Annahme, 
der Compiler würde überflüssige Konvertierungen einbauen.

von Peter D. (peda)


Lesenswert?

Walter T. schrieb:
> Ich nähere mich gerade dem Minimalbeispiel.

Sowas schreibt man besser:
1
struct
2
    {
3
        uint32_t ctrlCount;
4
        uint32_t keyCount;
5
        int uiElement;
6
        int_fast8_t keyAction;
7
        enum egml_states_e state;
8
        uint16_t motorCurrent;
9
        bool b0;
10
        bool b1;
11
        bool b2;
12
        bool b3;
13
    }
14
    State = { .ctrlCount = -1,
15
              .b0 = false,
16
             // usw.
17
            };
Damit werden immer alle Elemente initialisiert, d.h. die ohne explizite 
Zuweisung mit 0.

Beitrag #5974187 wurde von einem Moderator gelöscht.
von Walter T. (nicolas)


Lesenswert?

Jörg W. schrieb:
> Ich habe mal dein "intmath.h" ersetzt

Danke! Das hatte ich wohl übersehen.

Peter D. schrieb:
> Sowas schreibt man besser:

Erst einmal: Ja. Du hast Recht. Dieses sinnlose Beispiel sieht so aus, 
wie es aussieht, weil ich ein längeres Beispiel so lange zusammengekürzt 
habe, daß das Fehlen der Warnung erhalten blieb. "In der Realität" 
werden fast alle Felder des structs mit Variablen, nicht mit Konstanten 
initialisiert.

Jörg W. schrieb:
> clang schafft es es bis Version 8 nicht, eine Warnung zu generieren.
> GCC 5 ebenfalls nicht, GCC 8 wirft eine Warnung

Danke fürs Gegentesten. Dann kann ich das Problem wohl auf meinen GCC 
5.1.0 zurückführen. Es wird wohl mal Zeit für ein Update.

Ich werde mich künftig an den Tipp von PeDa halten, des Struct aus 
Prinzip erst einmal leer zu initialisieren - dann sollte diese Falle 
auch bei alten Compilerversionen nicht mehr auftauchen.

von A. S. (Gast)


Lesenswert?

Jörg W. schrieb:
> A. S. schrieb:
>> Ist das so?
>
> Ja, aber du hast es erstens völlig aus dem Zusammenhang gerissen und
> zweitens nicht richtig gelesen. Gehe also einfach nochmal zurück auf
> "Los!", und starte von vorn mit dem Thread.

Jörg, ich habe den Thread aufmerksam verfolgt. Kenne aber die 
Versionshistorie der Compiler nicht. Daher auf eine Frage 
zusammengefasst (für int sind sie dann eh hinfällig):

1) Darf jeder C/C++-Kompiler bei Variablen vom Typ bool (seit es das 
jeweils gibt) annehmen, dass diese immer true oder false enthält und 
keine abweichende Bitkombination?

von Theor (Gast)


Lesenswert?

Entschuldigung, dass ich hier mal die Absicht der Moderatoren 
durchkreuze, den Thread 
Beitrag "C (MinGW): Unerwartete Bool-Auswertung" 
nur noch für angemeldete Benutzer offen zu lassen. (Da gabs wohl ein 
paar Provokateure).
Ich bin nur sehr an Peters Stellungnahme interessiert.
(Falls gewollt mag dieser Beitrag auch in den Thread verschoben werden).

Peter D. schrieb:
> Theor schrieb:
>> Aber was ist mit der Regel das Vergleiche immer entweder zu 0 oder zu 1
>> evaluieren. (Siehe das Zitat aus C99 hier:
>> Beitrag "Re: C (MinGW): Unerwartete Bool-Auswertung")
>
> Arc N. schrieb:
>> Nur darf der Compiler das nicht annehmen, sondern sollte sich an den
>> Standard halten...
>
> Der Compiler trifft keine Annahmen, denn er ist kein Mensch. Er geht
> schlichtweg davon aus, daß das, was er einmal reingeschrieben hat, auch
> genauso zurück gelesen werden kann. Für die korrekte Funktion ist es
> unerheblich, ob er beim Schreiben nach bool konvertiert oder erst beim
> Lesen der bool-Variablen.
>
> Wenn einer am Compiler vorbei einen Bool falsch setzt, dann ist er auch
> ganz allein dafür verantwortlich. Ursächlich für das Fehlverhalten ist
> also nicht der Compiler, sondern der Programmierer mit seiner Annahme,
> der Compiler würde überflüssige Konvertierungen einbauen.


Ich vermute, dass Du beide Zitate in einen Zusammenhang stellst, weil 
von Standard die Rede ist.
Allerdings beziehen sich die beiden Zitate auf zwei verschiedene Aspekte 
des Standards.

Einmal geht es darum, ob bool Werte >1 als identisch mit 1 behandelt 
werden (Arc).
Ein andermal geht es darum, ob das Resultat von Vergleichen nicht 
grundsätzlich 0 oder 1 sein muss. (Der TO verwendet ja dieses Resultat 
als Operanden für den Schiebeoperator, nicht das bool selbst).

Dein Einwand beginnend mit "Wenn einer am Compiler vorbei ..." kann 
sich,  meiner Ansicht nach, auf das von Arc Gemeinte, aber nicht auf das 
von mir Gemeinte beziehen.

Die Koexistenz beider Regeln, halte ich für problematisch, Und zwar 
nicht, weil jemand einen Fehler gemacht hat, sondern weil, wie immer man 
diese beiden Regeln gestaltet, ein Widerspruch entstehen kann.

Und zwar:
1. Die eine Regel erlaubt dem Implementierer, davon auszugehen, das bool 
immer 0 oder 1 ist und das Vergleiche mit 0 und 1 resp. >1 redundant 
sind.
2. Die andere Regel schreibt vor, dass Vergleiche immer Resultat 0 oder 
1 haben.

Wenn aber die erste Regel nicht nur zur Code-Überprüfung (also zu 
Warnungen oder Fehlermeldungen) verwendet wird, sondern zur Optimierung, 
dann wird dabei potentiell (wie man in diesem Thread sieht) die zweite 
Regel verletzt.

Ich denke dieser Widerspruch entsteht durch die Tatsache, dass bool 
letztlich durch Speicher realisiert wird, der eben doch mehr als zwei 
Zustände annehmen kann. Man kann in gewissen Fällen garnicht anders, als 
entweder implizit die erste oder die zweite Regel temporär zu 
ignorieren.

Ob nun dieses bool >1 explizit hingeschrieben wird oder funktional 
entsteht (etwa durch externe Daten), beeinflusst nur die Tatsache ob der 
Compiler das erkennen kann. Nicht aber seine Entscheidung, dass in der 
gewählten Weise zu optimieren.
Letztlich musste man sich entscheiden, ob man diese Stellen nun 
unabhängig von der zweiten Regel optimiert. (Wenn etwa, das Resultat 
kein Teilausdruck mit letztlich arithmetischem Resultat ist, ist das ja 
immer zulässig; etwa in if-Bedingungen).

Ich bin, wie ich schon schrieb, aus pragmatischen Gründen geneigt, 
diesen Widerspruch zu akzeptieren, weil sonst im resultierenden Code 
alle bool zunächst auf 0 oder 1 abgebildet werden müssten. Aber das das 
nun völlig eindeutig so sein muss, will ich auch nicht bejahen.

Zum Schluss:
Eine Warnung, dass an solchen Stellen potentiell ein Problem entstehen 
könnte (gibt es ja auch für andere Fälle, die nicht definitiv potentiell 
ungewolltes Verhalten ergeben) halte ich für sinnvoll.

Ich schätze Dein Urteil im allgemeinen sehr, Peter, wenn ich auch hier 
nicht Deiner Ansicht bin. Daher würde ich mich freuen, wenn Du das 
kommentieren wolltest.


P.S. Im übrigen ist das Verhalten insgesamt gesehen auch nicht ganz 
konsistent. Soweit mir bekannt ist (ich bitte ggf. um Richtigstellung) 
gilt nämlich die Regel das >1 gleich true ist in if-Bedingungen auch für 
bool.

P.P.S. Als alter Knopf verwende ich schlicht bool so gut wie gar nicht. 
Daher kommt es wohl auch, dass ich überhaupt icht dazu neige, 
vorauszusetzen, dass nur entweder 0 oder 1 auftritt. Entweder ich bin 
durch Verwendung von Literalen sicher oder ich überprüfe das zur 
Laufzeit, nachdem ich Daten eingelesen habe.

P.P.P.S
Beitrag "Re: C (MinGW): Unerwartete Bool-Auswertung"
Jörg schrieb:

> Ich habe mal dein "intmath.h" ersetzt durch:
> ]...]
clang schafft es es bis Version 8 nicht, eine Warnung zu generieren.
GCC 5 ebenfalls nicht, GCC 8 wirft eine Warnung:
> ]...}
Es gibt also Licht am Ende des Tunnels. :)

Na Super. :-)

von Oliver S. (oliverso)


Lesenswert?

C11 schreibt dazu:
1
 6.3.1.2 When any scalar value is converted to _Bool, the result is 0 if the value compares equalto 0; otherwise, the result is 1

Damit dürfte es fast unmöglich sein, in einer Variablen vom Typ _Bool 
was anderes als 0 oder 1 unterzubringen.

Oliver

: Bearbeitet durch User
von Bernd K. (prof7bit)


Lesenswert?

Oliver S. schrieb:
> Damit dürfte es fast unmöglich sein, in einer Variablen vom Typ _Bool
> was anderes als 0 oder 1 unterzubringen.

Es ist möglich mittels Type-Punning:

    "If the member used to read the contents of a union object is not 
the same as the member last used to store a value in the object, the 
appropriate part of the object representation of the value is 
reinterpreted as an object representation in the new type as described 
in 6.2.6 (a process sometimes called ‘‘type punning’’). This might be a 
trap representation."

"Trap representation" ist der Begriff der diese "unmöglichen" Werte 
bezeichnet.

: Bearbeitet durch User
von mh (Gast)


Lesenswert?

Theor schrieb:
> Wenn aber die erste Regel nicht nur zur Code-Überprüfung (also zu
> Warnungen oder Fehlermeldungen) verwendet wird, sondern zur Optimierung,
> dann wird dabei potentiell (wie man in diesem Thread sieht) die zweite
> Regel verletzt.

Die Regeln gelten aber nur, wenn es ein fehlerfreies Programm gibt. Wenn 
in einem bool aber etwas anderes als 0 oder 1 steht, ist das Programm 
nicht mehr fehlerfrei. Der Standard ist sehr eindeutig in Bezug auf 
undefined behaviour.

Mein Nachtrag zum original Thread, bzw. meine Ergänzung zum dortigen 
Beitrag
1
Autor: Bernd K. (prof7bit)
2
Datum: 16.09.2019 15:39

Die genaue Definition von trap representation aus dem Standard 
(6.2.6.1.5 aus N1256):
1
Certain object representations need not represent a value of the object type. If the stored
2
value of an object has such a representation and is read by an lvalue expression that does
3
not have character type, the behavior is undefined. If such a representation is produced
4
by a side effect that modifies all or any part of the object by an lvalue expression that
5
does not have character type, the behavior is undefined.41) Such a representation is called a trap representation.

von Theor (Gast)


Lesenswert?

Hm. Offenbar liegt meiner Frage die folgende falsche Annahme zugrunde:

"Falls in in einem komplexen Ausdruck, ein Teilausdruck die Bedingungen 
für 'undefined behavior" erfüllt, so muss dieses Verhalten durch in der 
Auswertungsreihenfolge nachfolgende Ausdrücke aufgehoben (also wieder 
definiert) werden.

Das ist natürlich nicht zwangsläufig so und im Standard auch nicht 
gesagt.

Keine Ahnung wie ich darauf kam. Naja. Errare humanum est. :-)

OK. Danke.

von Peter D. (peda)


Lesenswert?

Ich verstehe Dein Problem nicht. Der Compiler arbeitet genau nach 
Standard. Jeder Ausdruck != 0 wird als true ausgewertet. Wird ein 
Ausdruck einem Bool zugewiesen, speichert der Compiler 0 oder 1. Er geht 
daher davon aus, daß er vorher 0 oder 1 gespeichert hat.

Probleme gibt es nur in folgenden Fällen, die alle Programmierfehler 
sind:
- Der Bool wird gelesen, bevor ihm was zugewiesen wurde.
- Per (void*) sagt man dem Compiler, halt die Klappe und mache was ich 
sage, auch wenn es falsch ist.
- Eine Union aus bool und int, auf die gegenseitig zugegriffen wird.

Beitrag #5974497 wurde von einem Moderator gelöscht.
von Theor (Gast)


Lesenswert?

Peter D. schrieb:
Ah. Danke Peter, dass Du nochmal auf meine Beiträge eingehst.

> Ich verstehe Dein Problem nicht.
> [...]

Ich schreibe nochmal in anderen Worten was mein Problem war. (Hat sich 
zwar eigentlich schon erledigt, aber vielleicht hast Du dazu etwas 
hinzuzufügen. Siehe: 
Beitrag "Re: Noch ein Punkt zu dem Thread: C (MinGW): Unerwartete Bool-Auswertung").

Nimmt man den Teil-Ausdruck:
1
(r != 0)<<2 )

so wird zunächst, der Teil
1
r != 0

ausgewertet.

Da r dem Standard gemäß nur true = 1 oder false = 0 sein kann, darf der 
Kompiler diesen Vergleich durch r ersetzen. So weit, so gut.
Der Typ dieses Zwischenresultats ist m.M.n. bool (bzw. _BOOL).

Da nun r, falls es nicht oder fehlerhaft initialisert wurde keinen 
Wert der ein bool repräsentieren darf, enthält, sondern einen anderen 
(sagen wir z.B. (15) enthält, steht dort nach der Auswertung offenbar 
sowas wie:
1
15 << 2

Dieser Schritt enthält ein implizites cast von bool nach int.
In diesem Fall ein cast einer ungültigen Repräsentation eines bools zu 
einem int. OK?

Ich habe nun, - wie ich jetzt meine, irrigerweise -, angenommen, der 
cast sollte wenigstens so realisiert sein, dass er auch ungültige 
Repräsentation in gültige umwandelt, bevor er die letzliche Umwandlung 
in den Zieltyp int vornimmt.

Anders gesagt, habe ich angenommen, dass solche impliziten cast so 
gestaltet sind oder sein sollten, dass aus undefined behavior wieder 
defined behavior wird. Das war aber mein Irrtum.

Der Standard bestimmt hier ausdrücklich "undefined behavior". Er sagt 
nicht , dass dieses undefined behavior "falls zweifelsfrei möglich" 
(ich denke, dass das theoretisch möglich ist, ist unstreitig) durch 
entsprechende Umwandlung sozusagen abgefangen werden muss.

Ich vermute, ich habe mich davon irreführen lassen, dass "man", etwa in 
if-Bedingungen, 0 als false (also der else-Zweig wird ausgeführt) und 
nicht-0 als true interpretiert und entsprechenden Code compiliert. 
Irgendwie bin ich davon ausgegangen, dass dies implizit auch für solche 
Ausdrücke gilt.

Habe ich mich verständlicher ausgedrückt?

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Theor schrieb:
> Ich vermute, ich habe mich davon irreführen lassen, dass "man", etwa in
> if-Bedingungen, 0 als false (also der else-Zweig wird ausgeführt) und
> nicht-0 als true interpretiert und entsprechenden Code compiliert.

Yep.

Der Punkt ist: die Bedingungen von if (etc.) haben den Typ "int", nicht 
"_Bool". Daher muss der Compiler an dieser Stelle explizit einen Test 
auf !=0 vornehmen. Hat er dagegen schon ein _Bool vorliegen, kann er 
implizit annehmen, dass dieses nur entweder 0 oder 1 sein kann. Damit 
kann er natürlich auch in einem if, wenn der Operand vom Typ _Bool ist 
(aber nur dann), einen Test auf ==1 statt !=0 einbauen.

von Oliver S. (oliverso)


Lesenswert?

Bernd K. schrieb:
> Es ist möglich mittels Type-Punning:

Es geht auch mit den weiter oben schon angedeuteten 
Pointer-cast-Schweinereien. Letztendlich ist das immer noch C ;)

Wenn’s dann dem Programmierer auf die Füße fällt, ist der halt selber 
schuld.

Oliver

von Jemand (Gast)


Lesenswert?

Jörg W. schrieb:
> wenn der Operand vom Typ _Bool ist
> (aber nur dann), einen Test auf ==1 statt !=0 einbauen.

…oder z. B. nur Bit 0 der Speicherstelle prüfen.
Alles schon gesehen.

von Theor (Gast)


Lesenswert?

Jörg W. schrieb:
> Theor schrieb:
>> Ich vermute, ich habe mich davon irreführen lassen, dass "man", etwa in
>> if-Bedingungen, 0 als false (also der else-Zweig wird ausgeführt) und
>> nicht-0 als true interpretiert und entsprechenden Code compiliert.
>
> Yep.
>
> Der Punkt ist: die Bedingungen von if (etc.) haben den Typ "int", nicht
> "_Bool".
> [...]

Der Ausdruck muss einen Skalar ergeben, wenn Du mir die Erbsenzählerei 
nachsehen willst. Kann also, unter Anderem, int, aber eben auch _Bool 
sein. :-)

Aber meine Frage ist ja nun erledigt. Danke für Eure freundliche Geduld.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Theor schrieb:
>> Der Punkt ist: die Bedingungen von if (etc.) haben den Typ "int", nicht
>> "_Bool". [...]
>
> Der Ausdruck muss einen Skalar ergeben, wenn Du mir die Erbsenzählerei
> nachsehen willst.

Na gut, überredet. :-)

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.