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]); }
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 ?
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?
> For-Schleife auf i < 2 reduziert und jetzt passt es!
Passt was?
Was steht jetzt in [2]?
Was muss zwingend in [2] stehen?
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 | char nummer[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.
Und denke daran, falls du mal verreist, die Grad auch negativ sein können, dann brauchst du in der Ausgabe Platz für das "-" Zeichen.
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.
edit: char nummer2[3]; nummer2[0] = '5'; nummer2[1] = '0'; nummer2[2] = '0';
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, Ja, tut sie. Absolute Grundlagen über C-Strings. Matthias Peters schrieb: > Bedeutet ja, das die Funktion > dtostrf den Speicherbereich erweitert ? Nein, tut sie nicht. Du als Programmierer bist dafür verantwortlich, dass der Platz ausreicht. Lies es nach: http://www.atmel.com/webdoc/AVRLibcReferenceManual/group__avr__stdlib_1ga060c998e77fb5fc0d3168b3ce8771d42.html
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 | char num[4]; |
2 | char nummer[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.
Vielen Dank nochmals Leute, für die zum Teil sehr detaillierten Erklärungen! Das ist echt Spitze ! Ihr habt mir wircklich sehr sehr geholfen :-)
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.