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