Forum: Compiler & IDEs Integer-Wert in ASCII-char


von TechInfo (Gast)


Lesenswert?

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

von johnny.m (Gast)


Lesenswert?

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

von TechInfo (Gast)


Lesenswert?

Den Xilinx Microblaze auf einem SpartanIIE.

In der mitgelieferten stdlib ist die Funktion nicht definiert (atoi 
dagegn schon).

von TechInfo (Gast)


Lesenswert?

Gibt es keine andere Möglichkeit? Wie ist denn die Funktion itoa 
implementiert?

von johnny.m (Gast)


Lesenswert?

Schau mal da:
Beitrag "Integer-Zahl in String mit bestimmter Zeichenlänge"
Da ist grad eben eine Methode beschrieben, wie es ohne 
Bibliotheksfunktionen geht.

von TechInfo (Gast)


Lesenswert?

Da ging es aber doch darum, dass er führende Nullen haben wollte wenn 
die Zahl kleiner ist als der String. Oder?

von johnny.m (Gast)


Lesenswert?

Das geht aber auch ohne führende Nullen. Die Umwandlung ist im Prinzip 
die selbe...

von TechInfo (Gast)


Lesenswert?

Thx, hab's schon ausprobiert. Jetzt warte ich noch auf die Erklärung ;)

von Karl H. (kbuchegg)


Lesenswert?

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.

von TechInfo (Gast)


Lesenswert?

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

von TechInfo (Gast)


Lesenswert?

Morgen,

gibt es denn auch eine ähnliche Möglichkeit für float-Variablen?

von Karl H. (kbuchegg)


Lesenswert?

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

von TechInfo (Gast)


Lesenswert?

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?

von TechInfo (Gast)


Lesenswert?

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
  }

von Karl H. (kbuchegg)


Lesenswert?

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
void foo( int z, char* Buffer )
2
{
3
  char s1[7];   // 10 Stellen reichen für einen int leicht
4
                // aus. Der Wertebereich ist ja -32768 bis +32767
5
  int j;
6
  int i = 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
1
  for( j = 0; j < i / 2; ++j ) {
2
    char tmp = s1[j];
3
    s1[j] = s1[i-j-1];
4
    s1[i-j-1] = tmp;
5
  }

  

von Karl H. (kbuchegg)


Lesenswert?

Ich habe die C-FAQ um eine entsprechenden Abschnitt
ergänzt:

http://www.mikrocontroller.net/articles/FAQ#Eigene_Umwandlungsfunktionen

von TechInfo (Gast)


Lesenswert?

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
int j=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)?

von TechInfo (Gast)


Lesenswert?

In die erste sprintf-Anweisung muss natürlich noch eine DUmmy-Variable,

sprintf(s3, "Hallo", j);

von Karl H. (kbuchegg)


Lesenswert?

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
> int j=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
  char s1[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.

von TechInfo (Gast)


Lesenswert?

Ok, plausibel. Aber auch mit einem Speicherbereich sieht es so aus:

Nach
1
char s2 [10];
2
sprintf(s2, "%i", j);

ist der String leer, aber
1
char s2[10];
2
sprintf(s2, "Hallo");

gibt er aus.

von Karl H. (kbuchegg)


Lesenswert?

TechInfo wrote:
> Ok, plausibel. Aber auch mit einem Speicherbereich sieht es so aus:
>
> Nach
>
>
1
> char s2 [10];
2
> sprintf(s2, "%i", j);
3
>
>
> ist der String leer, aber
>
>
1
> char s2[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.




von TechInfo (Gast)


Lesenswert?

1
int main (void) {
2
3
...Präprozessoranweisungen....
4
5
6
  int z=278;
7
  char s1[10];
8
  
9
  sprintf(s1, "%i", z);
10
  puts(s1);
11
12
...Präprozessoranweisungen....
13
14
return 0;
15
}

  

von TechInfo (Gast)


Lesenswert?

Schreibe ich
1
sprintf(s1, "Hallo %i", z);

wird wiederum Hallo+Leerzeichen ausgegeben. Er kommt nur mit der 
Variablen nicht klar.

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

Was für Präprozessoranweisungen werden denn da INNERHALB von main() 
verwendet?

von TechInfo (Gast)


Lesenswert?

Programmiere einen Microblaze-Softcore.

Daten- und Instruktionscache werden aktiviert, falls das vorher im 
Design so festgelegt worden ist:
1
      #if XPAR_MICROBLAZE_0_USE_ICACHE
2
        microblaze_init_icache_range(0, XPAR_MICROBLAZE_0_CACHE_BYTE_SIZE);
3
        microblaze_enable_icache();
4
      #endif
5
6
      #if XPAR_MICROBLAZE_0_USE_DCACHE
7
        microblaze_init_dcache_range(0, XPAR_MICROBLAZE_0_DCACHE_BYTE_SIZE);
8
        microblaze_enable_dcache();
9
      #endif

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

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

von TechInfo (Gast)


Lesenswert?

Kann man darauf tippen, dass bei der Implementierung des sprintf für 
diesen Prozessor etwas nicht ok ist? printf funktioniert übrigens auch 
nicht.

von Karl H. (kbuchegg)


Lesenswert?

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.

von TechInfo (Gast)


Lesenswert?

Ich habe auch ein Problem mit den Funktionen atoi und atof.
1
char s[10]="1234";
2
int i=0;
3
4
i=atoi(s);

Er schreibt dann in i jeweils nur die letzte Ziffer, in diesem Fall 4. 
Das selbe bei atof.

von Karl H. (kbuchegg)


Lesenswert?

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.

von TechInfo (Gast)


Lesenswert?

Blöd nur, dass er mir für die Diplomarbeit vorgegeben ist. Ich glaube 
ich werde mal bei Xilinx anrufen...

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
Noch kein Account? Hier anmelden.