Hallo, auf einem Mikrocontrollersystem sollen unsigned int Werte in ein
unsigned char Array umgewandelt werden. Beim Debuggen ist festzustellen,
dass die Adressen von test1 und test2 gleich sind. Folglich sind die
Inhalte ebenso gleich. Woran könnte dies liegen?
Die Convert Funktion ist böse. Sie enthält einen dicken Fehler !
Ihr Array result ist lokal und nachdem die Funktion endet mausetot !
Die Funktion gibt aber einen Pointer auf die Leiche zurück. Extra Pfui
!!!
user schrieb:> sollen unsigned int Werte in ein unsigned char Array umgewandelt werden
Ich interpretiere das so, dass du da aus einem Integer einen
ASCII-String machen willst.
user schrieb:> Beim Debuggen ist festzustellen, dass die Adressen von test1 und test2> gleich sind.
Zeig doch mal, was du da meinst..
> Folglich sind die Inhalte ebenso gleich.
Welche Inhalte?
> Woran könnte dies liegen?
Die Adressen von test1 und test2 können nicht gleiche sein. Nur die
Adressen, die dort an den Adressen gespeichert sind, können gleich sein.
Und genau das passiert, denn deine Convert() Funktion gibt beide Male
den selben Zeiger zurück.
Noch schlimmer: das ist ein Zeiger auf eine Variable result, die nur
temporär in der Convert() Funktion existiert!
Eigentlich ist es sogar ein Doppelfehler:
1. Die Funktion gibt einen Zeiger auf eine lokale Variable zurück,
welche nach Beenden der Funktion stirbt.
2. Macht man die Variable result static, damit sie den Aufruf überlebt,
nützt dies auch nichts. In diesem Fall würde auch 2x derselbe Pointer
zurückgegeben werden.
Eine Lösung:
In der aufrufenden Funktion (z.B. main() jeweils den Buffer result1 und
result2 runtergeben, damit Convert() diese füllen kann.
Die beste Lösung:
Der TO sollte sich ein C-Buch schnappen und die Programmiersprache
systematisch lernen. So wird das nix.
Joe F. schrieb:> unsigned char result1[12];> unsigned char result2[12];> [...]> sprintf(result,"%d.%d.%d.%d", x[3], x[2], x[1], x[0]);> }
Autsch! Buffer-Overflow!
Nur 12 Zeichen Platz für die IP-Adresse (denn um solch eine geht es ja
wohl) "123.123.123.123"!
Korrekt wäre:
unsigned char result1[16];
unsigned char result2[16];
Denn wir brauchen
4 x 3 Zeichen für die Dezimalzahlen
3 x 1 Zeichen für die Punkte
1 x 1 Zeichen für die Terminierung
===================================
16 Zeichen insgesamt.
Frank M. schrieb:> Autsch! Buffer-Overflow!>> Nur 12 Zeichen Platz für die IP-Adresse (denn um solch eine geht es ja> wohl) "123.123.123.123"!
Ich sehe gerade: Der TO hat denselben Fehler gemacht im
Urprungs-Programm.
user schrieb:> Ich muss eine ip string "192.168.0.34" in ein char Array umwandeln.
Das meinst Du jetzt nicht ernst, oder? Ein ip string ist ein char Array.
Du meinst tatsächlich: "Ich muss eine 32-Bit-IP-Adresse in einen IP
String bzw. char-Array umwandeln".
Paul schrieb:> was würde den int Convert ausgeben?> Nur 0 oder 1 für Funktion Fehlerfrei verarbeitet?
Ja, korrekt. Aber im Moment findet ja keine Prüfung in der Funktion
statt. Man könnte natürlich prüfen, ob der Buffer, der heruntergegeben
wird, überhaupt die insgesamt 16 Zeichen fassen kann. Dann wäre 0 oder 1
auf jeden Fall sinnvoll - oder Unix-like: 0 = OK, -1 = Error. Aber das
ist Geschmackssache.
user schrieb:> Ich muss eine ip string "192.168.0.34" in ein char Array> umwandeln.
Und weil diese konkrete IP-Adresse nur 12 Stellen hat, machst Du Dein
char-Array auch nur 12 Stellen lang? Du hast einfach obige Adresse
abgezählt?!? Es gibt durchaus IP-Adressen, die mehr Ziffern haben, z.B.
"192.168.178.100".
Du musst immer die maximal mögliche Länge beachten! Immer! Das wäre (wie
ich oben bereits schrieb) 16, denn Du hast auch noch vergessen, dass das
Terminierungszeichen '\0' auch noch ein weiteres Byte (=Zeichen) in dem
Array beansprucht.
user schrieb:> sprintf(&result[0], ...
Das kannst Du auch so schreiben:
1
sprintf(result,...
Ist einfacher und lesbarer. Und man muss nicht soviel tippen. Beide
Ausdrücke
&result[0]
und
result
sind äquivalent.
Allgemein gilt:
&result[n]
ist äquivalent zu
result + n
Steht in Deinem C-Buch unter Pointer-Arithmetik.
user schrieb:> Ok vielen Dank für eure untersützung...
--Gibt es einen besonderen Grund, dass das Ergebnis ein (unsigned char*)
statt eines (üblichen) char* ist?
--Oben wurde auf uint32_t hingewiesen. Damit ist man auf der sicheren
Seite.
--Anstatt einen Kommentar zu schreiben, dass der Aufrufer einen 16-Byte
Puffer zur Verfügung stellen muss, kann man das auch im Funktionskopf
definieren.
Mikro 7. schrieb:> --Anstatt einen Kommentar zu schreiben, dass der Aufrufer einen 16-Byte> Puffer zur Verfügung stellen muss, kann man das auch im Funktionskopf> definieren.
Dafür holst Du aber direkt mit der Keule aus und übergibst statt dem
Array einen Pointer auf ein Array - das ergibt eine Dimension mehr.
Ob das der TO mit seinem Wissenstand noch versteht? Das ist schon
"höhere Kunst" ;-)
Außerdem schützt es nicht vor Buffer-Overdflow. Ersetze alle drei
Vorkommnisse von [16] durch [12]. Das Programm wird nachwievor
anstandslos übersetzt, auch mit -Wall.
Frank M. schrieb:> das ergibt eine Dimension mehr.
Wo ist das Problem?
> Außerdem schützt es nicht vor Buffer-Overdflow.
Doch. Sogar zur Compilezeit!
> Ersetze alle drei> Vorkommnisse von [16] durch [12]. Das Programm wird nachwievor> anstandslos übersetzt, auch mit -Wall.
"Alle drei"... also auch in der Funktionsdefinition... das ist jetzt
nicht dein ernst, oder?
Mikro 7. schrieb:> Wo ist das Problem?Ich habe da kein Problem, ich arbeite teilweise mit drei oder gar vier
Sternchen in Funktionsparametern ;-)
Ich sehe das Problem eher beim TO, dessen Wissensstand man ganz gut
abschätzen kann.
> "Alle drei"... also auch in der Funktionsdefinition... das ist jetzt> nicht dein ernst, oder?
Doch, ist mein Ernst.
Schau Dir das Ausgangsposting an. Dort schrieb der TO innerhalb von
Convert():
unsigned char result[12] = {0};
Wenn ihn hier keiner darauf hingewiesen hätte, dass da 16 und nicht 12
stehen muss, warum hätte er dann im Funktionsparameter von convert()
dann [16] statt 12 geschrieben? Göttliche Eingebung? Nein, er hätte bei
Verwendung Deiner Methode auch im Funktionsparameter 12 geschrieben und
einen Buffer-Overflow provoziert.
Außerdem hat Dein Vorschlag neben der erhöhten Komplexität noch einen
Nachteil: Ich kann lediglich Buffer übergeben, die exakt 16 Bytes groß
sind. Einen größeren kann ich dann nicht übergeben, nämlich wenn ich
nach dem Aufruf noch etwas anhängen will, wie z.B. " ist meine
IP-Adresse".
Das folgende schützt tatsächlich vor einem Buffer-Overflow - auch wenn
der Programmierer die 12 statt 16 verwendet:
Hier kannst Du alle Stellen von [16] durch [12] ersetzen. Das Programm
wird nicht abstürzen oder das RAM irgendwo zerbröseln. Ok,
unvollständige Werte wird es ausgeben, aber das ist halt ein
Programmfehler. Aber es passiert kein Buffer-Overflow, der z.B. von
Hackern ausgenutzt werden kann, um Code ins Programm zu injizieren - wie
es schon oft in der Vergangenheit vorgekommen ist.
Außerdem kannst Du auch größere Buffer übergeben - noch ein Vorteil.
Frank M. schrieb:> Ich sehe das Problem eher beim TO, dessen Wissensstand man ganz gut> abschätzen kann.
In C: Wenn man etwas modifizieren will, übergibt man einen Pointer
darauf. Ist für einen Einsteiger straight forward. So auch in meinem
Beispiel.
Schwerer zu verstehen ist hingegen, dass ein Array implizit einen
Pointer auf das erste Element darstellt, Arrays in C eigentlich gar
keine Arrays sind (keine Längeninfo zur Laufzeit) und Arrays nicht
by-value übergeben werden können... Aber da muß man dann iwann durch.
;-)
> Wenn ihn hier keiner darauf hingewiesen hätte, dass da 16 und nicht 12> stehen muss, warum hätte er dann im Funktionsparameter von convert()> dann [16] statt 12 geschrieben? Göttliche Eingebung?
Die Übergabe von Pointern auf statische (fixe) Arrays ermöglicht das
Festellen von "ungültigen" (anderen) Puffergrößen zur Compilezeit. Nicht
mehr und nicht weniger.
Dieses Feature mit deiner Begründung abzulehen... kopfschüttel> Außerdem hat Dein Vorschlag...
Da stimme ich dir zu. Man kann Größen auch dynamisch zur Laufzeit
verwalten (häufig muss man das sogar). Der Programmierer muss
entscheiden, was in seiner Anwendung sinnvoll ist.
Edit: Spelling
Mikro 7. schrieb:> In C: Wenn man etwas modifizieren will, übergibt man einen Pointer> darauf.
Genau: Man will in diesem Beispiel chars modifizieren, also übergibt man
einen Pointer drauf, das wäre also (char *). Du übergibst aber einen
Pointer auf ein ganzes Array, also eine Dimension mehr, als nötig ist.
Übergibst Du auch (int **), wenn Du lediglich ints modifizieren willst?
> Dieses Feature mit deiner Begründung abzulehen... kopfschüttel
Okay, vielleicht überzeugt Dich folgender Blickwinkel:
Es gab und gibt Buffer-Overflows in C. Die gibt es und die wird es auch
zukünftig geben. Der Grund ist meistens darin zu finden, dass der
Programmierer
1. die erforderliche Länge eines Buffers falsch berechnet hat
oder
2. dass die Funktion, die den Buffer füllt, nicht auf die Länge
des Buffers achtet.
Dein Beispiel schützt (bedingt) vor 2, aber nicht vor 1.
Mein Beispiel, das wesentlich simpler gestrickt ist und deshalb auch von
einem fortgeschrittenen Anfänger verstanden wird, deckt beide Punkte ab.
Wenn Du auch diese Argumentation ablehnst, dann sag mir bitte, was an
meiner einfacheren Lösung schlechter ist als an Deiner komplexeren. Auch
nach 32 Jahren C-Programmierung bin ich immer noch bereit, dazuzulernen
:-)
Und sag mir bitte, warum Standardfunktionen strncpy(), strncat(),
snprintf() usw. alle so arbeiten wie ich und nicht so, wie Du? Waren das
alles Idioten, die die libc geschrieben haben?
user schrieb:> Hallo, auf einem Mikrocontrollersystem
(...)
in Anbetracht dessen, werfe ich mal eine schmale Lösung ganz ohne printf
in die Runde.
In solchen Umgebungen sollte der Programmierer natürlich genau wissen,
was er tut (meine Meinung). Spart Ausführungszeit und Speicherplatz.
Joe F. schrieb:> Spart Ausführungszeit und Speicherplatz.
nicht wirklich. Auf Atmels die nicht sinnvoll shiften und dividieren
können ist der code sehr ungünstig.
Offensichtlich haben wir unterschiedliche "Aufassungen".
Frank M. schrieb:> Man will in diesem Beispiel chars modifizieren, also übergibt man> einen Pointer drauf, das wäre also (char *).
Nein. Es soll ein char Array modifiziert werden.
Mikro 7. schrieb:> char s1[16] ;
Also wird ein Zeiger darauf übergeben.
Mikro 7. schrieb:> convert(i1,&s1) ;> Du übergibst aber einen> Pointer auf ein ganzes Array, also eine Dimension mehr, als nötig ist.
Es wird die Adresse von s1 übergeben. (Wie übergibt man eine zusätzliche
Dimensionen von s1?)
> Übergibst Du auch (int **), wenn Du lediglich ints modifizieren willst?
Nein. Du?
Ich würde int(*)[] übergeben. Das ist was anderes als (int**). (So
langsame vermute ich was du hier als Dimension mißverstehst.)
> Dein Beispiel schützt (bedingt) vor 2, aber nicht vor 1.
Es gibt einen Kontrakt zwischen Aufrufer und Aufgerufenen. Eine Funktion
muss ihre Zusage einhalten. Wenn du dem nicht zustimmst, können wir an
diesem Punkt die Diskussion einstellen.
> Mein Beispiel, das wesentlich simpler gestrickt ist und deshalb auch von> einem fortgeschrittenen Anfänger verstanden wird, deckt beide Punkte ab.
Und ich habe auch zugestimmt, dass man da so machen kann (und meist so
gemacht wird). Der Rest ist... Spekulation.
> Wenn Du auch diese Argumentation ablehnst, dann sag mir bitte, was an> meiner einfacheren Lösung schlechter ist als an Deiner komplexeren.
Nee... Du machst schon wieder Spass?!
Deine Lösung ist von Laufzeit, Quelltext und Zielcode komplexer. Dafür
ist sie aber auch genereller.
Mein Beispiel erscheint dir vielleicht komplexer, weil du möglicherweise
(wie imho die meisten C Programmierer) nicht gewohnt bist, mit Pointer
auf Arrays zu arbeiten.
> Auch> nach 32 Jahren C-Programmierung bin ich immer noch bereit, dazuzulernen> :-)
Finde ich gut.
> Und sag mir bitte, warum Standardfunktionen strncpy(), strncat(),> snprintf() usw. alle so arbeiten wie ich und nicht so, wie Du? Waren das> alles Idioten, die die libc geschrieben haben?
Deine Argumentation ist also: "Weil ich nicht weißt warum das so ist,
kann es nicht (gut) sein." Daran müssen wir noch arbeiten. ;-)
Na gut, hier ein Grund: Auch wenn sie wollten könnten sie es nicht, weil
die Arraygröße (wie bereits oben gesagt) zur Laufzeit nicht mehr
vorhanden ist. Man könnte es mit Makros lösen. In C++ kann man es mit
Templates lösen.
Als Schlusspunkt (für mich ist er das auf jeden Fall): Ein Pointer auf
ein Array ist eine Möglichkeit, nich passende Buffer zur Compilezeit
zu entdecken. Das funktioniert nur in bestimmten Situationen (fixes
Array) und ist kein Allheilmittel. Der Programmierer muss wissen
ob/wann/wie er diese Möglichkeit nutzt.
Schönes Wochende!
Mikro 7. schrieb:> Mein Beispiel erscheint dir vielleicht komplexer, weil du möglicherweise> (wie imho die meisten C Programmierer) nicht gewohnt bist, mit Pointer> auf Arrays zu arbeiten.
Nur als letztes Wort: An diesem Satz habe ich verstanden, dass Du zu
flüchtig liest.
Beispiele:
Frank M. schrieb:> Ich habe da kein Problem, ich arbeite teilweise mit drei oder gar vier> Sternchen in Funktionsparametern ;-)
Was hast Du "drei oder gar vier Sternchen" nicht verstanden? Ich bin mit
zum Beispiel mit (struct foo ** bar[i]) auf Du und Du. Das ist mein
täglich Brot.
Frank M. schrieb:> Ich sehe das Problem eher beim TO, dessen Wissensstand man ganz gut> abschätzen kann.
Warum beziehst Du diesen Satz auf mich, wenn ich explizit sage, dass es
zu komplex für den TO ist, ich selbst aber kein Problem damit habe?
Antwort:
Du liest nur quer, aber schießt wild um Dich. Das zeugt von mangelndem
Respekt gegenüber Deinem Leser.
Du hast auch nicht verstanden, dass Deine Lösung, ein Array als
Parameter konkret in der Länge anzugegben und auch nur konkret exakt so
zu erlauben, eine Speziallösung ist, die meist nicht praxistauglich ist.
Für eine lib wäre sie sogar untauglich.
Würdest Du jetzt für jede erdenkliche Array-Größe eine eigene
strcpy-Funktion schreiben, damit Du die Länge exakt in der Form [16]
angeben kannst? Da hättest Du aber eine zeitlang zu tun. ;-)
Immerhin hast Du dynamische Allokation schon selbst angesprochen. Da ist
schon Ende im Schacht und Du kannst Deine Lösung in die Tonne treten.
Aber egal, was Du von mir denkst. Du behandelst Deine Leser von oben
herab und daher bist Du kein adäquater Gesprächspartner für mich.
Daher: Bye :-)
Mikro 7. schrieb:> --Anstatt einen Kommentar zu schreiben, dass der Aufrufer einen 16-Byte> Puffer zur Verfügung stellen muss, kann man das auch im Funktionskopf> definieren.> #include <stdio.h>> #include <stdint.h>>> void convert(uint32_t i,char(*buffer)[16])
soll die 16 für den Sting sein, 4 Byte a 3 Stellen + '.'?
wo ist die Terminierungs NULL, ich glaube SO werde ich C nie verstehen.
ich brauch 16 + 1 Zeichen, macht für mich buffer[17] mit char von
buffer[0] - buffer[15] und Terminierung auf buffer[16]=0;
und das geht nur bis IVP4 später mit IVP6 kracht es wieder
Joachim B. schrieb:> soll die 16 für den Sting sein, 4 Byte a 3 Stellen + '.'?>> wo ist die Terminierungs NULL, ich glaube SO werde ich C nie verstehen.>> ich brauch 16 + 1 Zeichen, macht für mich buffer[17] mit char von> buffer[0] - buffer[15] und Terminierung auf buffer[16]=0;
Rechnen und Lesen will gelernt sein. Hier hats jemand schon
vorgerechnet:
Beitrag "Re: Konvertierung unsigned int --> unsigned char*"
guest schrieb:> Rechnen und Lesen will gelernt sein. Hier hats jemand schon> vorgerechnet:
arg.......
dem letzten Byte fehlt ja der Punkt, wird durch NULL ersetzt
macht 16 Zeichen, OK OK ich sehe es ein, ich ging von 4 x 4 + NULL aus
Ist warm hier und meine grauen Zellen sind auch schon alt.