Moinsen,
Ich habe gestern abend recht lange nach einem Fehler gesucht:
1
#include<stdbool.h>
2
#include<stdint.h>
3
4
staticinlineboolfoo(void)
5
{
6
return(GPIOA->IDR&GPIO_Pin_5)!=0;
7
}
8
9
uin32_tbar(void)
10
{
11
staticuint32_tbitmask=0;
12
for(inti=0;i<8;i++)
13
{
14
bitmask|=foo();
15
bitmask<<=1;
16
}
17
returnbitmask;
18
}
Letztendlich rührte der Fehler daher, daß foo() die Werte 0 und 32 und
nicht wie erwartet 0 und 1 annahm.
Als Lösung mußte dann ein Trigraph herhalten:
1
bitmask|=foo()?1:0;
Wie kommt der Compiler darauf, daß !true ausgerechnet 32 ist? 42 hätte
ich beinahe noch verstanden.
Unlogiker schrieb:> Wie kommt der Compiler darauf, daß !true ausgerechnet 32 ist?
Du selbst hast den Fehler gemacht, denn du gibst bei foo() nicht true
oder false zurück, sondern das Ergebnis einer mathematischen Operation.
Und GPIO_Pin_5 ist 32. So einfach ist das.
Wenn du boolean verwenden willst, dann musst du auch mit true und false
arbeiten und darfst nicht einfach alles nach Gutdünken und Gefühl
durcheinanderwursteln...
1
staticinlineboolfoo(void)
2
{
3
return(GPIOA->IDR&GPIO_Pin_5)?true:false;
4
}
Und ein paar Zeilen tiefer das selbe Gebastel: du bekommt einen boolean
zurück und weist den einem integer zu:
1
staticuint32_tbitmask=0;
2
:
3
bitmask|=foo();
Auch das ist impliziter Murks, weil du annimmst, dass true=1 und
false=0 ist.
Lothar M. schrieb:> sondern das Ergebnis einer matehmatischen Operation.
Das tut er doch gar nicht, da steht doch "!=0". In C ist das etwas
seltsam, in C++ wird alles was nicht 0 ist zu true, und true wird immer
zu 1, d.h. (int)(bool)(42) ist 1. Bin mir nicht 100% sicher dass das in
C auch so ist...
Dr. Sommer schrieb:> Das tut er doch gar nicht, da steht doch "!=0".
Ähm, richtig...
> Bin mir nicht 100% sicher dass das in C auch so ist...
Mich würde interessieren, ob unterschiedliche Optimierungslevel
unterschiedliche Ergebnisse bringen.
Keine echte Ahnung, aber bool kann auch nur ein Makro sein. Und da in C
alle Zahlen !=0 wahr sind wird Dein !=0 vermutlich wegoptimiert. Welcher
Compiler ist das? Das Problem sollte wenn Du bool durch unsigned int
ersetzt bleiben.
Ich vermute den Fehler darin, dass bar() mehrfach aufgerufen wird, dabei
aber die lokale Variable bitmask wegen des static nicht jedesmal
zurückgesetzt wird. Oder ist das etwa Absicht?
uin32_t ist übrigens kein Typ aus stdint.h. Exakt in dieser Form hast du
die Funktion also wohl nicht getestet, weswegen sich in deinem
tatsächlichen Code noch weitere Fehler verbergen können.
Die Funktion foo() ist aber korrekt. Sie würde selbst dann noch
funktionieren, wenn du das !=0 wegließest.
Lothar M. schrieb:> Mich würde interessieren, ob unterschiedliche Optimierungslevel> unterschiedliche Ergebnisse bringen.
Ich probiere es morgen abend aus. Ich habe gestern mit -O1 gearbeitet.
Yalu X. schrieb:> Ich vermute den Fehler darin, dass bar() mehrfach aufgerufen wird, dabei> aber die lokale Variable bitmask wegen des static nicht jedesmal> zurückgesetzt wird. Oder ist das etwa Absicht?
Natürlich.
Yalu X. schrieb:> uin32_t ist übrigens kein Typ aus stdint.h.
Stimmt. Es ist ein Tippfehler.
Yalu X. schrieb:> Exakt in dieser Form hast du> die Funktion also wohl nicht getestet, weswegen sich in deinem> tatsächlichen Code noch weitere Fehler verbergen können.
So lange gesucht habe ich deshalb, weil er sich zusammen mit einem
(n)<->(n+1)-Fehler zusammen einen schönen Abend gemacht hat.
Unlogiker schrieb:> Moinsen,>> Ich habe gestern abend recht lange nach einem Fehler gesucht:>
1
>uin32_tbar(void)
2
>{
3
>staticuint32_tbitmask=0;
4
>for(inti=0;i<8;i++)
5
>{
6
>bitmask|=foo();
7
>bitmask<<=1;
8
>}
9
>returnbitmask;
10
>}
11
>
>> Letztendlich rührte der Fehler daher, daß foo() die Werte 0 und 32 und> nicht wie erwartet 0 und 1 annahm.
Wie kommst Du darauf? Wahrscheinlich hast Du Dir den Wert von bitmask
angesehen ... bitmask hatte aber schon den Wert aus einer vorigen
Berechnung (static storage class).
Lothar M. schrieb:> Dr. Sommer schrieb:>> Das tut er doch gar nicht, da steht doch "!=0".> Ähm, richtig...>>> Bin mir nicht 100% sicher dass das in C auch so ist...> Mich würde interessieren, ob unterschiedliche Optimierungslevel> unterschiedliche Ergebnisse bringen.
Sicher nicht (s.a. as-if-rule).
Interessantes Problem, das ich noch nie hatte, obwohl ich mit Bitd sehr
oft arbeite.
Soweit ich weiß, ist false als 0 definiert und true als !false ... kann
also alles dein.
Genauso ist jedet Wert ungleich 0 true ... das sollte man schon wissen,
wenn man in C programmiert :)
Der Compiler hat alles richtig gemacht :)
Die Funktion selbst ist auch kein Geheimnis. Sie ist nur lang und
unübersichtlich. Und das Konstrukt, daß mir gestern den Abend versüßt
hat ((bool) ausdruck() != 0 != 1) ist nicht mehr drin, weil es durch den
Trigraph in der aufrufenden Funktion ersetzt wurde.
Mampf unterwegs schrieb:> Interessantes Problem, das ich noch nie hatte, obwohl ich mit Bitd sehr> oft arbeite.>> Soweit ich weiß, ist false als 0 definiert und true als !false ... kann> also alles dein.
Darum geht es hier nicht: lt. C11 liefern die Operatoren == != etc.
immer 0 oder 1 (bool gibt es ja nicht als primitiven DT).
Und es gibt die kontextuellen Konvertierungen von num. Typen.
> !false kann also alles dein.
Das ist ist nicht die Problemursache. Ursache ist, dass die Variable
bitmask als static deklariert wurde und noch Reste von vorherigen
Funktionsaufrufen enthält.
Das Problem liegt in getbutton(), denn dort wird gar kein Vergleich !=
gemacht.
Und: wie oben schon geschrieben, existiert in C der DT bool nicht,
sondern nur ein Typalias für int!
Unlogiker schrieb:> Wie kommt der Compiler darauf, daß !true ausgerechnet 32 ist?
C kennt kein "bool". Zwar kann man sich mit stdbool.h einen
entsprechenden Typ definieren, aber das Verhalten des Compilers ändert
sich dadurch nicht.
Es gibt ein "implizites" bool-Verhalten, das bedeutet, daß jeder von 0
abweichende Wert als "wahr" angesehen wird.
Daraus resultiert aber auch, daß ein Vergleich mit einer Konstanten für
"true" zu vermeiden ist.
In if/while/for-Statements kann das "implizite" bool-Verhalten aber auch
ohne diese Vergleiche genutzt werden. Das ist in C seit Jahrzehnten
etablierter Standard; der Code müsste nur leicht verändert werden:
Wilhelm M. schrieb:> Das Problem liegt in getbutton(), denn dort wird gar kein Vergleich !=> gemacht.
Das kommt jetzt auf io_bitIsSet() an...
Rufus Τ. F. schrieb:> Es gibt ein "implizites" bool-Verhalten, das bedeutet, daß jeder von 0> abweichende Wert als "wahr" angesehen wird.
So handhabe ich das auch. Es gibt "0" und es gibt "!0".
Und "!0" kann alles sein, was "nicht Null" ist.
Rufus Τ. F. schrieb:> Unlogiker schrieb:>> Wie kommt der Compiler darauf, daß !true ausgerechnet 32 ist?>> C kennt kein "bool". Zwar kann man sich mit stdbool.h einen> entsprechenden Typ definieren, aber das Verhalten des Compilers ändert> sich dadurch nicht.>> Es gibt ein "implizites" bool-Verhalten, das bedeutet, daß jeder von 0> abweichende Wert als "wahr" angesehen wird.
Aber wenn der Code so war, wie er uns glauben machen will (also mit !=
in foo()), dann gibt foo() definitiv 0 (false) oder 1 (true) zurück.
Wilhelm M. schrieb:> Aber wenn der Code so war, wie er uns glauben machen will (also mit !=> in foo()), dann gibt foo() definitiv 0 (false) oder 1 (true) zurück.
Ich glaube, die Optimierung hat zugeschlagen ...
Foo ist inline deklariert und wird wohl gleich wegoptimiert und durch
(1<<5) ersetzt, weil es in dem Schleifenfurchlauf true ergeben hätte.
Glaube der Fehler war das static und durch die Optimierung ist der TE in
die Irre geleitet worden.
Rufus Τ. F. schrieb:> static inline bool foo(void)> {> return (GPIOA->IDR & GPIO_Pin_5) != 0;> }>
In der Kombination ist das != 0 in foo() aber auch überflüssig.
Rufus Τ. F. schrieb:> uin32_t bar(void)> {> static uint32_t bitmask = 0;> for( int i = 0; i<8; i++ )> {> if (foo())> bitmask |= 1;> bitmask <<= 1;> }> return bitmask;> }
Ansonsten macht das if-Statement genau das, was der Trigraph auch
macht.
Mampf unterwegs schrieb:> Wilhelm M. schrieb:>> Aber wenn der Code so war, wie er uns glauben machen will (also mit !=>> in foo()), dann gibt foo() definitiv 0 (false) oder 1 (true) zurück.>> Ich glaube, die Optimierung hat zugeschlagen ...
Sicher nicht, denn dann wäre die as-if-rule verletzt.
> Foo ist inline deklariert und wird wohl gleich wegoptimiert und durch> (1<<5) ersetzt, weil es in dem Schleifenfurchlauf true ergeben hätte.
Die primäre Bedeutung von inline ist nicht die Optimierung (das macht
der Compiler eh) sondern die Umgehung einer ODR-Verletzung.
> Glaube der Fehler war das static und durch die Optimierung ist der TE in> die Irre geleitet worden.
s.o.
Unlogiker schrieb:> Rufus Τ. F. schrieb:>> static inline bool foo(void)>> {>> return (GPIOA->IDR & GPIO_Pin_5) != 0;>> }>>>> In der Kombination ist das != 0 in foo() aber auch überflüssig.
Nein, es ist im Gegenteil ganz zentral und wichtig!!! Und es ist der
Grund für Deinen Fehler!
Und es ist der Grund für: "Stop teaching C".
Wilhelm M. schrieb:> Nein, es ist im Gegenteil ganz zentral und wichtig!!!
in diesem Fall nicht.
Wenn bool = 1 Bit:
-> true = 1, Ausdruck ist nicht False, Return-Wert 1
Wenn bool > 16 Bit:
-> Ausdruck ist nicht False, jeder Wert, der nicht False ist, darf
zurückgegeben werden und wird auch im if-Statement korrekt
interpretiert.
Soooo müde bin ich dann doch nicht.
Unlogiker schrieb:> Wilhelm M. schrieb:>> Nein, es ist im Gegenteil ganz zentral und wichtig!!!>> in diesem Fall nicht.>> Wenn bool = 1 Bit:> -> true = 1, Ausdruck ist nicht False, Return-Wert 1>> Wenn bool > 16 Bit:> -> Ausdruck ist nicht False, jeder Wert, der nicht False ist, darf> zurückgegeben werden und wird auch im if-Statement korrekt> interpretiert.>> Soooo müde bin ich dann doch nicht.
Nochmal: bool ist in C ein Typalias für int!
1
return(GPIOA->IDR&GPIO_Pin_5)!=0;
Der &-Op ist ein Bitweises UND und liefert dann wohl den Wert 32 oder 0,
je nachdem.
Nur der !=-Op liefert dann die 0 oder 1, die Du haben möchtest!
Unlogiker schrieb:> Als Lösung mußte dann ein Trigraph herhalten: bitmask |= foo() ?> 1: 0;
Das ist definitiv nicht die Lösung, da 3-fach gemoppelt.
Schon != darf nur zu 0 oder 1 evaluieren.
Dann noch das bool darf auch nur zu 0 oder 1 evaluieren, hätte also
allein völlig gereicht.
Hier mal, was der AVR-GCC dazu sagt:
1
#include<stdbool.h>
2
3
boolfoo(chari)
4
{
5
8e:8111cpser24,r1
6
90:81e0ldir24,0x01;1
7
returni;
8
}
9
92:0895ret
Wie ja schon gesagt wurde, ist die static Variable der Fehler.
Peter D. schrieb:
> ist die static Variable der Fehler.
Ist sie nicht - aber ich habe durchaus Verständnis dafür, daß man sich
einen so langen Thread nicht mehr genau durchliest.
Peter D. schrieb:
> Hier mal, was der AVR-GCC dazu sagt:
Das ist interessant und genau das Verhalten, was ich erwartet hätte.
Wie gesagt: Ich stelle morgen abend mal wieder den "schlechten" Zustand
her und liefere das Assembler-Listing.
Wilhelm M. schrieb:> Nochmal: bool ist in C ein Typalias für int!
Nein, das ist höchstens dann der Fall, wenn man bool selber so
definiert. Der Unlogiker verwendet aber stdbool.h.
Beispiel:
In
1
boolb=123;
wird b nicht mit 123, sondern mit 1 initialisiert, da der zugewiesene
Wert implizit in den Wertebereich {0, 1} konvertiert wird.
Wilhelm M. schrieb:> Nochmal: bool ist in C ein Typalias für int! return (GPIOA->IDR &> GPIO_Pin_5) != 0;>> Der &-Op ist ein Bitweises UND und liefert dann wohl den Wert 32 oder 0,> je nachdem.>> Nur der !=-Op liefert dann die 0 oder 1, die Du haben möchtest!
Frank M. schrieb:> Wilhelm M. schrieb:>> Nochmal: bool ist in C ein Typalias für int! return (GPIOA->IDR &>> GPIO_Pin_5) != 0;>>>> Der &-Op ist ein Bitweises UND und liefert dann wohl den Wert 32 oder 0,>> je nachdem.>>>> Nur der !=-Op liefert dann die 0 oder 1, die Du haben möchtest!>>
1
> $ cat bool.c
2
> #include <stdbool.h>
3
> #include <stdint.h>
4
> #include <stdio.h>
5
>
6
> static bool foo(void)
7
> {
8
> return 32;
9
> }
10
>
11
> int main ()
12
> {
13
> printf ("%d\n", foo());
14
> return 0;
15
> }
16
>
17
> $ cc -O bool.c && ./a.out
18
> 1
19
>
>> Was sagst Du jetzt?
Dazu sage ich:
1
#define bool _Bool
2
#define true 1
3
#define false 0
4
5
#else /* __cplusplus */
6
7
/* Supporting _Bool in C++ is a GCC extension. */
8
#define _Bool bool
Also: bool als tatsächlich Bool'sch zu betrachten, ist eine GCC
Erweiterung.
Wilhelm M. schrieb:> Also: bool als tatsächlich Bool'sch zu betrachten, ist eine GCC> Erweiterung.
Da steht "C++". Hier aber geht's um C.
Zu klären wäre, welches C. C89, C99, C11?
Lothar M. schrieb:> Und "!0" kann alles sein, was "nicht Null" ist.
C99/C11: "The result of the logical negation operator ! is 0 if the
value of its operand compares unequal to 0, 1 if the value of its
operand compares equal to 0. The result has type int. The expression !E
is equivalent to (0==E)."
Bei
return (GPIOA->IDR & GPIO_Pin_5) != 0;
muss also zwingend 0 oder 1 rauskommen.
Unlogiker schrieb:> Wenn bool > 16 Bit:> -> Ausdruck ist nicht False, jeder Wert, der nicht False ist, darf> zurückgegeben werden
Nein.
> Soooo müde bin ich dann doch nicht.
Doch.
Und auch das ... != 0 ist redundant, denn mit bool=_Bool und "When any
scalar value is converted to _Bool, the result is 0 if the value
compares equal to 0; otherwise, the result is 1." ist das bereits in der
Typdefinition des Resultats von foo() enthalten. Aus
static inline bool foo(void) { return GPIOA->IDR & GPIO_Pin_5; }
wird bei C99/C11 und bool=_Bool zwingend 0 oder 1.
A. K. schrieb:> Und auch das ... != 0 ist redundant, denn mit bool=_Bool und "When any> scalar value is converted to _Bool, the result is 0 if the value> compares equal to 0; otherwise, the result is 1." ist das bereits in der> Typdefinition des Resultats von foo() enthalten.
Könnte es sein, dass genau diese Typdefinition durch das Inline vom
Compiler wegoptimiert wird? Ist nur ein Gedanke.
Wilhelm M. schrieb:> Das Problem liegt in getbutton(), denn dort wird gar kein Vergleich !=> gemacht.Lothar M. schrieb:> Das kommt jetzt auf io_bitIsSet() an...
For your convenience, the expression has been replaced in the original
question text.
In getbutton() wird tatsächlich kein Vergleich (mehr) mit Null gemacht.
Der Teil wurde aus dem angehängten Quelltext entfernt, weil er sich als
wirkungslos herausgestellt hat und durch einen ternären Operator in der
aufrufenden Funktion ersetzt. Den alten Stand habe ich noch im
Versionsverwaltungssystem- ich komme nur momentan nicht dran.
Die Beschreibung in der Fragestellung entspricht dem ursprünglichen
Stand, der komplette c-Quelltext dem Workaround.
Wilhelm M. schrieb:> Die primäre Bedeutung von inline ist nicht die Optimierung (das macht> der Compiler eh) sondern die Umgehung einer ODR-Verletzung.
Das primäre Ziel des Inline ist definitiv die Optimierung. Ich gehe
davon aus, daß der Compiler die komplette For-Schleife ausrollt und sich
eine handvoll Vergleiche mit konstanten Werten ergibt.
Gästle schrieb:> Könnte es sein, dass genau diese Typdefinition durch das Inline vom> Compiler wegoptimiert wird? Ist nur ein Gedanke.
Nein. Eine Optimierung, die das Verhalten ändert ist, nur zulässig, wenn
das Verhalten nicht von der Sprache definiert ist. Ist es aber.
Nope. Es wird das "static" sein. Mal vorausgesetzt, der gezeigte
Demo-Code hat irgendeine Ähnlichkeit mit dem Code, um den es in
Wirklichkeit geht.
PS: Das ... != 0 kann man zwar weglassen, wenn garantiert ist, dass bool
dem in C99/C11 definierten _Bool entspricht. Hat man C89 und irgendwo
ist bool als char/int/... definiert, klappt das natürlich nicht mehr.
Insofern empfiehlt es sich, es trotzdem reinzuschreiben. Und den
offiziell daraus resultierenden doppelten Vergleich mit 0 wird der
Compiler dann schon rausoptimieren.
Unlogiker schrieb:> ((bool) ausdruck() != 0 != 1) ist nicht mehr drin, weil es durch den> Trigraph in der aufrufenden Funktion ersetzt wurde.
Es geht also um die Zeile
1
bitmerker|=getbutton(i)?1:0;
Wenn ich dich richtig verstehe, tritt der Fehler dann auf, wenn du diese
Zeile durch
1
bitmerker|=getbutton(i);
ersetzt.
Welchen Compiler in welcher Version mit mit welcher Kommandozeile
benutzt du?
Ich habe deinen Code mit arm-none-eabi-gcc 7.2.0 kompiliert, nachdem ich
die fehlenden Definitionen hinzugefügt habe, und erhalte dabei für beide
der obigen Zeilen exakt den gleichen Assemblercode.
Sodele....ich bin doch dazu gekommen, zwischen Tür und Angel den alten
Zustand wiederherzustellen.
Compilerversion ist 5.4.1 20160609 (release) [ARM/embedded-5-branch
revision 237715]
bitmerker in push_button_poll () ist static definiert, überlebt also
jeden Funktionsaufruf. In bitmerker werden nur Bits gesetzt, aber nie
zurückgesetzt. Kein wunder, wenn irgendwann alle Bits bis auf das
unterste gesetzt sind.
Ist das wirklich das, was Du willst? Und was soll das volatile davor?
Verzweiflungstat?
Ich halte sowohl das static als auch das volatile für unsinnig. Oder ich
habe die Logik dahinter nicht verstanden. Kann es sein, dass Du die
Bedeutung von static nicht verstanden hast?
Wenn ich mich recht erinnere, wurdest Du in diesem Thread schon mehrfach
auf den static-Fehler als Reaktion auf den Eröffnungsbeitrag
hingewiesen. Überlesen?
Ist nicht ganz einfach zu verstehen, soll im Prinzip genau das gleiche
machen, wie meine Entprellroutine mit 4-fach Abtastung.
Nur eben nacheinander statt parallel. Deshalb für 8 Tasten die
32Bit-Variable und die muß auch static sein. Läßt sich daher nicht so
einfach für mehr Tasten erweitern, wie meine Routine.
Fehler sehe ich bisher keine.
Das volatile für lokale Variablen ist in der Tat Unsinn. volatile müssen
nur globale Variablen sein, die vom Main gelesen werden, aber von
Interrupts oder Hardware gesetzt werden.
Unlogiker schrieb:> Compilerversion ist 5.4.1 20160609 (release) [ARM/embedded-5-branch> revision 237715]
Ich vermute mal das ist ein GCC. Wenn in der Kommandozeile nichts weiter
angegeben ist, nimmt der seit GCC 5.1.0 C11.
Aber poste daher mal die von Dir verwendete komplette Kommandozeile mit
der diese Datei compiliert wird. Wenn da -std=c89 oder -ansi drin steht,
dann ists C89.
Peter D. schrieb:> Nur eben nacheinander statt parallel.
Genau. Und an wild verteilten Pins.
Das volatile hat in allen Fällen den einzigen Zweck, daß man im Debugger
etwas sieht. Ansonsten bleibt von der Schleife wenig übrig.
Darf ich noch anmerken, dass Du Dir mit paralleler Tastenauswertung Code
sparen könntest?
1) getbutton gibt alle Tasten direkt zurück (Switch/Case entfällt)
2) genmask entfällt und pushbutton_poll schiftet direkt 8 Bit.
1
uint_fast8_tpushbutton_poll(void)
2
{
3
staticuint_fast8_tout=0;
4
staticvolatileuint32_tbitm=0;
5
6
bitm<<=8;
7
bitm|=getbutton(i);
8
9
out&=bitm|(bitm>>8)|(bitm>>16)|(bitm>>24);
10
out|=bitm&(bitm>>8)&(bitm>>16)&(bitm>>24);
11
returnout;
12
}
wobei das mit einzelnen Variablen flexibler wäre (bis zu 64 Tasten bei
long long).
Achim S. schrieb:> Darf ich noch anmerken, dass Du Dir mit paralleler Tastenauswertung Code> sparen könntest?>> 1) getbutton gibt alle Tasten direkt zurück (Switch/Case entfällt)
Ob ich das zusammenschieben der Bits der einzelnen Tasten in
pushbutton_poll() oder in getbutton() vornehme, bleibt vom Ergebnis
gleich. Ich habe mich für das eine entschieden, man könnte sich auch für
das andere entscheiden.
nada schrieb:> Sicher, dass das nicht eine optimierte Version ist, wo der Debugger> einfach nur noch trash anzeigt?
volatile uint_fast8_t tmp
Außerdem geht es hier nicht um Entprellung. Ich habe keine Ahnung, warum
hier auf µC.net soviel Geschrei um Entprellung gemacht wird. Entprellung
macht man einfach und fertig.
Es geht auch nicht darum, ob da eine static-Variable hingehört (tut es).
Es geht darum, daß in einem Rückgabewert einer bool-Funktion ein Wert
steht, der nicht 0 oder 1 ist.
Offensichtlich müssen sich die Experten und die selbsternannen Experten
auch erst einmal besinnen, ob das überhaupt erlaubt ist. Womit wir
wieder beim Titel wären: "Böse Falle". Wenn man die Falle kennt, kann
man sie sehr einfach vermeiden. Wobei zur Vermeidung eben das != 0 nicht
ausreicht, sondern tatsächlich eine Bedingung (if oder ternärer
Operator) zum Einsatz kommen muß.
Unlogiker schrieb:> AWobei zur Vermeidung eben das != 0 nicht> ausreicht, sondern tatsächlich eine Bedingung (if oder ternärer> Operator) zum Einsatz kommen muß.
Die Sprachdefinition ist eindeutig. Nur gibts bisher keine Möglichkeit,
das Problem anhand eines minimierten Testcases zu reproduzieren, um
festzustellen, ob es ein Programmiererfehler ist, oder am Ende ein
Compilerfehler. Das wäre ist der typische Ansatz. Democode und ab und zu
ein Screenshot helfen da nicht weiter.
Wilhelm M. schrieb:> Sorry, bei mir hat es sich so fest im Hirn eingebrannt, dass bool in C> ein no-go ist ... Asche auf mein Haupt
stdbool kannte ich gar nicht ... werde ich auch zukünftig nicht
verwenden xD
In c++ ist die Sache natürlich eine andere :)
Die Definition in stdbool würde mich interessieren ... muss ich mal nach
dem source cood suchen
laut https://clang.llvm.org/doxygen/stdbool_8h_source.html
tatsächlich eine GnuC Erweiterung ...
Kannte true ubd false nur als
#define false 0
#define true !false
aber nicht als
#define true true
#define false false
#define bool _Bool
Unlogiker schrieb:> ...> Es geht darum, daß in einem Rückgabewert einer bool-Funktion ein Wert> steht, der nicht 0 oder 1 ist.> ...
Na schön. Aber das beruht entweder auf einem Irrtum bei der Feststellung
des Fehlers, oder einem Irrtum über den angewandten C-Standard oder
einem Compilerfehler.
Wenn ich das recht sehe wäre erst noch zu klären, was nun eigentlich
vorliegt. Nach dem was Du schreibst, spricht einiges dafür, dass man mal
nachprüft ob ein Compilerfehler vorliegt. Aber wir sollten bei
Fehlersuchen nie etwas Plausibles auch einfach als gegeben annehmen.
Ich schlage vor, Du kompilierst den in Deinem Eröffnungspost gezeigten
Code (mit korrigiertem Datentyp und um ein main ergänzt) und zeigst den
entstehenden Assemblercode. Dazu den Compileraufruf.
Der TO sollte einfach - wie üblich - ein MCVE (minimum complete
verifying example) liefern. Dann kann das jeder mit dem Fehler
nachvollziehen. Bei so einem einfachen Code schließe ich aber ein
Compilerfehler aus und auch einen Optimierungsfehler.
Wilhelm M. schrieb:> Der TO sollte einfach - wie üblich - ein MCVE (minimum complete> verifying example) liefern. Dann kann das jeder mit dem Fehler> nachvollziehen.
Sicherlich. Das habe ich ja schon geschrieben. Wobei ich diesen Compiler
selbst nicht habe und daher auch gerne den Assemblercode sehen würde,
den der TO mit seinem Compiler erzeugt.
> Bei so einem einfachen Code schließe ich aber ein> Compilerfehler aus und auch einen Optimierungsfehler.
Ich stimme Dir an sich zu.
Aber diese Situation hat auch ein psychologisches Moment. Der TO scheint
mir im Moment äusserst frustriert und sachlichen Einwänden nur begrenzt
zugänglich zu sein. Er fühlt sich sogar, wie die Wortwahl "Falle" zeigt,
betrogen, hereingelegt, hintergangen. Hat also in dem Zusammenhang eine
gewisse Neigung zu Mißtrauen.
Ich räumte daher in meinem Beitrag die Möglichkeit ein, dass nicht er
den Irrtum begangen hat, sondern ein anderer. Das ist ein Trick um an
dem Problem arbeiten zu können und den Fokus des TO darauf zu lenken und
nicht auf einen eventuellen Fehler seinerseits. Ich bin ja auch
letztlich nicht daran interessiert ihn als Person eines Fehlers zu
überführen, sondern die Ursache zu finden. (Einfach nur, weil ich
neugierig bin).
Leider hast Du diesen Versuch mit Deinem Beitrag konterkariert in dem Du
die Wahrscheinlichkeit das es nicht sein Fehler sein könnte, als gering
beschreibst. Schade. (Man muss nicht immer schreiben, was durchaus
plausibel ist).
Aber vielleicht hat der TO ja doch inzwischen einiges seiner normalen
Ruhe und Gelassenheit wiedergewonnen und kann akzeptieren, dass die
Möglichkeit besteht, dass er einen Irrtum begangen hat.
Mampf unterwegs schrieb:> tatsächlich eine GnuC Erweiterung ...
Nein, seit fast 20 Jahren genormt. _Bool ist dabei ein builtin type,
lediglich die Sichtbarkeit als bool wird durch <stdbool.h> erzeugt.
Damit vermeidet man, dass (ältere) Programme, die "bool" als legitimen
Namen im appliation name space selbst definiert haben, mit der
C99-Ergänzung kollidieren. (_Bool ist im implmentation name space.)
Was du da als GCC-Erweiterung siehst, ist lediglich, dass auch im
C++-Modus (der schon immer bool hatte) die C99-artige Implementierung
über <stdbool.h> sichtbar gemacht wird. Damit kann man entsprechenden
C-Code alternativ als C++-Code compilieren.
Unlogiker schrieb:> Es geht darum, daß in einem Rückgabewert einer bool-Funktion ein Wert> steht, der nicht 0 oder 1 ist.
Ja, und darum, dass du uns bislang noch keinen Code zeigen konntest,
den wir auch nachvollziehen (sprich: compilieren) können, der das
von dir behauptete Verhalten tatsächlich erzeugt.
Das Stück Code da oben lässt sich für einen Außenstehenden nicht
compilieren, da zu viel vom „Drumherum“ fehlt (angefangen bei den
Definitionen für deinen Prozessor).
Kannst du denn mal das komplette Disassembly der Funktion zeigen?
An der Stelle, wo du oben im Debugger angehalten hast, wurden zuvor
nur zwei Speicherstellen umkopiert. Es ist daher nicht klar, woher
der Wert, den du da im Debugger siehst, überhaupt kam.
> Offensichtlich müssen sich die Experten und die selbsternannen Experten> auch erst einmal besinnen, ob das überhaupt erlaubt ist.
Da braucht man sich nicht zu besinnen, das ist ganz klar definiert:
1
6.3.1.2 Boolean type
2
3
When any scalar value is converted to _Bool, the result is 0 if the value compares equal
Unlogiker schrieb:> Warum schreibt keiner etwas? Das Fenster war im Screenshot ja wirklich> denkbar ungünstig gescrollt... fällt mir erst jetzt auf.
Screenshots schauen sich die wenigsten an (schlecht lesbar, wenig
Information, viel fehlt).
Hänge das .lss-File an, das ist deutlich informativer.
Dein Code ist ja recht komplex, da kann der Fehler leicht woanders sein.
Ich vermute mal stark, Du hast neben dem Hinzufügen des ?: noch andere
Änderungen gemacht und die haben dann den eigentlichen Fehler
korrigiert.
Daher wird das .c und .lss-File für den fehlerhaften Code benötigt,
alles andere ist im Kaffeesatz lesen.
Theor schrieb:> Wilhelm M. schrieb:>> Der TO sollte einfach - wie üblich - ein MCVE (minimum complete>> verifying example) liefern. Dann kann das jeder mit dem Fehler>> nachvollziehen.>> Bei so einem einfachen Code schließe ich aber ein>> Compilerfehler aus und auch einen Optimierungsfehler.>> Ich stimme Dir an sich zu.> Aber diese Situation hat auch ein psychologisches Moment. Der TO scheint> mir im Moment äusserst frustriert und sachlichen Einwänden nur begrenzt> zugänglich zu sein.
Wer keine Antwort hören möchte, sollte keine Fragen stellen ...
Durch das stdbool.h und inline sieht mir der Code aus dem ursprünglichen
Post nach ISO C99 oder ISO C11 aus. Dann wäre es ein Compilerbug, wenn
die Verwendung des Trigraphs irgendetwas am beobachtbaren Verhalten des
Programms ändert.
Welcher Compiler wurde denn verwendet?
Philipp
Philipp Klaus K. schrieb:> Dann wäre es ein Compilerbug, wenn> die Verwendung des Trigraphs irgendetwas am beobachtbaren Verhalten des> Programms ändert.
s/Trigraph/ternärer Operator/
> Welcher Compiler wurde denn verwendet?
Steht doch oben: GCC 5.4.1.
Philipp Klaus K. schrieb:> Durch das stdbool.h und inline sieht mir der Code aus dem ursprünglichen> Post nach ISO C99 oder ISO C11 aus. Dann wäre es ein Compilerbug, wenn> die Verwendung des Trigraphs irgendetwas am beobachtbaren Verhalten des> Programms ändert.>> Welcher Compiler wurde denn verwendet?
Wie so oft, "besteht" der TO darauf, dass der Fehler im Compiler liegt.
Dies ist mindestens sehr unwahrscheinlich ...
Ohne ein MCVE (inkl. Compiler-Version, Zielplattform und Optionen) kann
dem TO einfach nicht geholfen werden. Wenn er das nicht einsieht, wird
er keine sinnvolle Hilfe bekommen. Er hat dann zwar einen Workaround
gebastelt, versteht aber die Ursache immer noch nicht und hat bei der
nä. Gelegenheit dasselbe Problem ...
Unlogiker schrieb:> Das volatile hat in allen Fällen den einzigen Zweck, daß man im Debugger> etwas sieht. Ansonsten bleibt von der Schleife wenig übrig.
Wie siehts denn im Debugger aus, wenn Du im EmBitz die Debug-Version
statt Release-Version aktivierst, damit keine Optimierung mehr
vorgenommen wird? In diesem Fall könntest Du nämlich diese ganzen
volatile-Definitionen weglassen und kannst trotzdem debuggen.
Desweiteren würde mich die Ausgabe von tmp mal auf dem UART
interessieren, ob da auch 12 für tmp statt 1 ausgegeben wird. Es könnte
nämlich auch eine Macke des Debuggers sein, der mit der optimierten
Version nicht zurechtkommt. Der Optimizer geht nämlich manchmal derart
verschlungene Wege, der nichts mehr mit dem Quellcode 1:1 zu tun hat.
Zum Beispiel können ganze Reihenfolgen von Statements umgekrempelt
werden. Damit ist auch ein Breakpoint in den optimierten Code komplett
unzuverlässig und Du kannst Dir überhaupt nicht sicher sein, ob er den
Inline-Code(!) von push_button_poll() schon ausgeführt hat oder nicht.
Deshalb: Wenn Du den Debugger benutzt, erstelle auch eine Debug-Version
und keine optimierte Version. Wenn Du optimierten Code testen willst,
baue Outputs auf einen UART ein und verzichte auf den Debugger.
Um mal Nägel mit Köpfen zu machen, ich habe das Beispiel mal so
ergänzt, dass es self-contained ist.
@Unlogiker: passt das so? Wenn nicht, bitte korrigiere es mal.
Compiliert mit:
Jörg W. schrieb:> @Unlogiker: passt das so? Wenn nicht, bitte korrigiere es mal.
io_setInputPU() löscht noch in MODER, aber das sollte hier keine
Auswirkungen haben.
Wenn jemand weiß, wie man in EmBitz die Compiler-Aufrufzeile exportiert
(ich habe gestern abend in der Doku um beim herumklicken nichts
gefunden), liefere ich die noch gerne nach.
Unlogiker schrieb:> Wenn jemand weiß, wie man in EmBitz die Compiler-Aufrufzeile exportiert
Naja, notfalls eine Programm mit dem Namen GCC.EXE schreiben, das nichts
anderes macht, als seine Kommandozeilenparameter in eine Textdatei zu
schubsen... komme ich aber vor heute abend nicht zu.
Kann es sein, dass du hier irgend ein Registerwert bekommst? Es ist
schon auffällig wenn es hier um einen GPIO_Pin5 geht und 2^5 = 32 ist
(Also Bit # 5 = true) ist.
Gruß
Unlogiker schrieb:> Ich hatte es oben etwas zusammengekürzt, um es übersichtlicher zu> machen, aber in der obigen Textdatei fehlen noch Optionen.
Nicht sehr tragisch, das meiste sind ja nur Warnungen. -O1 statt -Os
ist der einzige relevante Unterschied, den ich erstmal sehe.
Hier das, was der GCC (auch bei mir ein 5.4.1) daraus generiert.
Die entscheidende Stelle ist hier:
1
ldr r2, [sp, #4]
2
subs r2, r2, #1
3
cmp r2, #7
4
bhi .L13
5
tbb [pc, r2]
6
.L5:
7
.byte (.L4-.L5)/2
8
.byte (.L6-.L5)/2
9
.byte (.L13-.L5)/2
10
.byte (.L7-.L5)/2
11
.byte (.L13-.L5)/2
12
.byte (.L13-.L5)/2
13
.byte (.L13-.L5)/2
14
.byte (.L8-.L5)/2
15
.p2align 1
16
.L4:
17
ldr r2, [r0, #16]
18
ubfx r2, r2, #1, #1
19
b .L3
20
.L6:
21
ldr r2, [r0, #16]
22
ubfx r2, r2, #4, #1
23
b .L3
24
.L7:
25
ldr r2, [r0, #16]
26
ubfx r2, r2, #3, #1
27
b .L3
28
.L8:
29
ldr r2, [r0, #16]
30
ubfx r2, r2, #7, #1
31
b .L3
32
.L13:
33
mov r2, lr
34
.L3:
35
str r2, [sp, #8]
36
ldr r7, [sp, #8]
37
ldr r2, [r3, #4]
38
orrs r2, r2, r7
39
str r2, [r3, #4]
Der Bereich vom Anfang des Zitats bis .L3 entspricht getbutton().
In r0 ist die Adresse von GPIOA. Die Zählvariable i befindet sich,
durch das "volatile" erzwungen, in SP+4. Sie wird am Anfang in r2
geladen und um 1 verringert, um damit in die Sprungtabelle .L5 zu
gehen. Diese hat nur sinnvolle Einträge für 0, 1, 3 und 7; die
Einträge 2, 4, 5 und 6 zeigen auf .L13 hinter die Tabelle.
Die Tabelleneinträge sind die Entscheidungseinträge für die IO-Logik.
Sie gehen jeweils auf GPIOA->IDR ([r0, #16]) und kopieren danach
ein benanntes Bitfeld (ich hatte sie recht willkürlich mit 1, 4, 3 und
7 festgelegt) in Bit 0 von r2. Bei .L3 wird dieses Bit nach [sp,#8]
in "tmp" geschrieben (wiederum durch "volatile" erzwungen), danach
neu ausgelesen und als Verschiebezahl benutzt, um das entsprechende
Bit in [r3, #4] ("bitmerker") zu setzen.
Sieht mir alles OK aus, entspricht aber nicht dem, was du oben als
Assemblercode stehen hast.
Ich habe den angepassten Code von Jörg mal mit dem GCC 7.2.0 und -O1
kompiliert und kann im Assembleroutput nichts Verdächtiges entdecken:
1
movs r4, #0
2
mov ip, r4 ; ip=0
3
...
4
5
.L4:
6
ldr r2, [r0, #16] ; IO-Register lesen
7
ubfx r2, r2, #1, #1 ; Bitfeld an Bit 1 mir Breite 1 extrahieren
8
; Ergebnis kann nur 0 oder 1 sein
9
.L3:
10
str r2, [sp, #8] ; tmp speichern (wegen volatile
11
ldr r7, [sp, #8] ; und wieder laden
12
ldr r2, [r3, #4] ; bitmerker laden
13
orrs r2, r2, r7 ; verodern
14
str r2, [r3, #4] ; bitmerker speichern
15
16
...
17
18
; dasselbe für die drei anderen Tasten:
19
.L6:
20
ldr r2, [r0, #16]
21
ubfx r2, r2, #4, #1
22
b .L3
23
.L7:
24
ldr r2, [r0, #16]
25
ubfx r2, r2, #3, #1
26
b .L3
27
.L8:
28
ldr r2, [r0, #16]
29
ubfx r2, r2, #7, #1
30
b .L3
31
.L12:
32
mov r2, ip ; Defaultwert 0 (keine gültige Taste)
33
b .L3
34
35
...
Der mit bitmerker veroderte Wert kann also nur 0 oder 1 sein, es sei
denn, ich habe etwas Wichtiges übersehen.
Da du einen viel älteren Compiler verwendest, kann bei dir das Ergebnis
natürlich anders aussehen. Poste doch mal den für pushbutton.c erzeugten
Assemblercode (nicht als Ausschnitt wie in deinem Debugger-Screenshot,
sondern komplett) oder alternativ die Objektdatei pushbutton.o.
Der komplette Assembleroutput befindet sich im Anhang.
Edit:
Jörg war schneller, aber zum Glück mit sehr ähnlichem Ergebnis :)
Frank M. schrieb:> Es könnte> nämlich auch eine Macke des Debuggers sein
Das kann sein.
Man sollte daher immer auch dazu sagen, ob der Fehler in echt auftritt
oder nur im Debugger.
Ohne diese Angabe gehe ich immer davon aus, daß ein Fehler in echt
aufgetreten ist.
Weil einen Debugger benutze ich immer erst, wenn die Applikation nicht
das Gewünschte macht und ich den Fehler nicht finde.
Ich kann einen Fehler des Debuggers genausowenig ausschließen wie einen
eigenen Fehler.
Wie ich schon oben schrieb, hat mir der eingangs erwähnte-Bool-Fehler es
insbesondere erschwert, den zweiten Fehler (Zaunpfahlproblem) zu
entdecken.
Heute abend schaue ich mal, wie es beim printf-Debugging aussieht.
Unlogiker schrieb:> den zweiten Fehler (Zaunpfahlproblem) zu> entdecken.
Das hatte ich mir doch gedacht, es liegt gar nicht am stdbool.h.
Du hast einfach zuviele Änderungen gleichzeitig gemacht und dann den
falschen verdächtigt. Gib es zu.
Peter D. schrieb:> Gib es zu.
Peter, auch hier gilt bis zum Beweis des Gegenteils die
Unschuldsvermutung. Wir sind nicht bei der spanischen Inquisition …
Peter D. schrieb:> Du hast einfach zuviele Änderungen gleichzeitig gemacht und dann den> falschen verdächtigt.
Verdächtigt habe ich grundsätzlich erst einmal jeden. So funktioniert
Fehlersuche.
Fakt ist: Im Debugger wurde "tmp" je nach Pinbelegungg als 12 oder 32
angezeigt.
Anbei das *.s-File. Um Mißverständnisse zu vermeiden, habe ich das
C-File, aus dem es kompiliert wurde, auch noch einmal angehängt.
du gibts kein bool zurück, du gibts einen unsigned 32 Bit Wert zurück.
Nämlich dein unsigned 32bit bitmask. Das wird dann ausgewertet mit != 0.
Es kann niemals nur 0 oder 1 zurückkommen von der Funktion bar.
Wenn PeDa ein Patent halten würde auf das Layout seines 2bit
8(/16/32)-Fach Parallelzählers, dann könnte ich es verstehen, wenn man
versucht die Bits anders zu layouten und statt binär zu zählen, vier
einzelne Bits nach der vier Finger-Methode aufreiht. Da aber die
Ausgangslagen eine andere ist und das ganze weder unkompliziert, noch
aktuell Insektenfrei zu sein scheint:
Warum?
Ja, so weit habe ich es auch inzwischen entziffert. Die Werte in tmp
waren also tatsächlich Artefakte, die vom Debugger ausgelesen wurden.
Printf liefert auch immer 1 oder 0.
Carl D. schrieb:> Da aber die> Ausgangslagen eine andere ist und das ganze weder unkompliziert, noch> aktuell Insektenfrei zu sein scheint:> Warum?
Es wurde am Montagabend geschrieben, Montagabend alle Fehler
herausgefunden und es funktioniert.
Als Nebenprodukt habe ich etwas über Artefakte beim Debugging, über
einen Online-C-Interpreter und über die Definitionen von "bool" in
verschiedenen Versionen von C gelernt. Also: Warum nicht?
Unlogiker schrieb:> Carl D. schrieb:>> Da aber die>> Ausgangslagen eine andere ist und das ganze weder unkompliziert, noch>> aktuell Insektenfrei zu sein scheint:>> Warum?>> Es wurde am Montagabend geschrieben, Montagabend alle Fehler> herausgefunden und es funktioniert.>> Als Nebenprodukt habe ich etwas über Artefakte beim Debugging, über> einen Online-C-Interpreter und über die Definitionen von "bool" in> verschiedenen Versionen von C gelernt. Also: Warum nicht?
Und nicht zu vergessen, daß man nur "Compilerfehler" schreien sollte,
wenn man den auch vorzeigen kann ;-)
Carl D. schrieb:> Und nicht zu vergessen, daß man nur "Compilerfehler" schreien sollte,> wenn man den auch vorzeigen kann ;-)
Wobei -wenn ich nicht den Überblick verloren habe- auch niemand einen
Compilerfehler unterstellt hat.
Unlogiker schrieb:> Carl D. schrieb:>> Und nicht zu vergessen, daß man nur "Compilerfehler" schreien sollte,>> wenn man den auch vorzeigen kann ;-)>> Wobei -wenn ich nicht den Überblick verloren habe- auch niemand einen> Compilerfehler unterstellt hat.
Indirekt schon.
Unlogiker schrieb:> Wie kommt der Compiler darauf, daß !true ausgerechnet 32 ist? 42 hätte> ich beinahe noch verstanden.
Unlogiker schrieb:> Wobei -wenn ich nicht den Überblick verloren habe- auch niemand einen> Compilerfehler unterstellt hat.
Indirekt hast du es, indem du behauptet hast, dass der Rückkehrwert
einer "bool" deklarierten Funktion andere Werte angenommen hätte.
Tja, Debuggen von Code auf Plattformen, die ein paar Register mehr
frei haben als der leidige x86, kann manchmal Überraschungen
beinhalten. Zwar wird eine volatile-Variable vom Compiler natürlich
honoriert, aber eine C-Codezeile generiert u. U. an verschiedenen
Stellen Assemblercode. Damit ist das Setzen eines Breakpoints
bezogen auf eine C-Zeile dann nicht mehr völlig eindeutig, und es kann
gut sein, dass die Variable an der Stelle, wo der Debugger anhält, doch
einen anderen Wert hat.
Ich habe mir übrigens angewöhnt, fürs Debuggen globale
volatile-Variablen
einzufügen, die auch wirklich nur einmal den Wert zugewiesen bekommen.
In deinem Falle hast du ja eine normale Variable volatile gesetzt und
sie anschließend wieder rückgelesen. Globale Variable sind dahingehend
vorteilhaft, dass sie dem Compiler weniger Freiheiten für das
Umsortieren
von Code lassen, denn er kann nicht wissen, ob wer von woanders noch
drauf zugreift (bspw. vom Debugger :).
So ganz passt dein Assemblerlisting jedoch nach wie vor nicht zu dem
Stück Disassembler, welches du oben in der GUI zeigst.
1
cmp r2, #15 cmp r2, #15
2
bls .L11 bls.n 0x8002a02
3
cbz r4, .L2 cbz r4, 0x8002a84
4
ldr r3, .L16 ldr r3, [pc, #8]
5
str r1, [r3] str r1, [r3, #0]
Bis hierhin sind beide gleich. Das Stück hier gibt's nur in
deinem Screenshot:
1
ldr r2, [sp, #4]
2
str r2, [sp, #8]
Hier gibt es kleinere Änderungen:
1
ldr r3, .L16 ldr r3, [pc, #148]
2
ldr r7, [sp, #8]
3
ldr r0, [r3] ldr r2, [r3, #0]
4
orrs r2, r7
5
str r3, [r3, #0]
Irgendwas hast du also offenbar zwischenzeitlich geändert. Damit
ist gerade der dich zur Konfusion getriebene Wert für "tmp" so nicht
mehr im aktuellen Code enthalten und lässt sich nicht klären.
Generell schadet es nicht, wenn man den Assemblercode zumindest
einigermaßen verstehen lernt und nachvollziehen kann. Dann ist es
im Zweifelsfalle einfacher, die Werte in den CPU-Registern im
Debugger nachzuvollziehen, statt noch X weitere Hilfsvariablen
anzulegen, um irgendein Problem einzukreisen.
Jörg W. schrieb:> Indirekt hast du es, indem du behauptet hast, dass der Rückkehrwert> einer "bool" deklarierten Funktion andere Werte angenommen hätte.
Nicht unbedingt. Am Anfang stand durchaus die Möglichkeit im Raum, daß
ein anderer Wert für Bool als 0 und 1 erlaubt sein könnte.
Ich habe mich ein paar Jahren mit Optimierung beschäftigt. Meine
Erfahrungen mit Optimierern, Compilern, Softwerkern und kleinen Kindern,
die ihr Zimmer aufräumen sollen sind:
Sie halten das durch Gleichungen, Quelltexte oder Anweisungen
vorgegebene Regelwerk genau ein, entwickeln aber jede Menge Phantasie
darin, trotzdem etwas komplett anderes zu machen als das, was
beabsichtigt war.
Sprich: Mein erster Gedanke war, daß der Compiler den Standard mal
wieder auf eine spitzfinde Art und Weise auslegt, an die ich noch nicht
gedacht habe.
Unlogiker schrieb:>> Ich habe mich ein paar Jahren mit Optimierung beschäftigt. Meine> Erfahrungen mit Optimierern, Compilern, Softwerkern und kleinen Kindern,> die ihr Zimmer aufräumen sollen sind:> Sie halten das durch Gleichungen, Quelltexte oder Anweisungen> vorgegebene Regelwerk genau ein, entwickeln aber jede Menge Phantasie> darin, trotzdem etwas komplett anderes zu machen als das, was> beabsichtigt war.
Gegenthese:
Sie machen immer genau das, was man von ihnen verlangt,
nur lassen viele Arbeitsanweisungen zu wünschen übrig.
Nicht klar beschrieben, was das Ergebnis sein soll und/oder zur Hälfte
vor-compiliert und damit den Handlungsspielraum des Werkzeuges zu stark
einschränkend.
Da schließ ich mich gar nicht aus. Oft stell ich fest, daß
Spitzfindigkeiten des Compilers auf unscharfer Zielvorgabe beruhen.
Immer alle Warnungen an und diese schön brav lesen.
Carl D. schrieb:> Sie machen immer genau das, was man von ihnen verlangt,> nur lassen viele Arbeitsanweisungen zu wünschen übrig.
Genau wie die kleine Tochter, die alle Legosteine mit Kaugummi an die
Wand geklebt hat. "Du hast gesagt, ich soll den Boden aufräumen!"
Unlogiker schrieb:> Jörg W. schrieb:>> Indirekt hast du es, indem du behauptet hast, dass der Rückkehrwert>> einer "bool" deklarierten Funktion andere Werte angenommen hätte.>> Nicht unbedingt. Am Anfang stand durchaus die Möglichkeit im Raum, daß> ein anderer Wert für Bool als 0 und 1 erlaubt sein könnte.>> Ich habe mich ein paar Jahren mit Optimierung beschäftigt. Meine> Erfahrungen mit Optimierern, Compilern, Softwerkern und kleinen Kindern,> die ihr Zimmer aufräumen sollen sind:> Sie halten das durch Gleichungen, Quelltexte oder Anweisungen> vorgegebene Regelwerk genau ein, entwickeln aber jede Menge Phantasie> darin, trotzdem etwas komplett anderes zu machen als das, was> beabsichtigt war.>> Sprich: Mein erster Gedanke war, daß der Compiler den Standard mal> wieder auf eine spitzfinde Art und Weise auslegt, an die ich noch nicht> gedacht habe.
Naja. Offen gesagt, Deine Reaktion ist unbefriedigend - sie enthält
immer noch, wenn auch nicht ausdrücklich aber im Subtext, den unbelegten
Vorwurf, dass hier etwas anderes als definiert, geschieht.
Tatsache bleibt, dass Du für den ersten Code immer noch nicht den
Assemblertext gezeigt hast und alle Versuche von Dritten, anhand dieses
Codes den Fehler nachzvollziehen, gescheitert sind.
Tatsache bleibt auch, dass der Standard in der Hinsicht, in der Du die
Frage aufgeworfen hast, keinerlei Interpretationsspielraum offen lässt.
Auch an dem zweite Code von Dir liess sich der Fehler nicht nachweisen.
Was bleibt, sind vielleicht verständliche Irrtümmer. Aber auch das, -
entschuldige den Ausdruck - sture Beharren darauf, das "irgendwas nicht
gestimmt hat" unter Rückzug auf immer diffusere Argumente oder
Einzelfallbehauptungen die dann spätestens beim Kind, dass sein Zimmer
anders aufräumt als "man" aufräumen versteht, nichts mehr mit einer
Ursachenforschung zu tun haben, sondern mit, - eine andere Möglickeit
halte ich nicht für plausibel -, Gesichtswahrung. Dafür aber, dass ich
nun knapp 20 Leute damit beschöftigt haben, einen Compilerfehler
aufzuspüren, und die Antworter teilweise der Inkompetenz zu beschuldigen
fehlt für mich dann aber ein eindeutiges: "Tut mir leid. Mein Fehler".
Wobei wegen des fehlenden Assemblercodes noch eine Möglichkeit - aber
wohl eher theoretischer Natur - besteht, dass tatsächlich ein
Compilerfehler vorliegt.
Dafür sprechend fällt mir eine Sache besonders auf: Das sich durch viele
Beiträge ziehende Motiv des Misstrauens. Auch in dem hier zitierten
Beitrag. Insbesondere aber auch, der in dem zweiten Screenshot gezeigte
Codeteil, den ich hier mal abgetippt habe.
1
...
2
if((bitmerker&mask)==mask){
3
...
4
}
5
elseif((~bitmerker&mask)==mask){
6
...
7
}
8
...
Das ist, - wir mir scheint ziemlich offensichtlich und bestätigt von
anderen Äusserungen in diesem Thread -, geleitet von der Furcht, es
ergäbe sich nicht zwingend aus der boolschen Logik, dass das Gegenteil
von 0 auch tatsächlich und immer 1 ist und man müsste das ausdrücklich
hinschreiben, damit der Compiler das auch wirklich merkt - verflixt noch
mal, sonst macht er wieder irgendwas Abseitiges. :-)
Irgendwie sind die ganze Zeit alle anderen Schuld. Der Compiler, der
Standard, ein online-Interpreter etcpp.
Ich möchte betonen, dass es mir nicht um ein abwertendes Urteil über die
Person geht, sondern um die Feststellung, welche Mechanismen zu dem
Fehlurteil geführt haben. Auch wenn es mich persönlich nicht unmittelbar
betrifft, halte ich es doch für erstrebenswert, sich über seinen eigenen
Denkprozesse klarzuwerden, einfach weil ich mit dem TO in der selben
Welt lebe und auf sein Urteilsvermögen zumindest potentiell angewiesen
bin - so wie er auf meines.
Im übrigen erspart man sich damit lange Threads wie diese, wo die
Antworter sich irgendwann veranlasst sehen, negative Belege erbringen zu
müssen, weil der Fragesteller keine positiven Beweise erbringt.
Es gibt übrigens eine, wenn auch fiktive, Parallele zu dem vorliegenden
Fall. Den Prüfer der Computerprogramme für die Landung von Raumschiffen
namens Cornelius bei Stanislav Lem in der Geschichte "Ananke" z.B. in
dem Suhrkamp Taschenbuch "Die Jagd".
Der hat das Mißtrauen so weit getrieben, dass er Computer in den
Wahnsinn getrieben hat. Nur das der TO im eigentlichen Sinne sich selbst
ein Bein gestellt hat.
Wohlgemerkt: Es handelt sich bei dieser Bemerkung um eine Anekdote -
kein Argument.
Unlogiker schrieb:> Ja, so weit habe ich es auch inzwischen entziffert. Die Werte in tmp> waren also tatsächlich Artefakte, die vom Debugger ausgelesen wurden.> Printf liefert auch immer 1 oder 0.Frank M. schrieb:> Der Optimizer geht nämlich manchmal derart verschlungene Wege, der> nichts mehr mit dem Quellcode 1:1 zu tun hat. Zum Beispiel können ganze> Reihenfolgen von Statements umgekrempelt werden. Damit ist auch ein> Breakpoint in den optimierten Code komplett unzuverlässig und Du kannst> Dir überhaupt nicht sicher sein, ob er den Inline-Code(!) von> push_button_poll() schon ausgeführt hat oder nicht.>> Deshalb: Wenn Du den Debugger benutzt, erstelle auch eine Debug-Version> und keine optimierte Version. Wenn Du optimierten Code testen willst,> baue Outputs auf einen UART ein und verzichte auf den Debugger.
Ich rate Dir einfach, den letzten Abschnitt aus meinem Beitrag vom
Vormittag zukünftig zu beherzigen. Optimierten Code zu debuggen ist ein
No-Go. Das kannst Du auch nicht dem Debugger anlasten. Der kann nichts
dafür, dass der Optimizer den Code derart umkrempelt, dass Breakpoints
auf C-Codezeilen zum Glücksspiel werden.
Frank M. schrieb:> Optimierten Code zu debuggen ist ein No-Go.
Um hier mal ein wenig Spannung in die Diskussion zu bringen :-)
Das sehe ich diametral entgegengesetzt. OK, zumindest ab dem Punkt,
da man sich erstmal prinzipiell in C auskennt und nicht nur die
einfachen Anfängerfehler macht. Ohne Optimierung debuggt man einen
völlig anderen Code als mit eingeschalteter Optimierung. Wenn der
Code und die Laufzeit überhaupt noch passen, debuggt man am Ende
trotzdem zweimal: erstmal die „Pipi“-Fehler (10 % der Zeit), danach
dann das, was man wirklich vergurkt hat (wie ein vergessenes
„volatile“), 90 % der Zeit. Kann man den ersten Schritt gleich
weglassen, sich an die Ungereimtheiten gewöhnen, die das Debuggen
eines hochgradig optimierten Codes hervor bringen, und dann zum
zweiten Schritt übergehen.
Ich will ja mal nicht behaupten, dass ich noch nie mit -O0 compiliert
hätte, aber jegliches einigermaßen ernsthafte Projekt habe ich mit
aktivierter Optimierung debuggt.
Jörg W. schrieb:> Ich will ja mal nicht behaupten, dass ich noch nie mit -O0 compiliert> hätte, aber jegliches einigermaßen ernsthafte Projekt habe ich mit> aktivierter Optimierung debuggt.
Bei VisualStudio/-C hättest du dir das aber gaaanz schnell abgewöhnt...
;)
Theor schrieb:> else if ((~bitmerker & mask) == mask)
Nein. Dieser Code macht etwas völlig anderes, als es
1
elseif((~bitmerker&mask)
machen würde.
Jetzt könnte ich Dir unterstellen, daß Dein Diskussionsstil durch ein
generell oberflächiges Lesen des Beitrages und ein generelles Mißtrauen
gegenüber den hinter Quelltext steckenden Absichten geprägt ist und ein
daraus resultierendes Bedürfnis, lange Texte zu schreiben
interpretieren.
Frank M. schrieb:> Optimierten Code zu debuggen ist ein> No-Go.
Dazu gibt es geteilte Meinungen. In einem anderen Thread wurde mir dazu
geraten, mindestens -O1 beim Debuggen zu nutzen. :-) Ich mache das so,
daß beim Release-Build die Debug-Option feststeht (-O2 oder -Os) und ich
im Debug-Build nach Bedarf umstelle.
Beim vorliegenden Fall beispielsweise hätte ich an anderer Stelle ohne
Inlining keinerlei Chance mehr, das Timing überhaupt einzuhalten.
Man kann auch einfach die dafür vorgesehene Option
-Og
benutzen, da wird soweit optimiert, wie die Struktur Debugger-vertäglich
möglich, ohne den Code-Wust von -O0 zu hinterlassen.
Wobei ich persönlich Dinge die Timing erforder, eher mit -O(2..3) und
einem LA debugge.
https://gcc.gnu.org/onlinedocs/gcc/Optimize-Options.html
-Og Optimize debugging experience. -Og enables optimizations that do not
interfere with debugging. It should be the optimization level of choice
for the standard edit-compile-debug cycle, offering a reasonable level
of optimization while maintaining fast compilation and a good debugging
experience.