Forum: Compiler & IDEs Merkwürdiges Verhalten des GCC


von HerbertK (Gast)


Lesenswert?

Hallo zusammen!

Teste gerade ein LCD-Menü für einen AVR (für AVR-GCC) vorab auf dem PC 
unter Linux (ebenfalls mit GCC). Die Menü-Daten liegen in einem Array 
die Größe des Array soll der Preprozessor dem #define size zuweisen. Bis 
hier funktioniert noch alles.
Die Größe size wird dann zum Vergleich für die Begrenzung der Variablen 
LCDstate benutzt. Das funktioniert nicht sauber, wenn der vom 
Preprozessor zugewiesene Wert zum Vergleich benutzt wird.

Wird in der Vergleichsfunktion dagegen eine manuell definierte Konstante 
oder eine Variable benutzt, funktioniert alles tadellos!

Hier der Codeausschnitt:
1
#define NR_ENTRIES(x)  (sizeof(x)/sizeof(*x))
2
#define size   NR_ENTRIES( mainMenu ), mainMenu
3
                     // size wird richtig zugewiesen mit 14
4
#define size1  14    // manuelle Zuweisung zum debug
5
6
uint8_t KEYstate=0;
7
8
int main (void) {
9
10
  while ((c = getch ()) != '\n') {
11
    if (KEYstate != KEY_CL){    // hat sich der Tastenstatus geändert?
12
13
      if (KEYstate == KEY_MINUS) {
14
        LCDstate--;
15
        if (LCDstate == 255) {LCDstate = size-1;}
16
                             // Vergleich erfolgt mit 14 anstatt 13
17
      }
18
      if (KEYstate == KEY_PLUS)  {
19
        LCDstate++; 
20
        if (LCDstate >= size) {LCDstate = 0;}
21
                             // LCDstate immer auf 0 bei der Plus-Taste
22
      }
23
24
      write(LCD,"%s", mainMenu[LCDstate].text);
25
      write(LCD, " LCDstate: %i, Size: %i",LCDstate, size); // debug
26
  }
27
}
Das Verhalten tritt nur auf, beim Vergleich die Größe size mit Zuweisung 
über #define size  NR_ENTRIES( mainMenu ), mainMenu und dass, obwohl die 
Zuweisung mit 14 richtig erfolgt. Folgende Änderungen im Code ergeben 
verschiedene Ergebnisse.

// Hier erhalte ich einen Vergleich mit 14 anstatt 13
if (LCDstate == 255) {LCDstate = size-1;}

// Hier erhalte ich einen Vergleich mit 13, richtig!
if (LCDstate == 255) {LCDstate = -1+size;}


// LCDstate ist danach immer 0 (size über Preprozessor zugewiesen)
if (LCDstate >= size) {LCDstate = 0;}

// die Funktion arbeitet richtig
if (LCDstate >= 14) {LCDstate = 0;}

// die Funktion arbeitet richtig (size1 manuell zugewiesen)
if (LCDstate >= size1) {LCDstate = 0;}

Dieses Verhalten kann ich mir nicht erklären. Könnte das ein Bug im GCC 
sein, oder habe ich hier einen Fehler der mir nicht einleuchten will?

: Bearbeitet durch User
von g457 (Gast)


Lesenswert?

> [..] #define size zuweisen. Bis hier funktioniert noch alles.

Nicht wirklich. Mach mal den Kommaoperator raus, der ist (nicht nur 
hier) böse.

von Bernd K. (prof7bit)


Lesenswert?

Ich sehe keine Deklaration für die Variable LCDstate, bitte poste alle 
relevanten Codeausschnitte, diese Variable ist relevant für das Problem, 
also ist es auch sinnvoll ihre Deklaration mal zu sehen. Oder bin ich 
blind?

Sowohl KEYstate alsd auch LCDState scheinen von Außerhalb der 
while-schleife manipuliert zu werden, evtl durch einen Interrupt. Wenn 
Du solche Variablen in einer while() auf Veränderung pollst dann denkt 
der Compiler sie können sich niemals ändern und optimiert sie kurzerhand 
weg!

Daher ist es zwingend daß Du alle Variablen mit denen Du dergleichen 
Dinge tust als volatile deklarierst!

: Bearbeitet durch User
von Tom (Gast)


Lesenswert?

>>#define size   NR_ENTRIES( mainMenu ), mainMenu
Warum baut man sich solche Fallen ein?

>>LCDstate = size-1;
wird damit besonders lustig.

von Rolf M. (rmagnus)


Lesenswert?

HerbertK schrieb:
> if (LCDstate == 255) {LCDstate = size-1;}
>                              // Vergleich erfolgt mit 14 anstatt 13

Machen wir einfach mal die Makro-Ersetzung:
1
 if (LCDstate == 255) {LCDstate = (sizeof(mainMenu)/sizeof(*mainMenu)), mainMenu-1;}

: Bearbeitet durch User
von HerbertK (Gast)


Lesenswert?

Vielen Dank für die Antworten!

@g457
Ich weiß leider nicht, welchen Kommaoperator du ansprichst.

@Bernd K.
Du bist nicht blind! Habe ich vergessen hinzuschreiben. Richtig ist:

uint8_t KEYstate=0, LCDstate=0;

Beide Variablen werden nicht ausserhalb der Schleife manipuliert. Zur 
Erfassung vom KEYstate liegt noch folgender Code innerhalb der Schleife, 
den ich zur Übersichtlichkeit weggelassen hatte, da es m. e. in meinem 
Fall nicht relevant ist.

    KEYstate = KEY_CL;
    switch (c) {
    case KEY_DOWN:
      KEYstate=KEY_RETURN;
    break;
    case KEY_UP:
      KEYstate=KEY_RETURN;
    break;
    case KEY_LEFT:
      KEYstate=KEY_MINUS;
    break;
    case KEY_RIGHT:
      KEYstate = KEY_PLUS;
    break
    }

Habe KEYstate und LCDstate jetzt als volatile uint8_t deklariert. Das 
hat aber keine Änderung ergeben.

@Tom
Tom schrieb:
> #define size   NR_ENTRIES( mainMenu ), mainMenu
> Warum baut man sich solche Fallen ein?
>
>>>LCDstate = size-1;
> wird damit besonders lustig.

Das verstehe ich nicht.
1. Es stammt nicht von mir sondern einen der Moderatoren hier und ich 
bin davon ausgeganen, dass der Code erprobt ist.
2. Der Preprozessor läuft doch vor dem Compiler. Bis der Compiler sein 
Werk beginnt, sollte es doch genau so sein wie ein

#define size  14

und das ist wie eine Konstante. Wieso ist also:

LCDstate = 14-1; besonders lustig und stellt eine Falle dar?

@Rolf Magnus
Habe beide Vergleiche durch Makros ersetzt.

if (LCDstate == 255) {LCDstate = (sizeof(mainMenu)/sizeof(*mainMenu)), 
mainMenu-1;}
liefert den falschen Vergleich mit 14

if (LCDstate >= (sizeof(mainMenu)/sizeof(*mainMenu)), mainMenu) 
{LCDstate = 0 ;}
liefert LCDstate 0

@Alle
Was mir vor allem nicht einleuchten will: warum funktioniert:

if (LCDstate == 255) {LCDstate = -1+size;} richtig und
if (LCDstate == 255) {LCDstate = size-1;} nicht richtig?

Wo ist da der Unterschied?

Warum funktioniert es mit der manuellen Zuweisung

#define size1  14

richtig, obwohl size ja auch richtig ermittelt wurde, was ich ja mit

write(LCD, " LCDstate: %i, Size: %i",LCDstate, size);

überprüfen kann?

von (prx) A. K. (prx)


Lesenswert?

HerbertK schrieb:
> if (LCDstate == 255) {LCDstate = -1+size;} richtig und

Führt zu LCDstate = mainMenu;

> if (LCDstate == 255) {LCDstate = size-1;} nicht richtig?

Führt zu LCDstate = mainMenu-1;

a = b,c-1 führt zu a = c-1, wobei von b nur Seiteneffekte bleiben.
a = -1+b,c führt zu a = c und das -1+ ist für den Allerwertesten.

Anders wärs bei a = (b,c)-1 vs a = -1+(b,c).
Was das eigentlich soll entzieht sich freilich meiner Erkenntnis.

: Bearbeitet durch User
von OldMan (Gast)


Lesenswert?

HerbertK schrieb:
> Warum funktioniert es mit der manuellen Zuweisung

Weil NUR Dein Makro NR_ENTRIES(x) in der Zuweisung angewendet wird und
das -1 auf MainMenu angewendet wird, dies jedoch NICHT LCDstate 
zugewiesen wird, da es NACH dem Komma steht!!!!
LCDstate = -1+size funktioniert nur, weil LCDstate zuerst mit -1 geladen 
wird und dann durch den Makro NR_ENTRIES(x) die 14 aufaddiert werden.

Ändere die Definition von size bzgl. dem Komma und alles wird gut!

von Rolf M. (rmagnus)


Lesenswert?

A. K. schrieb:
> HerbertK schrieb:
>> if (LCDstate == 255) {LCDstate = -1+size;} richtig und
>
> Führt zu LCDstate = mainMenu;

Nein.

> a = b,c-1 führt zu a = c-1, wobei von b nur Seiteneffekte bleiben.

Nein, denn es wird aufgelöst als:

(a = b), c-1

Die Zuweisung bindet stärker als das Komma.

> a = -1+b,c führt zu a = c und das -1+ ist für den Allerwertesten.

Nein, es führt zu a = -1+b.

HerbertK schrieb:
> @g457
> Ich weiß leider nicht, welchen Kommaoperator du ansprichst.

Na der, den du verwendet hast:

HerbertK schrieb:
1
 #define size   NR_ENTRIES( mainMenu ), mainMenu
2
                                      ^
3
                                  Dieses Komma

> 1. Es stammt nicht von mir sondern einen der Moderatoren hier und ich
> bin davon ausgeganen, dass der Code erprobt ist.

Dann hast du ihn vermutlich falsch übertragen, denn er egibt keinen 
Sinn.

> 2. Der Preprozessor läuft doch vor dem Compiler. Bis der Compiler sein
> Werk beginnt, sollte es doch genau so sein wie ein
>
> #define size  14
>
> und das ist wie eine Konstante.

Das ist aber nicht das, was bei der Ersetzung dieses Makros rauskommt.

> Wieso ist also:
>
> LCDstate = 14-1; besonders lustig und stellt eine Falle dar?

Weil da nicht "14-1" rauskommt, sondern "14, mainMenu-1".

> @Alle
> Was mir vor allem nicht einleuchten will: warum funktioniert:
>
> if (LCDstate == 255) {LCDstate = -1+size;} richtig und
1
LCDstate = -1+(sizeof(mainMenu)/sizeof(*mainMenu)), mainMenu;
2
[C]
3
hier ist die -1 links vom Komma.
4
5
> if (LCDstate == 255) {LCDstate = size-1;} nicht richtig?
6
7
[C]
8
LCDstate = (sizeof(mainMenu)/sizeof(*mainMenu)), mainMenu-1;
hier ist sie rechts vom Komma.

> Warum funktioniert es mit der manuellen Zuweisung
>
> #define size1  14
>
> richtig, obwohl size ja auch richtig ermittelt wurde, was ich ja mit
>
> write(LCD, " LCDstate: %i, Size: %i",LCDstate, size);
>
> überprüfen kann?

Es würde genau die gleichen Fehler aufweisen, wenn du dort auch den 
Komma-Operator verwendest, wie bei der anderen Variante:
1
#define size1  14, mainMenu

Wirf bei deinem Makro size einfach das Komma und alles danach raus. Dann 
funktionert es:
1
  #define size   NR_ENTRIES( mainMenu )

von (prx) A. K. (prx)


Lesenswert?

Rolf Magnus schrieb:
> Die Zuweisung bindet stärker als das Komma.

Oops. Stimmt.

von Bernd K. (prof7bit)


Lesenswert?

Warum kompiliert das überhaupt?

Ist
1
a = b,c;

irgendeine obskure Syntax die zwar niemand verwendet aber trotzdem 
irgendeinen Sinn aus grauer Vorzeit hat?

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

Das ist halt der Kommaoperator.

Es gibt Fälle, wo er einen Sinn haben kann:

for (i = 0, y = 1; i < 16; i++, y <<= 1)
{
 ...
}

von (prx) A. K. (prx)


Lesenswert?

Bernd K. schrieb:
> irgendeine obskure Syntax die zwar niemand verwendet aber trotzdem
> irgendeinen Sinn aus grauer Vorzeit hat?

Als es noch keine Inline-Funktionen gab nutzte man der Effizienz halber 
in Makros, die in funktionalem Kontext genutzt wurden, wahre Kunstwerke, 
gebildet aus ?: und , Operatoren.

von HerbertK (Gast)


Lesenswert?

g457 schrieb:
> Nicht wirklich. Mach mal den Kommaoperator raus, der ist (nicht nur
> hier) böse.

Rolf Magnus schrieb:
> Wirf bei deinem Makro size einfach das Komma und alles danach raus. Dann
> funktionert es:  #define size   NR_ENTRIES( mainMenu )

Das war es!

Vielen Dank

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.