Hallo Leute,
ich habe an meinen Arduino Mega ein Tastenfeld und ein LCD Display.
Ich lese zur Zeit zwei chars ein. Diese wandle ich in float um. Ich
möchte nämlich mit GPS Koordinaten in Dezimalgrad rechnen.
Die Berechnung der Fließkommazahl möchte ich dann wieder auf den LCD
Display ausgeben, deshalb das Ergebnis wieder in char umwandeln.
Wenn ich eingebe.
nummer = 200
nummer2 = 500
bekomme ich als Ergebnis: 201000.000 in der Ausgabe.
Vermutlich mindestens ein fehler drin. Seltsamerweise werden auch statt
8 Stellen 10 ausgegeben, davon abgesehen stimmt das Ergebnis natürlich
nicht.
Über Hilfe und Tips wäre ich sehr dankbar.
void loop(){
char nummer[3], nummer2[3] , char num[3];
float zahl1, zahl2, ergebnis;
//======== HIER EINGABE WEGGELASSEN======//
zahl1= atof(nummer); // Umwandlung und Berechnung
zahl2= atof(nummer2);
ergebnis= zahl1 + zahl2;
dtostrf(ergebnis,8,3,num);
lcd.setCursor(0,0);
lcd.clear();
lcd.print("Ergebnis");
lcd.setCursor(0,1);
lcd.write(num);
delay(10000);
}
Matthias Peters schrieb:> char nummer[3], nummer2[3]
Wie viele Zeichen kann man diesen beiden Arrays speichern, wenn man sie
als C-Strings benutzt?
Nein, nicht drei. Also lassen weder "500" noch "200" sich darin
unterbringen.
Ich lese in einer for-Schleife nur drei Ziffern ein.
weis nicht, ob er eine Null am Ende dranhängt, aber selbst wenn, dann
sind
2000 + 5000 != 201000 !
Hier die Eingabe:
lcd.clear();
lcd.setCursor(0,0);
lcd.print("Bitte Eingabe 1: ");
for(int i =0; i<3;i++) // Eingabe Zahl 1
{
nummer[i] = eingabe();
delay(100);
lcd.setCursor(i,1);
lcd.write(nummer[i]);
}
Ho mann Sorry Jungs.
For-Schleife auf i < 2 reduziert und jetzt passt es!
Aber selbst wenn ich drei Zahlen eingebe mit der Null am Ende, habe ich
ja keine Null eingegeben, wenn sie nicht mehr reinpasst.
Dann müsste als ergebnis ja trotzdem 70 rauskommen ?
Matthias Peters schrieb:> Ach Mist verstehe. [0] [1] [2], wenn er am Ende eine Null reinschreibt> habe ich nur 2 Stellen. Wie erklärt sich dann das dumme Ergebnis ?
Von deinem Geschriebeben verstehe ich dass Du den unterschied von Null
'0' zu NUL nicht intus hast.
Stimmts?
Hallo,
zunächst: Strings in C werden mit '\0' (NUL) terminiert.
Um also in einem string eine "200" zu speichern, benötigst du schonmal
mindestens 4 byte.
Außerdem wenn deine Ausgabe 8 Zeichen lang sein soll, dann muss auch da
das Array dementsprechend angepasst werden.
Matthias Peters schrieb:> Ich lese zur Zeit zwei chars ein. Diese wandle ich in float um. Ich> möchte nämlich mit GPS Koordinaten in Dezimalgrad rechnen.>> Die Berechnung der Fließkommazahl möchte ich dann wieder auf den LCD> Display ausgeben, deshalb das Ergebnis wieder in char umwandeln.
Versuche doch bitte dich exakt auszudrücken: Was du einliest, sind
vermutlichst nicht zwei char, sondern zwei Zeichenketten zu neudeutsch
Strings. Wie in deinem Beispiel 200 und 500 eben. Dafür brauchst du
jeweils so viel Platz in deinen jeweiligen Eingabe-Puffern, daß alle
Zeichen plus ein abschließendes Nullbyte hineinpassen. Je 3 Byte sind
viel zu wenig. Ebenso brauchst du ausreichend Platz für deinen
Ausgabepuffer. Erstmal weißt du nicht, was das Programm im Laufe seines
Lebens so alles an Eingaben bekommt und du weißt auch nicht, was dann
beim Ausgabe-Konvertieren draus wird. Es könnte nämlich dir passieren,
daß aus Bereichsgründen dein Ausgabekonverter auf Exponentialdarstellung
umschaltet. Das sieht dann etwa so aus:
1.234567E+21
Dafür würdest du also mindestens 13 Plätze im Puffer brauchen.
Es ist immer besser, sich vorher Gedanken über mögliche Puffergrößen für
zu erwartende String-Längen zu machen, denn C hat ja keine
Stringverarbeitung wie Pascal.
W.S.
Genaulesender schrieb:> Matthias Peters schrieb:>> Ach Mist verstehe. [0] [1] [2], wenn er am Ende eine Null reinschreibt>> habe ich nur 2 Stellen. Wie erklärt sich dann das dumme Ergebnis ?>> Von deinem Geschriebeben verstehe ich dass Du den unterschied von Null> '0' zu NUL nicht intus hast.> Stimmts?
Ja genau das stimmt! :-)
Jetzt wäre das ja geklärt. Ich dachte halt, das dort nur eine '0' stehen
müsste.
> For-Schleife auf i < 2 reduziert und jetzt passt es!>>Passt was?>>Was steht jetzt in [2]?>>Was muss zwingend in [2] stehen?
Wenn ich '0' eintippe gibt es ja Probleme, da eine Zeichenkette mit '\0'
abgeschlossen werden muss, wie oben bereits mehrfach erwähnt.
Das habe ich es ja zuerst nicht gemacht, was dann ja auch kein logisches
Ergebnis lieferte.
char nummer[3];
nummer[0] = '7';
nummer[1] = '7';
nummer[2] = '0'; //Falsch
Dann habe ich statt alle drei Byte der Zeichenkette zu beschreiben, nur
die ersten beiden mit Werten gefüllt:
chat nummer[3];
nummer[0] = '7';
nummer[1] = '7';
Da die Berechnung jetzt stimmt, muss '\0' ja automatisch an die letzte
Stelle der Variable geschrieben worden sein, weil ich ja nichts
reingeschrieben habe, oder bei der Definition der Variablen hat bereits
das letzte Byte automatisch das Zeichen '\0' bekommen ?
Dann könnte ich es ja so machen, das ich einfach so viele Zeichen
einlese, bis die Variable voll ist und dann in das letzte Byte explizit
'\0' reinschreibe.
Probiere das mal aus. Schonmal danke für Eure Hilfe.
@ W.S Danke für den Hinweis mit den Puffer. Arbeite mich da Stück für
Stück rein.
Matthias Peters schrieb:> Wenn ich '0' eintippe gibt es ja Probleme, da eine Zeichenkette mit '\0'> abgeschlossen werden muss, wie oben bereits mehrfach erwähnt.> Das habe ich es ja zuerst nicht gemacht, was dann ja auch kein logisches> Ergebnis lieferte.
Richtig. Schau dir mal eine ASCII-Tabelle an. '0' entspricht 0x30,
während die Terminierung '\0' 0x00 (NUL) enstpricht.
Matthias Peters schrieb:> Da die Berechnung jetzt stimmt, muss '\0' ja automatisch an die letzte> Stelle der Variable geschrieben worden sein, weil ich ja nichts> reingeschrieben habe, oder bei der Definition der Variablen hat bereits> das letzte Byte automatisch das Zeichen '\0' bekommen ?
Initialisier dein Array in dem Fall dann (Und achte immer auf deine
nötigen Arraygrößen):
1
charnummer[3]="77";
So initialisiert der Compiler nummer[2] automatisch mit '\0'.
Matthias Peters schrieb:> Dann könnte ich es ja so machen, das ich einfach so viele Zeichen> einlese, bis die Variable voll ist und dann in das letzte Byte explizit> '\0' reinschreibe.
Ja. Auch hier: Achte darauf, dass das Array auch auf jeden Fall groß
genug ist.
Matthias Peters schrieb:> Da die Berechnung jetzt stimmt, muss '\0' ja automatisch an die letzte> Stelle der Variable geschrieben worden sein, weil ich ja nichts> reingeschrieben habe, oder bei der Definition der Variablen hat bereits> das letzte Byte automatisch das Zeichen '\0' bekommen ?
Es kann auch einfach zufällig '\0' gewesen sein, weil bisher noch nichts
anderes an diese Speicherstelle geschrieben worden ist. Bei der
Definition wird in lokale Variablen nur dann was geschrieben, wenn man
sie explizit initialisiert.
Matthias Peters schrieb:> Ach Mist verstehe. [0] [1] [2], wenn er am Ende eine Null reinschreibt> habe ich nur 2 Stellen. Wie erklärt sich dann das dumme Ergebnis ?
Ist Dir das inzwischen klar? Das ist ein guter Test, ob Du die C-Strings
verstanden hast. Dann ist die Antwort eigentlich offensichtlich. ;-)
char nummer[3];
nummer[0] = '2';
nummer[1] = '0';
A. H. schrieb:> Matthias Peters schrieb:>> Ach Mist verstehe. [0] [1] [2], wenn er am Ende eine Null reinschreibt>> habe ich nur 2 Stellen. Wie erklärt sich dann das dumme Ergebnis ?>> Ist Dir das inzwischen klar? Das ist ein guter Test, ob Du die C-Strings> verstanden hast. Dann ist die Antwort eigentlich offensichtlich. ;-)
Es ging ja um folgendes.
char num[3];
char nummer[3];
nummer[0] = '2';
nummer[1] = '0';
nummer[2] = '0';
char nummer2[3];
nummer[0] = '5';
nummer[1] = '0';
nummer[2] = '0';
float zahl = atof(nummer);
float zahl2 = atof(nummer2);
float ergebnis = zahl1 + zahl2;
dtostrf(ergebnis,8,3,num);
Als Ausgabe erhielt ich: 201000.000
Ich kann es mir nur so erklären, dass die Funktion atof die Zeichenkette
so lange einliest, bis sie auf ein '\0' stößt, was bei mir nicht
passiert, und deshalb über den reservierten Speicher hinaus irgendwann
auf ein '\0' stößt. Vielleicht passiert das aber auch nicht, weil kein
'\0' kommt und zahl wird mit irgendwelchen Werten gefüllt.
Dann ist die Fließkommazahl viel größer als 200.
Wenn ich das mit einer zweiten Zahl genau so mache und beide float
Zahlen addiere bekomme ich dann so eine hohe Zahl heraus. Anders kann
ich mir das nicht erklären.
Wenn man den Wert der float-Variable, also 201000.000 jetzt mal als
"korrekt" annehmen würde, verstehe ich nicht, warum in der Ausgabe
201000.000 angezeigt wird, ich habe der Zeichenkette num[3] ja
prinzipiell nur 2 Plätze eingeräumt. Bedeutet ja, das die Funktion
dtostrf den Speicherbereich erweitert ?
Komisch auch, das die 8 Stellen nicht passen.
Matthias Peters schrieb:> Ich kann es mir nur so erklären, dass die Funktion atof die Zeichenkette> so lange einliest, bis sie auf ein '\0' stößt, was bei mir nicht> passiert, und deshalb über den reservierten Speicher hinaus irgendwann> auf ein '\0' stößt.
Ganz genau. Jetzt musst Du nur noch daran denken, dass die drei Felder,
die Du angelegt hast, im Speicher hintereinander stehen. Man kann sie
also auch als ein zusammenhängendes Feld betrachten und genau das
machen die String-verarbeitenden Funktionen auch. Sie erhalten ja nur
einen Pointer (siehe Pointer-Array-Beziehung) wissen daher nicht, wie
groß Du das Feld deklariert hast. Auf die Positionen 0-2 des Feldes hast
Du „200“ geschrieben. Das abschließende '\0' fehlt, wäre aber durch die
nächste Aktion ohnehin überschrieben worden, den an die Positionen 3-5
schreibst Du „500“. Im Speicher steht jetzt hintereinander also
„200500“. In der darauf folgenden Position 6 muss zufällig ein '\0'
stehen (oder ein anderes Zeichen, das atof zum Abbruch zwingt), denn
das ist genau das, was das erste atof liest. Das zweite atof liest
aus dem gleichen Speicher, nur beginnt es drei Positionen weiter hinten,
also bei „500“. Und 200500+500 sind?
Du solltest das überprüfen können, indem Du Zahl und Zahl2 mal
getrennt ausgibst.
PS: Das drei hintereinander deklarierte Felder tatsächlich einen
zusammenhängenden Speicherbereich bilden muss nicht sein, es gibt da
noch das Thema Alignment. Häufig ist es aber der Fall und das würde das
Verhalten hier exakt erklären.
Matthias Peters schrieb:> Wenn man den Wert der float-Variable, also 201000.000 jetzt mal als> "korrekt" annehmen würde, verstehe ich nicht, warum in der Ausgabe> 201000.000 angezeigt wird, ich habe der Zeichenkette num[3] ja> prinzipiell nur 2 Plätze eingeräumt. Bedeutet ja, das die Funktion> dtostrf den Speicherbereich erweitert ?
Nein, das bedeutet es nicht, das kann ein Funktion gar nicht. Hast Du
Dich schon mal gefragt, was ein Buffer-Overflow ist? Das hier ist einer,
ein ganz klassischer.
Eine Funktion, die in den Speicher schreibt, kann, solange sie nur einen
Pointer bekommt, genauso wenig wissen, wie groß der reservierte
Speicherbereich ist, wie eine lesende Funktion dies kann. Sie muss sich
darauf verlassen, dass der Speicherbereich, den Du reserviert hast, groß
genug ist. Das kannst Du aber nur in den seltensten Fällen wirklich
garantieren. Selbst wenn Du Dir vorher ausrechnest, wie viele Stellen
ein Zahlenbereich höchstens haben kann, es kann ja sein, dass Dein Code
mal auf eine andere Plattform portiert oder mit einem anderen Compiler
übersetzt wird, und da können schon wieder ganze andere Werte gelten.
Das war tatsächlich ein schwerer Design-Fehler früher
C-Implementierungen. Heute sollte es eigentlich immer auch eine Variante
der Funktion geben, der Du die Größe des Puffers mitgeben kannst und es
ist definitiv besser, nur solche Funktionen zu verwenden. Denn anders
hat ein Funktion keine Chance vernünftige Annahmen über die Größe des
Puffers zu machen und das heißt, sie wird im Zweifel einfach über den
Puffer hinaus schreiben. Das kann nicht nur zu undefiniertem Verhalten
führen, denn in dem versehentlich überschrieben Speicher wird in der
Regel ja etwas anders Wichtiges stehen, sondern ist, zumindest bei
Funktionen, die die zu schreibenden Daten von außen erhalten, auch ein
erhebliches Sicherheitsrisiko, denn es kann gegebenenfalls genutzt
werden, um beliebige Daten in Bereiche zu schreiben, die später als Code
interpretiert werden.
Matthias Peters schrieb:> char num[3];> char nummer[3];
Du hast hier sogenannte Auto-Variablen definiert. Deren Anfangswert (und
damit deren Speicherstellen) sind undefiniert. Wenn Du Glück hast,
steht da irgendwo mal ein NUL-Byte (ja, NUL mit einem L) drin.
Aber auf Glück darf ein Programmierer niemals spekulieren.
Damit dort eine dreistellige Zahl gespeichert wird, brauchst Du pro
String 3+1 = 4 Bytes.
Schreibe also:
1
charnum[4];
2
charnummer[4];
Jetzt weiter:
> nummer[0] = '2';> nummer[1] = '0';> nummer[2] = '0';
Wenn Du Glück hast, steht in nummer[3] bereits ein NUL, also '\0'. Die
Chance ist aber nur 1/256, kannst Du also vergessen.
Deshalb musst Du schreiben:
1
nummer[0]='2';
2
nummer[1]='0';
3
nummer[2]='0';
4
nummer[3]='\0';
Oder auch kürzer:
strcpy (nummer, "200");
Über den Speicherplatzbedarf Deines dtostrf()-Aufrufs mach Dir bitte nun
selbst Gedanken. Stelle dann einen entsprechend großen Buffer zur
Verfügung. Dann klappt das auch.