Forum: Mikrocontroller und Digitale Elektronik Böse Falle Boolean


von Unlogiker (Gast)


Lesenswert?

Moinsen,

Ich habe gestern abend recht lange nach einem Fehler gesucht:
1
#include <stdbool.h>
2
#include <stdint.h>
3
4
static inline bool foo(void)
5
{
6
     return (GPIOA->IDR & GPIO_Pin_5) != 0;
7
}
8
9
uin32_t bar(void)
10
{
11
    static uint32_t bitmask = 0;
12
    for( int i = 0; i<8; i++ )
13
    {
14
        bitmask |= foo();
15
        bitmask <<= 1;
16
    }
17
    return bitmask;
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.

von Dr. Sommer (Gast)


Lesenswert?

true und false sind natürlich 1 und 0. Da ist irgendwas anderes falsch. 
Es geht um C, ja?

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

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
static inline bool foo(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
    static uint32_t bitmask = 0;
2
    :
3
        bitmask |= foo();
Auch das ist impliziter Murks, weil du annimmst, dass true=1 und 
false=0 ist.

: Bearbeitet durch Moderator
von Dr. Sommer (Gast)


Lesenswert?

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

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

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.

: Bearbeitet durch Moderator
von Dumdi D. (dumdidum)


Lesenswert?

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.

von Yalu X. (yalu) (Moderator)


Lesenswert?

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.

: Bearbeitet durch Moderator
von Dumdi D. (dumdidum)


Lesenswert?

https://stackoverflow.com/questions/3661751/what-is-0-in-c
Sieht so aus als ob !!x garantiert 0 oder 1 zurueck gibt. (Und viele 
Schreiber denken das !=0 das auch tur, vermutlich kuesstest du !=0 
zweimal schreiben)

von Unlogiker (Gast)


Lesenswert?

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.

von Unlogiker (Gast)


Lesenswert?

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.

von Wilhelm M. (wimalopaan)


Lesenswert?

Unlogiker schrieb:
> Moinsen,
>
> Ich habe gestern abend recht lange nach einem Fehler gesucht:
>
1
> uin32_t bar(void)
2
> {
3
>     static uint32_t bitmask = 0;
4
>     for( int i = 0; i<8; i++ )
5
>     {
6
>         bitmask |= foo();
7
>         bitmask <<= 1;
8
>     }
9
>     return bitmask;
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).

von Wilhelm M. (wimalopaan)


Lesenswert?

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

von Mampf unterwegs (Gast)


Lesenswert?

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

von Unlogiker (Gast)


Angehängte Dateien:

Lesenswert?

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.

von Wilhelm M. (wimalopaan)


Lesenswert?

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.

von Mampf unterwegs (Gast)


Lesenswert?

ui und das static bitmask ist ein böser Fehler! Das wird exakt ein 
einziges mal mit 0 initialisiert und im nächsten Aufruf behält es den 
alten Wert!

von Stefan F. (Gast)


Lesenswert?

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

von Wilhelm M. (wimalopaan)


Lesenswert?

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!

: Bearbeitet durch User
von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

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:
1
static inline bool foo(void)
2
{
3
     return (GPIOA->IDR & GPIO_Pin_5) != 0;
4
}
5
6
uin32_t bar(void)
7
{
8
    static uint32_t bitmask = 0;
9
    for( int i = 0; i<8; i++ )
10
    {
11
        if (foo())
12
            bitmask |= 1;
13
        bitmask <<= 1;
14
    }
15
    return bitmask;
16
}

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

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.

: Bearbeitet durch Moderator
von Wilhelm M. (wimalopaan)


Lesenswert?

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.

von Mampf unterwegs (Gast)


Lesenswert?

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.

von Unlogiker (Gast)


Lesenswert?

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.

von Kaj (Gast)


Lesenswert?

Unlogiker schrieb:
> Als Lösung mußte dann ein Trigraph herhalten:
Ein Trigraph ist was ganz anderes :)

https://de.wikipedia.org/wiki/Trigraph

von Wilhelm M. (wimalopaan)


Lesenswert?

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.

von Wilhelm M. (wimalopaan)


Lesenswert?

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

: Bearbeitet durch User
von Unlogiker (Gast)


Lesenswert?

Kaj schrieb:
> Ein Trigraph ist was ganz anderes :)

Ja, es ist gestern spät geworden. Der ternäre Operator gehört nicht zu 
meinem aktiven Wortschatz.

von Unlogiker (Gast)


Lesenswert?

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.

Beitrag #5200645 wurde von einem Moderator gelöscht.
von Wilhelm M. (wimalopaan)


Lesenswert?

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!

: Bearbeitet durch User
von Peter D. (peda)


Lesenswert?

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
bool foo( char i )
4
{
5
  8e:  81 11         cpse  r24, r1
6
  90:  81 e0         ldi  r24, 0x01  ; 1
7
  return i;
8
}
9
  92:  08 95         ret

Wie ja schon gesagt wurde, ist die static Variable der Fehler.

Beitrag #5200657 wurde vom Autor gelöscht.
von Unlogiker (Gast)


Lesenswert?

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.

von Yalu X. (yalu) (Moderator)


Lesenswert?

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
bool b = 123;

wird b nicht mit 123, sondern mit 1 initialisiert, da der zugewiesene
Wert implizit in den Wertebereich {0, 1} konvertiert wird.

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

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

Was sagst Du jetzt?

von Wilhelm M. (wimalopaan)


Lesenswert?

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.

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

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?

Beitrag #5200683 wurde von einem Moderator gelöscht.
von Yalu X. (yalu) (Moderator)


Lesenswert?

Wilhelm M. schrieb:
> Also: bool als tatsächlich Bool'sch zu betrachten, ist eine GCC
> Erweiterung.

Nein, das ist C nach ISO-Norm:

1
6.2.5 Types
2
...
3
An object declared as type _Bool is large enough to store the values 0
4
and 1.
5
...
6
7.18 Boolean type and values <stdbool.h>
7
...
8
The macro
9
               bool
10
expands to _Bool.

: Bearbeitet durch Moderator
von (prx) A. K. (prx)


Lesenswert?

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.

: Bearbeitet durch User
von Dirk (Gast)


Lesenswert?

Versuch mal eine Klammer herum zu machen:
1
return ((GPIOA->IDR & GPIO_Pin_5) != 0);

von (prx) A. K. (prx)


Lesenswert?

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.

: Bearbeitet durch User
von Gästle (Gast)


Lesenswert?

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.

von Unlogiker (Gast)


Lesenswert?

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...
1
    static_always_inline uint16_t io_bitIsSet(GPIO_t * const GPIOx, const IOPin_t Pin)
2
    {
3
        return GPIOx->IDR & Pin;
4
    }

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.

von (prx) A. K. (prx)


Lesenswert?

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.

: Bearbeitet durch User
von Walter T. (nicolas)


Lesenswert?

Dirk schrieb:
> Versuch mal eine Klammer herum zu machen:

Der Ausdruck ist auch ohne Klammer ein eindeutiger rvalue.

von Yalu X. (yalu) (Moderator)


Lesenswert?

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.

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

Wilhelm M. schrieb:
> Also: bool als tatsächlich Bool'sch zu betrachten, ist eine GCC
> Erweiterung.
Augenscheinlich nicht:  http://codepad.org/l51Hk0U8

von Dirk B. (dirkb2)


Lesenswert?

Du kannst auch doppelt negieren:
1
static inline bool foo(void)
2
{
3
     return !!(GPIOA->IDR & GPIO_Pin_5);
4
}

von Wilhelm M. (wimalopaan)


Lesenswert?

Lothar M. schrieb:
> Wilhelm M. schrieb:
>> Also: bool als tatsächlich Bool'sch zu betrachten, ist eine GCC
>> Erweiterung.
> Augenscheinlich nicht:  http://codepad.org/l51Hk0U8

Ja, da hast Du Recht seit C99:

http://en.cppreference.com/w/c/types/boolean

Sorry, bei mir hat es sich so fest im Hirn eingebrannt, dass bool in C 
ein no-go ist ... Asche auf mein Haupt!

Beitrag #5200804 wurde von einem Moderator gelöscht.
Beitrag #5200834 wurde vom Autor gelöscht.
von Unlogiker (Gast)


Angehängte Dateien:

Lesenswert?

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]

von Unlogiker (Gast)


Angehängte Dateien:

Lesenswert?

Scheinbar kann man als Gast nur eine Datei anhängen.

von Unlogiker (Gast)


Angehängte Dateien:

Lesenswert?

Warum schreibt keiner etwas? Das Fenster war im Screenshot ja wirklich 
denkbar ungünstig gescrollt... fällt mir erst jetzt auf.

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

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?

: Bearbeitet durch Moderator
von Peter D. (peda)


Lesenswert?

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.

von Gerd E. (robberknight)


Lesenswert?

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.

von Unlogiker (Gast)


Lesenswert?

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.

von nada (Gast)


Lesenswert?

Sicher, dass das nicht eine optimierte Version ist, wo der Debugger 
einfach nur noch trash anzeigt?
1
08002A02  ldr r2, [sp, #4]
Das ist mir zu hoch - getbutton() vom Compiler schon gekillt und vor dem 
loop-compare in eine Stackvariable verfrachtet?

von A. S. (Gast)


Lesenswert?

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_t pushbutton_poll(void)
2
{
3
static uint_fast8_t out = 0;
4
static volatile uint32_t bitm = 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
    return out;
12
}
wobei das mit einzelnen Variablen flexibler wäre (bis zu 64 Tasten bei 
long long).
1
typedef unsigned char tTasten;
2
#define N_HIST 3
3
4
tTasten pushbutton_poll(void)
5
{
6
static tTasten out = 0;
7
static tTasten T[N_HIST+1]={};
8
tTasten told = T[0];
9
tTasten tnew = T[0];
10
11
    /* dirty trick, just saves 2 redundant LOC */
12
    T[N_HIST] = getbutton();
13
14
    for(int i = 0; i < N_HIST; i++)
15
    {
16
       T[i] = T[i+1]; 
17
       told|= T[i];
18
       tnew&= T[i];
19
    }
20
    out = (out&told)|tnew;
21
    return out;
22
}

von Unlogiker (Gast)


Lesenswert?

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

von Unlogiker (Gast)


Lesenswert?

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

von (prx) A. K. (prx)


Lesenswert?

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.

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

Unlogiker schrieb:
> Wobei zur Vermeidung eben das != 0 nicht ausreicht, sondern tatsächlich
> eine Bedingung (if oder ternärer Operator) zum Einsatz kommen muß.
Du hast den Link dort im 
Beitrag "Re: Böse Falle Boolean" und den Post vor 
diesem gesehen?

Oder sieh dir mal die an:
http://codepad.org/eITSn6gh
http://codepad.org/OJS3UxoY

von Mampf unterwegs (Gast)


Lesenswert?

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

von Mampf unterwegs (Gast)


Lesenswert?

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

von Theor (Gast)


Lesenswert?

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.

von Wilhelm M. (wimalopaan)


Lesenswert?

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.

von Theor (Gast)


Lesenswert?

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.

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


Lesenswert?

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
4
to 0; otherwise, the result is 1.

von Peter D. (peda)


Lesenswert?

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.

von Wilhelm M. (wimalopaan)


Lesenswert?

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

von Philipp Klaus K. (pkk)


Lesenswert?

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

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


Lesenswert?

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.

von Wilhelm M. (wimalopaan)


Lesenswert?

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

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

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.

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


Angehängte Dateien:

Lesenswert?

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:
1
arm-none-eabi-gcc -Os -S -std=c99 -mcpu=cortex-m4 -mthumb -DSTM32F446xx -I /path/to/CMSIS/Include -I /path/to/Device/ST/STM32F4xx/Include/ pushbutton.c

Passt das auch?

von Unlogiker (Gast)


Angehängte Dateien:

Lesenswert?

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.

von Unlogiker (Gast)


Angehängte Dateien:

Lesenswert?

Ich hatte es oben etwas zusammengekürzt, um es übersichtlicher zu 
machen, aber in der obigen Textdatei fehlen noch Optionen.

von Unlogiker (Gast)


Lesenswert?

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.

von MoD (Gast)


Lesenswert?

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ß

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


Lesenswert?

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.

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


Angehängte Dateien:

Lesenswert?

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.

von Yalu X. (yalu) (Moderator)


Angehängte Dateien:

Lesenswert?

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

: Bearbeitet durch Moderator
von Peter D. (peda)


Lesenswert?

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.

von Unlogiker (Gast)


Lesenswert?

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.

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


Lesenswert?

Schick uns doch vor allem mal das Disassembly deiner Funktion.  Wenn
du nicht weißt, wie du das machst, kannst du auch das .o-File
schicken.

von Peter D. (peda)


Lesenswert?

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.

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


Lesenswert?

Peter D. schrieb:
> Gib es zu.

Peter, auch hier gilt bis zum Beweis des Gegenteils die
Unschuldsvermutung.  Wir sind nicht bei der spanischen Inquisition …

von Unlogiker (Gast)


Angehängte Dateien:

Lesenswert?

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.

von (prx) A. K. (prx)


Lesenswert?

Bloss falls da immer noch Missverständnisse sind: Die Funktion
1
uint_fast8_t pushbutton_poll(void)
2
{
3
    static uint_fast8_t out = 0;
4
    static volatile uint32_t bitmerker = 0; // Bit-Zustaende aller acht Pins hintereinander
5
    volatile uint32_t mask = genmask();
6
    for( volatile unsigned int i = 1; i < 1U<<N_PUSHBUTTON; i<<=1 )
7
    {
8
        volatile uint_fast8_t tmp = getbutton(i);
9
        bitmerker |= tmp;
10
        if( (bitmerker & mask) == mask )
11
        { // Alle vier vergangenen Abfragen war Pin gesetzt
12
            out |= i;
13
        }
14
        else if( (~bitmerker & mask) == mask )
15
        { // Alle vier vergangenen Abfragen war Pin nicht gesetzt
16
            out &= ~i;
17
        }
18
        bitmerker <<= 1;
19
    }
20
    return out;
21
}
entspricht bis auf die Sichbarkeit der Namen einer Funktion:
1
static uint_fast8_t out = 0;
2
static volatile uint32_t bitmerker = 0; // Bit-Zustaende aller acht Pins hintereinander
3
4
uint_fast8_t pushbutton_poll(void)
5
{
6
    volatile uint32_t mask = genmask();
7
    for( volatile unsigned int i = 1; i < 1U<<N_PUSHBUTTON; i<<=1 )
8
    {
9
        volatile uint_fast8_t tmp = getbutton(i);
10
        bitmerker |= tmp;
11
        if( (bitmerker & mask) == mask )
12
        { // Alle vier vergangenen Abfragen war Pin gesetzt
13
            out |= i;
14
        }
15
        else if( (~bitmerker & mask) == mask )
16
        { // Alle vier vergangenen Abfragen war Pin nicht gesetzt
17
            out &= ~i;
18
        }
19
        bitmerker <<= 1;
20
    }
21
    return out;
22
}

: Bearbeitet durch User
von Checker (Gast)


Lesenswert?

1
uin32_t bar(void)
2
{
3
    static uint32_t bitmask = 0;
4
    for( int i = 0; i<8; i++ )
5
    {
6
        bitmask |= foo();
7
        bitmask <<= 1;
8
    }
9
    return bitmask;
10
}

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.

von (prx) A. K. (prx)


Lesenswert?

Code von getbutton(2)
1
        ldr     r2, [r0, #16]     ;r2 = Portinhalt
2
        ubfx    r2, r2, #2, #1    ;r2.bit0 = r2.bit2, Rest auf 0
3
        b       .L3
4
.L3   : str     r2, [sp, #8]      ;tmp = r2
und von getbutton(4)
1
        ldr     r2, [r0, #16]     ;r2 = Portinhalt
2
        ubfx    r2, r2, #4, #1    ;r2.bit0 = r2.bit4, Rest auf 0
3
        b       .L3
4
.L3:    str     r2, [sp, #8]      ;tmp = r2

: Bearbeitet durch User
von Carl D. (jcw2)


Lesenswert?

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?

: Bearbeitet durch User
von Unlogiker (Gast)


Lesenswert?

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.

von Unlogiker (Gast)


Lesenswert?

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?

von Carl D. (jcw2)


Lesenswert?

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

von Unlogiker (Gast)


Lesenswert?

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.

von Carl D. (jcw2)


Lesenswert?

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.

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


Lesenswert?

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.

von Unlogiker (Gast)


Lesenswert?

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.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Unlogiker schrieb:
> Anbei das *.s-File.

> pushbutton.s (1 MB, 8 Downloads)

Hallo die Waldfee...

Mit -g0 anstatt -g3 wäre das etwas Foren-freundlicher.

von Unlogiker (Gast)


Lesenswert?

Johann L. schrieb:
> Mit -g0 anstatt -g3 wäre das etwas Foren-freundlicher.

Ohja, und besser lesbar ist es auch.

von Carl D. (jcw2)


Lesenswert?

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.

von Unlogiker (Gast)


Lesenswert?

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

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


Lesenswert?

Carl D. schrieb:

> Immer alle Warnungen an und diese schön brav lesen.

An Warnungs-Optionen mangelt's ihm ja wirklich nicht. ;-)

https://www.mikrocontroller.net/attachment/346723/compiler_options.txt

von Theor (Gast)


Lesenswert?

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
else if ((~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.

von Theor (Gast)


Lesenswert?

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.

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

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.

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


Lesenswert?

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.

von nada (Gast)


Lesenswert?

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

von Unlogiker (Gast)


Lesenswert?

Theor schrieb:
> else if ((~bitmerker & mask) == mask)

Nein. Dieser Code macht etwas völlig anderes, als es
1
else if ((~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.

von Carl D. (jcw2)


Lesenswert?

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.

von Unlogiker (Gast)


Lesenswert?

Carl D. schrieb:
> Man kann auch einfach die dafür vorgesehene Option
> -Og

Die habe ich im GNU manual gesucht und nicht gefunden. Wo finde ich das?

von nada (Gast)


Lesenswert?

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.

von Unlogiker (Gast)


Lesenswert?

Danke!

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.