Hallo,
ich möchte einen Integer-Wert in einen char umwandeln, damit ich den
Wert über UART verschicken kann.
Wie löse ich das am geschicktesten, wenn ich auch mal mehr als eine
Ziffer umwandeln will? Die Funktion "itoa" ist leider anscheinend bei
meinem uC nicht implementiert.
Danke.
TechInfo
> Die Funktion "itoa" ist leider anscheinend bei meinem uC nicht> implementiert.
Und welcher ist "Dein" µC? Um die Funktion nutzen zu können, musst Du
schon die stdlib.h einbinden. Und das sollte bei allen µCs mit
ausreichend Speicherressourcen funktionieren (wobei itoa gar nicht so
viel Speicher benötigt)
TechInfo wrote:
> Thx, hab's schon ausprobiert. Jetzt warte ich noch auf die Erklärung ;)
Und auch die steht schon dort.
Aber du solltest wirklich versuchen solch einfache Funktionen
ohne fremde Hilfe zu verstehen: Papier und Belistift nehmen
und selbst mal Computer spielen. Das hilft ungemein.
@Karl-Heinz Buchegger
Danke. Nachdem ich kurz nachgeschlagen habe wie die modulo-Division
funktioniert war das Nachvollziehen kein Problem. Trotzdem, von selbst
auf so etwas zu kommen ist wieder eine andere Geschichte...
TechInfo wrote:
> Morgen,>> gibt es denn auch eine ähnliche Möglichkeit für float-Variablen?
Sicher. Geht fast völlig analog.
Den Vorkommaanteil kannst du ja schon bearbeiten
(Fortgesetzte Division durch 10 und mittels Restbildung
bei der Division durch 10 die Einerstelle extrahieren).
Der Nachkommaanteil ist sogar noch einfacher:
Wenn du 0.89654
mit 10 multiplizierst, erhältst du: 8.9654
Davon schneidest du (zb durch zuweisen zu einem int) den
Nachkommanteil ab und erhältst 8 -> ausgeben
Von den 8.9654 ziehst du die 8 ab und kriegst 0.9654
und jetzt wiederholt sich das Spielchen solange bis du
genügend Kommastellen ausgegeben hast:
0.9654 * 10 -> 9.654 -> 9
-9
-----
0.654
0.654 * 10 -> 6.54 -> 6
-6
----
0.54
0.54 * 10 -> 5.4 -> 5
-5
----
0.4
0.4 * 10 4.0 -> 4
-4
----
0.0
Das Grundprinzip ist also immer mittels Multiplikation
bzw. Division mit 10 oder auch Restbildung, sich die gewünschten
Informationen so zurecht zu legen, dass man sie leicht
weiterverarbeiten kann. Und jetzt rate mal warum unser gewohntes
Zahlensystem das 10-er System heist :-)
Du musst dir nur klar machen, dass eine Zahl 568
auch geschrieben werden kann als 5 * 100 + 6 * 10 + 8
oder aber, was gleichbedeutend ist: 5 * 10^2 + 6 * 10^1 + 8 * 10^0
Hey, klasse. Vielen Dank.
Mein Problem ist jetzt noch, dass ich keine führenden Nullen haben
möchte, deshalb muss ich die Anzahl der Ziffern der Int-Zahl genau
kennen, um die dann als Schleifenindex verwenden zu können.
Da müßte doch auch etwas ähnliches machen können, oder? Für int-Zahlen
könnte man ja solange dividieren lassen, bis null heraus kommt. Dann
müßte man den Schleifenkopf eben leicht ändern. Aber für float?
TechInfo wrote:
> Für int hab ich mir jetzt so geholfen:>>
1
j=z;
2
>while(j>0)
3
>{
4
>j=j/10;
5
>i++;
6
>}
7
>
8
>while(--i>=0)
9
>{
10
>s1[i]='0'+z%10;
11
>z/=10;
12
>}
13
>
So kann man das machen, muss es aber nicht. Divisionen
sind teuer!
Dein Problem ist doch, dass in der Standardschleife ...
1
i=0;
2
while(z>0){
3
s1[i++]='0'+z%10;
4
z/=10;
5
}
6
s1[i]='\0';
... die Stellen genau falsch herum herauspurzeln. Das heist
der String in s1 ist genau falsch herum.
Das kann aber leicht behoben werden:
Etweder indem du den String zunächst in einem Zwischenspeicher
generieren lässt, oder aber indem der String im Nachhinein
gespiegelt werden muss.
Zwischenspeicher:
Das ist sicherlich die einfachste Variante und braucht ein
bischen zusätzlichen Speicher:
1
voidfoo(intz,char*Buffer)
2
{
3
chars1[7];// 10 Stellen reichen für einen int leicht
4
// aus. Der Wertebereich ist ja -32768 bis +32767
5
intj;
6
inti=0;
7
8
while(z>0){
9
s1[i++]='0'+z%10;
10
z/=10;
11
}
12
13
// jetzt da wir wissen wieviele Stellen tatsächlich benötigt
14
// werden (nämlich i) können die Zeichen in den Zielstring
15
// an die richtige Stelle umkopiert werden:
16
17
for(j=0;j<i;++j)
18
Buffer[i-j-1]=s1[j];
19
20
Buffer[i]='\0';
21
}
Da muss jetzt natürlich noch eine Behandlung für negative Zahlen
rein und auch die 0 muss getrennt behandelt werden. Das ist aber
als Übungsaufgabe für den Leser weitergegeben.
Die andere Möglichkeit ist es den String, der ja verkehrt herum
generiert wurde, zu spiegeln.
Dazu betrachten wir einfach mal einen String von dem wir
die Länge kennen (und das tun wir, die Divisionschleife
hinterlässt diese ja in i)
+---+---+---+---+---+---+---+---+
| a | b | c | d | e | f | g | h |
+---+---+---+---+---+---+---+---+
um diesen String (ohne zusätzliches Array) zu spiegeln, genügt
es jeweils das erste mit dem letzten Element zu vertauschen,
dann das zweite mit dem vorletzten usw. bis man in der Mitte
angekommen ist.
+---+---+---+---+---+---+---+---+
| a | b | c | d | e | f | g | h |
+---+---+---+---+---+---+---+---+
^ ^
+---------------------------+
+---+---+---+---+---+---+---+---+
| h | b | c | d | e | f | g | a |
+---+---+---+---+---+---+---+---+
^ ^
+-------------------+
+---+---+---+---+---+---+---+---+
| h | g | c | d | e | f | b | a |
+---+---+---+---+---+---+---+---+
^ ^
+-----------+
+---+---+---+---+---+---+---+---+
| h | g | f | d | e | c | b | a |
+---+---+---+---+---+---+---+---+
^ ^
+---+
+---+---+---+---+---+---+---+---+
| h | g | f | e | d | c | b | a |
+---+---+---+---+---+---+---+---+
Oder in Code
Vielen Dank dass du dir die Zeit nimmst für diese detaillierten
Erklärungen.
Wo wir grade bei Strings sind, noch zwei Fragen:
Folgendes funktioniert bei meinem Mikroprozessor einwandfrei:
1
char*s3;
2
3
sprintf(s3,"Hallo");
Dieses hier aber nicht:
1
char*s3;
2
intj=4;
3
4
sprintf(s3,"%d",j);
Woran kann das liegen?
Und wann bietet es sich an, den String gleich als Array zu deklarieren
(char s3[5]), wann sollte man nur einen Zeiger deklarieren (char* s3)?
TechInfo wrote:
> Vielen Dank dass du dir die Zeit nimmst für diese detaillierten> Erklärungen.>> Wo wir grade bei Strings sind, noch zwei Fragen:>> Folgendes funktioniert bei meinem Mikroprozessor einwandfrei:>>
1
char*s3;
2
>
3
>sprintf(s3,"Hallo");
>> Dieses hier aber nicht:>>
1
char*s3;
2
>intj=4;
3
>
4
>sprintf(s3,"%d",j);
>> Woran kann das liegen?
Weil beides falsch ist, nur beim ersten hast
du das Pech, dass es so aussieht als würde es
funktionieren.
sprintf möchte einen Speicherbereich in dem es das
Ergebnis ablegt. Ein Pointer ist aber eine Variable
die auf so einen Speicherbereich zeigen kann, selbst
aber diesen Bereich nicht zur Verfügung stellt.
1
chars1[20];
2
sprintf(s1,"Hallo");
Jetzt hat s1 einen Speicher*bereich* in dem es die Zeichen
ablegen kann.
> Und wann bietet es sich an, den String gleich als Array zu deklarieren> (char s3[5]), wann sollte man nur einen Zeiger deklarieren (char* s3)?
Ein Array nimmst du dann, wenn du eine Speicherfläche brauchst
in der gearbeitet werden kann (zb. weil dort das Ergebnis einer
Stringbehandlung abgelegt werden muss).
Einen Pointer nummst du dann wenn
* Ein Array an eine Funktion übergeben werden soll.
Arrays an sich kann man nicht übergeben. Stattdessen wird
immer die Adresse des ersten Array Elementes übergeben.
Und diese Adresse fängt die Funktion in einem Pointer
auf (Pointer == Variable die eine Speicheradresse enthält).
* Du einen konstanten Text hast, der nicht verändert werden muss.
Der Compiler muss ja natürlich den konstanten Text auch irgendwie
in dein Programm einbauen. Das heist aber auch, so ein konstanter
Text liegt schon irgendwo im Speicher. Nur hast du nicht das
Recht diesen Text zu verändern.
const char* Text = "Hallo World";
Text ist ein Zeiger, ein Pointer, enthält also die Speicher-
adresse wo denn im Speicher die Zeichenfolge "Hallo World"
abgelegt wurde.
Merke: Ein Zeiger speichert nur eine Speicheradresse. Ob unter
dieser Speicheradresse auch tatsächlich gelesen und geschrieben
werden kann, sprich: ob unter dieser Speicheradresse auch wirklich
legal auf den Speicher zugegriffen werden kann, ist nicht das
Bier des Pointers.
Stell dir eine Bibliothek vor:
In den Regalen stehen Bücher. Nur manchmal steckt auch ein
Zettel dazwischen, auf dem steht: "Zu finden in Gang A, Regal 4"
Genau das ist ein Pointer. Er sagt dir wo die eigentliche Information
zu finden ist. Ob es einen "Gang A", "Regal 4" auch tatsächlich
gibt, interessiert aber den Pointer nicht. Es ist der Job des
Bibliothekars, äh Programmierers, dafür Sorge zu tragen, dass
dieser verweis auch tatsächlich richtig ist.
(An der Stelle Gang A, Regal 4 könnte dann ein Buch stehen. In
dem Buch gibt es dann einen weiteren Zettel "Gang A, Regal 5".
Dort findet sich dann ein weiterer Zettel, ... Obwohl also
die Bücher kreuz und quer über die komplette Bibliothek
verstreut sind, definieren diese Zettel eine eindeutige
Abfolge von Büchern (zb. Titel in alphabetischer Reihenfolge).
Muss ein neues Buch eingefügt werden, so stellt man es einfach
irgendwohin, wo gerade Platz ist. Beim alphabetischen Vorgänger
dieses neuen Buches kommt dann einfach ein neuer Zettel hinein
und in das neue Buch der Zettel vom Vorgänger. Dadurch ist
die Kette nicht unterbrochen und man hat beim Einfügen in die
Reihenfolge nicht viel Arbeit. Ansonsten müsste man nämlich
alle Bücherregale umräumen um Platz für das neue Buch zu schaffen.
Wichtig ist:
Ein Pointer ist nur ein Verweis in den Speicher. Eine Pointer-
variable speichert eine Speicheradresse. Es ist dein Job dafür
zu Sorgen, dass dieser Verweis auch auf tatsächlich verwenbaren
Speicher zeigt.
char* s1;
sprintf( s1, ...
Wo zeigt denn s1 hin? Auf welche Speicheradresse? Niemand
weiss es, weil Variableninhalte zufällig sind, wenn Variablen
auf die Welt kommen.
Aber
char Buffer[20];
char* s1;
s1 = & Buffer[0];
Jetzt zeigt s1 auf den Beginn der Speicherfläche, die 'Buffer'
genannt wird. & ist der Adress-Operator und er liefert die
Speicheradresse einer Variablen.
Teil der Arraybehandlung in C ist es auch, dass Arrays in vielen
automatisch die Adresse auf das erste Element darstellen. Praktisch
immer dann wenn der Arrayname alleine (also ohne Indizierung)
vorkommt.
s1 = & Buffer[0];
und
s1 = Buffer;
sind also gleichwertig. Und daher ist es auch nicht notwendig
sprintf( &Buffer[0], " .....
zu schreiben, sondern
sprintf( Buffer, " ....
reicht völlig.
Wenn du also eine Funktion siehst, die als Arugment einen Pointer
haben möchte, heist das nicht notwendigerweise, dass du nur
eine Pointervariable anlegen musst und alles ist in Butter.
Meistens ist es so, dass dieser Argument-Pointer anzeigt, dass
die Funktion über diesen Pointer einen errechneten Wert ablegen
möchte. Nur muss man der Funktion dann aber auch die Speicheradresse
einer geeigneten Variablen geben, damit sie das tun kann.
TechInfo wrote:
> Ok, plausibel. Aber auch mit einem Speicherbereich sieht es so aus:>> Nach>>
1
>chars2[10];
2
>sprintf(s2,"%i",j);
3
>
>> ist der String leer, aber>>
1
>chars2[10];
2
>sprintf(s2,"Hallo");
3
>
>> gibt er aus.
Dann musst du schon das komplette Programm posten.
Viele Fehler haben die Eigenart, sich an ganz
anderer Stelle bemerkbar zu machen als dort
wo sie auftreten.
Konkret kann man daher nur sagen: Du hast mindestens
einen Fehler im Programm. Die Symptome die du siehst sind,
dass
char s2 [10];
sprintf(s2, "%i", j);
nicht funktioniert.
Die Ursache dafür kann aber was ganz anderes sein.
OK. Ich hatte schlimmes befürchtet, aber das ist legitim. Nur bedingte
Compilation.
Manche mit C noch nicht sehr erfahrene Leute setzen munter #defines oder
gar #include-Anweisungen irgendwo in den Quellcode ...
Probier mal %d anstelle von %i
> Kann man darauf tippen, dass bei der Implementierung des sprintf> für diesen Prozessor etwas nicht ok ist?
Möglich wärs.
Wenn das tatsächlich stimmt, und ich habe keinen Grund
daran zu zweifeln, dann wäre das für mich mitlerweile ein
Grund den ganzen Kompiler, so wie er ist, von der Festplatte
zu löschen.
Wenn die Standardlibrary solch einfache Dinge nicht hinkriegt,
wer weiss welche Überraschungen dann noch warten.