Hallo alle zusammen,
ich hab ein kleines Problem mit einer Variable, die ein gewisses
eigenleben entwickelt hat.
Ich bastle gerade einen kleinen Taschenrechner und habe erstmal eine
einfache Addition implementiert. Alles funktioniert prima... zumindest
bis man versucht Zahlen über 999 miteinander zu addieren. Sobald ein
Summand 4-stellig wird lässt sich etwas interessantes beobachten: In der
nächsten Addition werden die beiden letzten Zahlen des vorherigen ersten
Summanden ab der 2. eingegeben Zahl hinten angehängt! :-D Ich weiß wie
bekloppt das klingt, aber mein lcd lügt mich nicht an. Ein Beispiel:
1. Addition
Eingabe:
1122 + 3
Ausgabe:
1125
2. Addition
Eingabe:
22 + 3
Ausgabe:
2225, weil 22 die letzten beiden Stellen aus der vorhergehenden Addition
abbekommt und zu 2222 wurde.
Ich dachte natürlich es liegt daran, dass ich die Variablen oder den
Zwischenspeicher nicht wieder auf 0 setzte, bevor ich die nächste
Addition mache, aber daran liegt es ganz sicher nicht (s. Quellcode) und
die letzten beiden Stellen werden ja auch erst angehängt, nachdem die 2.
Zahl eingegeben wurde.
Mich macht das irgendwie ganz schön konfus... hier jetzt erstmal der
Quellcode:
1
uint16_tadd_zahl(charzahl,charausg){// Zahl an den Zwischenspeicher hängen
2
if(flag==1){// Bildschirm nach fertiger Rechnung löschen
3
lcd_clear();
4
flag=0;
5
}
6
7
lcd_string(ausg);// eingegebene Zahl ausgeben
8
wert[i]=zahl;// Zahl an den Zwischensp. hängen
9
i++;// Position im Zwischensp. erhöhen
10
if(i>5){// Abfrage ob eingegebene Zahl zu groß für 16-bit Variablen
11
lcd_home();
12
lcd_string("zu lang!");
13
_delay_ms(2000);
14
lcd_clear();
15
i=0;
16
}
17
return0;
18
}
19
20
intmain(void)
21
{
22
23
24
// unwichtiges Initialisierungsblabla
25
26
int16_ta=0;
27
int16_tb=0;
28
charerg[8];
29
sprintf(wert,"0");
30
31
while(1){
32
33
if(get_key_short(1<<KEY1)){// Taster für die Eingabe von "1"
34
add_zahl(49,"1");// Übergabe von ASCII-Wert und der Zahl als char *
35
}
36
37
// weitere Taster
38
39
if(get_key_short(1<<KEY_PLUS)){// Plus Taste
40
a=atoi(wert);// in int umwandeln
41
lcd_string("+");
42
sprintf(wert,"0");// Zwischenspeicher nullen
43
i=0;// Nummernlänge zurücksetzen
44
}
45
46
if(get_key_short(1<<KEY_GLEICH)){// Gleich Taste
47
b=atoi(wert);// in int umwandeln
48
b=a+b;// die Addition
49
itoa(b,erg,10);// zurückwandeln
50
51
set_cursor(0,2);
52
lcd_string(erg);// Ausgeben
53
54
sprintf(wert,"0");// alles auf 0 setzten
55
i=0;
56
flag=1;
57
a=0;
58
b=0;
59
}
60
61
}
62
}
* kann man das nicht auch irgendwie einheitlich machen? Zumal der letzte
Übergabewert beim compilieren immer die Warnung erzeugt:
"warning: passing argument 2 of 'add_zahl' makes integer from pointer
without a cast"
Ich programmiere eigentlich Java und das auch nicht besonders viel,
daher kann es gut sein, dass mein C Code wegen Unleserlichkeit und
Umständlichkeit einigen Unmut bei euch hervorruft. Korrigiert mich bitte
bei allem, was man auch einfacher lösen kann, schließlich lerne ich
gerne dazu!
Gruß,
JayJay
Typischer Fehler beim Umgang mit Strings.
Der String in wert[] wird nicht terminiert und außerdem ist das Array zu
klein. Und du schreibst ggf. auch hinter das Array.
So jetzt hats sichs ausgegeistert. Indem ich vor jeder Umwandlung noch
eine Endekennung hinten ans Char-Array gepackt habe funktioniert alles
wunderbar. Ich bin doch schon zu sehr an Java gewöhnt...
Ein bisschen Verwirrung haben noch die Funktionen atoi() und itoa()
gestiftet, die ich in atol() und ltoa() geändert habe, damit ich mit
32bit Variablen rechnen kann.
Ärgerlich ist nur, dass die beiden Funktionen keine Errormeldungen
zurückgeben, wenn das Ergebnis außerhalb des Wertebreichs liegt. Bei
normalem C sollte das eigentlich so sein, aber AVR GCC sagt in der
Header-Datei stdlib.h:
"In contrast to
\code (int)strtol(s, (char **)NULL, 10); \endcode
this function does not detect overflow (\c errno is not changed and
the result value is not predictable), [blabla]"
wie strol() die Errormeldung bei strol aber genau funktioniert hab ich
nicht so recht verstanden.
Für Fließkommazahlen gibt es schließlich atof() aber kein ftoa(). Wie
soll ich Fließkommazahlen dann auf dem LCD ausgeben?
Naja und eine Frage bleibt: Gibts irgendeine elegante Möglichkeit diese
doofen Warnungen loszuwerden:"warning: passing argument 2 of 'add_zahl'
makes integer from pointer without a cast"
so long...
JayJay wrote:
> Ärgerlich ist nur, dass die beiden Funktionen keine Errormeldungen> zurückgeben, wenn das Ergebnis außerhalb des Wertebreichs liegt. Bei> normalem C sollte das eigentlich so sein,
Nein. Du solltest dir den C-Standard mal ansehen...
> wie strol() die Errormeldung bei strol aber genau funktioniert hab ich> nicht so recht verstanden.
Die ,,Meldung'' besteht daraus, eine 0 als Ergebnis der Konvertierung
zu liefern. Falls das Ergebnis zu groß oder klein wird, wird
errno auf ERANGE gesetzt und die Konvertierung liefert LONG_MAX
bzw. LONG_MIN.
Die Erkennung eines Fehlers erfolgt, indem man die Adresse einer
Zeigervariablen bei endptr angibt. Nach der Konvertierung, die 0
ergeben hat, schaut man dann nach, worauf der Zeiger zeigt. Zeigt
er auf ein Zeichen '\0', dann war die Konvertierung (mit dem Wert
0) erfolgreich, da alle Zeichen bis zum Ende des Strings konvertiert
werden konnten. Zeigt der Zeiger auf was anderes, dann ist das
das erste nicht mehr konvertierbare Zeichen. Ob du nun einen String
"12x" als gültigen Wert 12 akzeptieren willst (endptr würde auf das
Zeichen 'x' zeigen) oder nicht, musst du mit deiner Applikation
vereinbaren.
All das ist Standard-C; das kannst du besser an einem PC ausprobieren
als auf einem Controller. Wenn du verstanden hast, wie's funktio-
niert, übernimmst du dann den Code in den Controller.
> Für Fließkommazahlen gibt es schließlich atof() aber kein ftoa(). Wie> soll ich Fließkommazahlen dann auf dem LCD ausgeben?
sprintf (dann bitte aber gegen die Gleitkommaversion linken), oder
als Einfach-Varianten dtostre/dtostrf (das sind private Erweiterungen
der avr-libc gegenüber dem Standard).
> Naja und eine Frage bleibt: Gibts irgendeine elegante Möglichkeit diese> doofen Warnungen loszuwerden:"warning: passing argument 2 of 'add_zahl'> makes integer from pointer without a cast"
Natürlich: sauberen Code schreiben. Wenn dein zweites Argument ein
Zeiger ist, warum deklarierst du add_zahl dann so, dass sie einen
Integer übernimmt?
So ich hab mich jetzt ein bisschen in Zeiger und Vektoren eingelesen. In
der Tat gibt es keine Warnungen mehr und alles funktioniert prima, wenn
der Funktionskopf so aussieht:
1
uint8_tadd_zahl(charzahl,char*ausg){
und so aufgerufen wird:
1
add_zahl(49,"1");
Nur irgendwie verstehe ich nur so halb warum, denn der
Dereferenzierungsoperator * sagt doch nur, dass auf den Inhalt der
übergeben Adresse zugegriffen wird und wird deswegen so etwa in dieser
Form benutzt:
1
voidcquadrat(int*wert){
2
*wert=(*wert)*(*wert);
3
}
4
5
voidmain(void){
6
intx;
7
scanf("%i",&x);
8
cquadrat(&x);
9
//irgendeine Ausgabe
10
}
Aber
1
add_zahl(49,"1");
übergibt doch aber keine Adresse einer Variable sondern einen
stinknormalen char. Oder behandelt C das wie char-Array der Länge 2 (
1
{'1',0}
) bei der Übergabe, sodass noch dereferenziert muss (schließlich ist ein
char-Array ja immer nur die Adresse eines Variablenfeldes)?
> Nein. Du solltest dir den C-Standard mal ansehen...
Hupps.. ich hab mir ne C++ Referenz angeschaut und gedacht in diesen
elementaren Funktionen der stdlib hätte sich nix geändert... falsch
gedacht wie sich rausstellt. ^^
so long...
Das ist eine grosse pfui kacka in C und C++. Wie es Java und C#
behandeln
weiss ich nicht. C und C++ haben so etwas wie Sequenzpunkt, dh alle
Nebeneffekte klingen ab nach einem Sequenzpunkt.
Hier modifizierst du Quelle und Ziele in einem Sequenzpunkt ..
das ist analog zu i = i++;
weitere Fehlerquellen ähnlicher Art
int foo(int a, int b){return a*b;}
int x=1;
foo(x,++x);
Ich glaube die C Profis sind da sensibilisiert dadrauf und wenn sie
andere Sprachen lernen, dann fragen die sich wie macht es wohl Java
an der Stelle? Die meisten Javaprogrammierer achten viel weniger
dadrauf, meine Meinung.
Daniel -------- wrote:
> das ist analog zu i = i++;
Nein, es ist analog zu i = i * i, und das ist völlig legal.
Undefiniert wäre sowas wie:
*wert = (*wert)++ * (*wert);
> Aber> add_zahl(49, "1");> übergibt doch aber keine Adresse einer Variable
Doch - naja, nicht die einer Variablen, aber die eines "string literal".
> sondern einen stinknormalen char. Oder behandelt C das wie char-> Array der Länge 2 ({'1',0}) bei der Übergabe,
Das wird nicht nur wie ein char-Array behandelt, sondern es ist eins.
Bei der Übergabe an die Funktion wird dann ein Zeiger auf das erste
Zeichen daraus. Wie sollte das auch sonst funktionieren? So ein String
Literal ist immer gleich aufgebaut, egal wieviele Zeichen da drinstehen.
Wie du ja selbst oben schreibst, mußt du '1' draus machen, wenn du
wirklich nur ein einzelnes Zeichen haben willst und kein Array.
> sodass noch dereferenziert muss (schließlich ist ein> char-Array ja immer nur die Adresse eines Variablenfeldes)?
Das stimmt nicht. Ein char-Array ist nicht die Adresse, aber es kann in
den meisten (aber nicht allen Fällen) genauso verwendet werden. Es ist
wichtig, den Unterschied zu erkennen.
> Hupps.. ich hab mir ne C++ Referenz angeschaut und gedacht in> diesen elementaren Funktionen der stdlib hätte sich nix geändert...> falsch gedacht wie sich rausstellt. ^^
Eigentlich hat sich da auch nichts geändert. C++ übernimmt einfach die
komplette Standardlib von C und hängt noch ein paar Sachen dazu.
>Eigentlich hat sich da auch nichts geändert.
Auf cplusplus.com findet man für atoi() und seine Freunde:
>On success, the function returns the converted integral number as an int value.>If no valid conversion could be performed, a zero value is returned.>If the correct value is out of the range of representable values, INT_MAX >or
INT_MIN is returned.
Hmm... ich finde diese ganzen Zeiger relativ kompliziert... Zwar gibt es
auch bei Java Vektoren aber die benutzt man selten. Hier wird bei der
Übergabe nicht auf Adressen oder ähnliches geachtet, es muss nix
dereferenziert werden und das macht die geschichte angenehm einfach.
Ich denk mal das Thema ist damit abgeschlossen.
Danke für eure Hilfe.