Forum: Mikrocontroller und Digitale Elektronik IF Abfrage -> warning: (764) mismatched comparison


von Teo D. (teoderix)


Lesenswert?

Moin,

ich versuch mich grad mal wieder in C und bekomm's natürlich nicht 
gebacken :(

Folgende IF Abfrage wird nie Wahr.
1
char Drehgeber(void);
2
3
if (Drehgeber() == -1)   // main.c:51: warning: (764) mismatched comparison

Wenn ich die Funktion explizit als signed deklariere, bleibt die Warnung 
aus, ändert aber nichts im Ablauf. Auch Spielereien mit <0 bringt keine 
Änderung ?-(

Was ist hier meine grundsätzliche Wissenslücke.


PS: Die Funktion liefert definitiv -1

von Mark B. (markbrandis)


Lesenswert?

char ist für Zeichen gedacht.

unsigned char ist für reine Zahlenwerte zwischen 0 und 255 (in aller 
Regel - ein char muss nicht unbedingt 8 Bits haben).

signed char ist das, was Du willst, wenn Du Zahlen zwischen -128 und 127 
abspeichern willst (in aller Regel, siehe oben).

von Mark B. (markbrandis)


Lesenswert?

Bei mir tut's einwandfrei:

1
#include <stdio.h>
2
3
char Drehgeber(void)
4
{
5
  // liefert definitiv -1 zurück :-)
6
  return -1;
7
}
8
9
int main()
10
{
11
  if (Drehgeber() == -1)
12
    printf("-1\n");
13
  else
14
    printf("was anderes als -1\n");
15
16
  return 0;
17
}

Ausgabe:
-1

Compiler:
gcc (GCC) 4.8.1

von Mark B. (markbrandis)


Lesenswert?

Teo Derix schrieb:
> PS: Die Funktion liefert definitiv -1

Das wage ich jetzt mal dezent zu bezweifeln. Zeig den Code.

von Teo D. (teoderix)


Lesenswert?

Mark Brandis schrieb:
> signed char ist das, was Du willst, wenn Du Zahlen zwischen -128 und 127
> abspeichern willst (in aller Regel, siehe oben).

Teo Derix schrieb:
> Wenn ich die Funktion explizit als signed deklariere, bleibt die Warnung
> aus, ändert aber nichts im Ablauf.

Teo Derix schrieb:
> PS: Die Funktion liefert definitiv -1

Habs mit
PORTx += Drehgeber() getestet. Zählt wie gewollt rauf und runter.

von Mark B. (markbrandis)


Lesenswert?

Teo Derix schrieb:
> Habs mit
> PORTx += Drehgeber() getestet. Zählt wie gewollt rauf und runter.

Wenn der Rückgabewert von Drehgeber() immer gleich -1 ist, dann steht 
da:

PORTx = PORTx + Drehgeber()

was gleichbedeutend ist mit

PORTx = PORTx - 1

Was heißt da "rauf und runter" zählen? Es wird immer dekrementiert.

Zeige ein komplettes Code-Beispiel, mit dem man den Fehler reproduzieren 
kann.

: Bearbeitet durch User
von Teo D. (teoderix)


Lesenswert?

1
 if (scan_Tick) {
2
            // PORTB = PORTB + Drehgeber();
3
            if (Drehgeber() == 1) {
4
                PORTB = (PORTB << 1);
5
                RB7 = 1;
6
            }
7
            if (Drehgeber() == -1) {
8
                PORTB = (PORTB >> 1);
9
                RB7 = 0;
10
            }
11
            scan_Tick = 0;
12
        }
1
char Drehgeber(void) {
2
    static unsigned char alt = 0;
3
    char neu = 0;
4
5
    neu = DrehPort1 | (DrehPort2 << 1);
6
    if (alt == neu) {
7
        return 0;
8
    }
9
10
    if ((neu > 2) || (neu == 0)) {
11
        if ((alt ^ neu) == 1) {
12
            alt = neu;
13
            return 1;
14
        } else {
15
            alt = neu;
16
            return -1;
17
        }
18
    }
19
    alt = neu;
20
    return 0;
21
}

von Mark B. (markbrandis)


Lesenswert?

Teo Derix schrieb:
> PS: Die Funktion liefert definitiv -1

Die Funktion hat drei verschiedene mögliche Rückgabewerte: 1, 0 und -1.

von Teo D. (teoderix)


Lesenswert?

Richtig...
Wenn sie aber -1 liefert, wird der IF-Zweig trotzdem nicht ausgeführt. 
Darum raff ich's ja nicht!

von Teo D. (teoderix)


Lesenswert?

Irgend wie scheint es mir so, als ob -1 intern anders dargestellt wird 
als eine (signed)char Variable mit dem Inhalt -1.
Was allem dem widerspricht, was ich von anderen Hochsprachen her kenne.
Werd das gleich mal mit ner Hilfs-Var testen.

von Peter II (Gast)


Lesenswert?

Teo Derix schrieb:
> Irgend wie scheint es mir so, als ob -1 intern anders dargestellt wird
> als eine (signed)char Variable mit dem Inhalt -1.
> Was allem dem widerspricht, was ich von anderen Hochsprachen her kenne.
> Werd das gleich mal mit ner Hilfs-Var testen.

du schreibt nur "char" damit ist überhaupt nicht definert ob es unsigned 
oder signed ist. Das kann ja nach Compiler anders sein, uns sogar per 
Parameter umgeschaltet werden.

von Oliver S. (oliverso)


Lesenswert?

Peter II schrieb:
> du schreibt nur "char" damit ist überhaupt nicht definert ob es unsigned
> oder signed ist.

Natürlich ist das eindeutig definiert, vom Compiler. Man sollte halt nur 
wissen, wie.

Teo Derix schrieb:
> Wenn ich die Funktion explizit als signed deklariere, bleibt die Warnung
> aus

Das ist wohl mal wieder so ein Fall, in dem du an der völlig falschen 
Stelle suchst. Der Fehler steckt vermutlich ganz woanders. Denn du 
kannst 100% davon ausgehen, daß
> if (Drehgeber() == -1)
genau das tut, was du erwartest, wenn Drehgeber einen signed char 
zurückgibt.

Woher weißt du, daß der if-Zweig nie durchlaufen wird?

Oliver

von Kaj (Gast)


Lesenswert?

Oliver S. schrieb:
> Natürlich ist das eindeutig definiert, vom Compiler. Man sollte halt nur
> wissen, wie.

Jein. In Atmel Studio (GCC) ist char per default ueber die IDE als 
unsigned definiert. Muss man nur halt wissen und ggf. aendern.

von MaWin (Gast)


Lesenswert?

Teo Derix schrieb:
> Was ist hier meine grundsätzliche Wissenslücke.

Wenn die Funktion bei char niemals -1 liefert, bei signed char aber 
schon, dann ist dein normales char eben per Compileroption ein unsigned 
char.

Wenn man die Funktion Drehgeber mit ihrem statischen von Aufruf zu 
Aufruf veränderten Zustand nicht ein mal wie in PORTB = PORTB + 
Drehgeber() sondern 2 mal wie bei if(Drehgeber() == -1) und 
if(Drehgeber() == 1) aufruft dann kommt was falsches raus.

von Martin L. (maveric00)


Lesenswert?

Hallo,

zusätzlich kann einem die automatische Promotion des C-Standards hier 
einen Streich spielen, da sowohl ein unsigned char USC als auch der 
PORTx bei

PORTx=PORTx+USC

zu einem int gewandelt wird, addiert wird und dann wieder zu einem 
unsigned char gewandelt wird, wobei der Überlauf verloren geht, während

USC==-1

zu einem int gewandelt wird (da -1 ein int ist) und da USC niemals 
negativ werden kann stets falsch ist. Dies erkennt der Compiler und 
optimiert gleich den ganzen if-Zweig weg (kannst Du ja einmal im 
produzierten Assembler-Code nachsehen).

Die Zuweisung USC=-1 und das Rückwandeln der addierten Port-Werte 
funktioniert, da hier der Standard vorschreibt:

"the value is converted by repeatedly adding or subtracting one more 
than the maximum value that can be represented in the newtype until the 
value is in the range of the newtype."

In Wirklichkeit enthält USC also 255 und nicht -1, wie Du gedacht hast, 
was bei der Addition im Port allerdings ebenfalls zu dem beobachteten 
Ergebnis führt. Allerdings optimiert hier der Compiler auch, indem er 
einfach das höherwertige Byte weglässt.

Schöne Grüße,
Martin

: Bearbeitet durch User
von Ast E. (vis)


Lesenswert?

MaWin schrieb:
> Wenn die Funktion bei char niemals -1 liefert, bei signed char aber
> schon, dann ist dein normales char eben per Compileroption ein unsigned
> char.


Und das ist Rätsels Lösung ;-)

Die Funktion kann eben doch kein -1 zurück geben. Man könnte aber auch 
die ganze Problematik umgehen, indem man gar nicht erst mit -1 sondern 
beispielsweise mit 2,3,4,5.. arbeitet

von sebi707 (Gast)


Lesenswert?

char ist der einzige C-Typ der nicht standardäßig signed ist wenn man 
nichts dran schreibt. Welches von beidem zutrifft hängt vom Compiler und 
dessen Einstellungen ab. Das hat vor allem den Grund, dass char einglich 
für Zeichen gedacht ist. Wenn du damit Zahlen übergeben möchtest 
solltest du dich explizit für unsigned oder signed entscheiden, je 
nachdem in welchem Zahlenbereich du arbeiten möchtest.

von Teo D. (teoderix)


Lesenswert?

MaWin schrieb:
> Wenn die Funktion bei char niemals -1 liefert, bei signed char aber
> schon, dann ist dein normales char eben per Compileroption ein unsigned
> char.

Das hab ich natürlich nicht vergessen. Eine einfache char Definition ist 
beim xc8 definitiv ein signed char.
Werde mir aber angewöhnen es explizit aus zu schreiben, man weiß ja 
nie...

MaWin schrieb:
> Wenn man die Funktion Drehgeber mit ihrem statischen von Aufruf zu
> Aufruf veränderten Zustand nicht ein mal wie in PORTB = PORTB +
> Drehgeber() sondern 2 mal wie bei if(Drehgeber() == -1) und
> if(Drehgeber() == 1) aufruft dann kommt was falsches raus.

Da Blödheit, nicht per seh weh tut, such ich mir jetzt ne dicke dicke 
Mauer, für meinen Schädel :)
War halt scho a bissel spät.

Danke an alle die sich für mich den Kopf zerbrochen haben, jetzt bin 
ich dran. aua au aua...

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


Lesenswert?

Teo Derix schrieb:
> MaWin schrieb:
>> Wenn man die Funktion Drehgeber mit ihrem statischen von Aufruf zu
>> Aufruf veränderten Zustand nicht ein mal wie in PORTB = PORTB +
>> Drehgeber() sondern 2 mal wie bei if(Drehgeber() == -1) und
>> if(Drehgeber() == 1) aufruft dann kommt was falsches raus.
Das ist hier jetzt recht einfach zu sehen, aber wehe, wenn die Zeilen 
ein wenig weiter auseinander geraten...
Aus diesem Grund ist es sinnvoll, solche Sachen (z.B. auch Ports) nur 
ein einziges Mal am Beginn der Hauptschleife einzulesen, und dann 
während der Schleife mit statischen Kopien zu arbeiten.
Das gilt für Alles, was sich ohne Zutun des Programms ändern kann, 
besonders naheliegend sind Ports und Zähler.

Nur mal das hier (als Code für irgendeinen uC):
1
   if (PortB.0==1) {
2
      mach1();
3
   }
4
5
   if (sonstwas) 
6
      tudies();
7
8
   if (PortB.0==0) {
9
      mach2();
10
   }
Auch hier kann sich der Portpin während der Programmausführung ändern, 
und dann wird entweder mach1() und mach2() aufgerufen, oder auch 
keine von beiden. Es könnte sein, dass der Entwickler das nicht 
wollte...

Mit einer lokalen Kopie passiert das nicht:
1
   pb0 = PortB.0;
2
   if (pb0==1) {
3
      mach1();
4
   }
5
6
   if (sonstwas) 
7
      tudies();
8
9
   if (pb0==0) {
10
      mach2();
11
   }

Und im vorliegenden Code ist es das selbe:
1
 if (scan_Tick) {
2
            dg = Drehgeber();
3
            if (dg == 1) {
4
                PORTB = (PORTB << 1);
5
                RB7 = 1;
6
            }
7
            if (dg == -1) {
8
                PORTB = (PORTB >> 1);
9
                RB7 = 0;
10
            }
11
            scan_Tick = 0;
12
        }

BTW:
Es ist übrigens lustig, dass man solche Programmierfehler manchmal beim 
Bedienen von Geräten geradezu vor sich sieht...

: Bearbeitet durch Moderator
von Teo D. (teoderix)


Lesenswert?

Lothar Miller schrieb:
> Aus diesem Grund ist es sinnvoll, solche Sachen (z.B. auch Ports) nur
> ein einziges Mal am Beginn der Hauptschleife einzulesen, und dann
> während der Schleife mit statischen Kopien zu arbeiten.
> Das gilt für Alles, was sich ohne Zutun des Programms ändern kann,
> besonders naheliegend sind Ports und Zähler.

Wissen und dran denken sind halt zwei Paar Schuhe, wenn man nur alle 
paar Monate, mal was Programmiert :-/
Als Assembler Fritze, kommt es mir im momentan auch noch so vor, als 
hätte ich dicke dicke Wollhandschuhe an. Man glaubt irgendwie, nicht so 
richtig an das Teil ran zu kommen.

von Rudolph (Gast)


Lesenswert?

Teo Derix schrieb:
> Als Assembler Fritze, kommt es mir im momentan auch noch so vor, als
> hätte ich dicke dicke Wollhandschuhe an. Man glaubt irgendwie, nicht so
> richtig an das Teil ran zu kommen.

Sowas wie "PORTB = drölf;" ist doch noch direkt an der Hardware.

Bei anderen Controllern bekommst Du nichtmal mehr direkten Zugriff auf 
Register über den Compiler, zumindest nicht, wenn Du die Includes 
benutzt die mitgeliefert werden.
Um einen Port-Pin zu setzen wird dann eine Funktion aufgerufen die eine 
Funktion aufruft die erstmal ausdekodiert was man überhaupt will, um 
eine Funktion aufzurufen die dann den Pin setzt -> Fortschritt.

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.