Hallo,
ich habe ein unsigned char* irdata. Das bekommt einen Pointer auf einen
"string" wie "NEC 0xBE414DB2 32" zugewiesen.
Diesen string teile ich auf mit
1
inti=0;
2
char*arg[3];
3
4
char*irString=strdup(irData);
5
char*token=strtok(irString," ");
6
7
while(token!=NULL){
8
strcpy(arg[i],token);
9
token=strtok(NULL," ");
10
i++;
11
}
Wenn ich das danach ausgebe mit
1
Serial.print("Protocol:");Serial.print(*arg[0]);
2
Serial.print(" Code:");Serial.print(*arg[1]);
3
Serial.print(" Bits:");Serial.print(*arg[2]);
bekomme ich
1
Protocol:N Code:0 Bits:3
Lasse ich die Sternchen weg
1
Serial.print("Protocol:");Serial.print(arg[0]);
2
Serial.print(" Code:");Serial.print(arg[1]);
3
Serial.print(" Bits:");Serial.print(arg[2]);
bekomme ich, wie gewollt
1
Protocol:NEC Code:0xBE414DB2 Bits:32
Wie Pointer funktionieren ist mir eigentlich klar. Aber hier verstehe
ich es nicht. Warum bekomme ich bei der Version mit Sternchen nur
jeweils die ersten Zeichen?
Gruß
Robert
Weil der * operator den pointer derefenziert und die Speicherstelle
ausliest, auf die der pointer zeigt. Der Inhalt dieser Speicherstelle
ist genau ein character.
Alles weitere kannst du hier nachlesen :
http://www.lysator.liu.se/c/bwk-tutor.html#pointers
Ein String ist in C immer ein Zeichen länger als erwartet, weil noch ein
0x00 als Ende-Kennung angehängt wird. Daher solltest du dein arg[3] noch
einmal überdenken.
Mehr Platz als notwendig zu reservieren, hat selten geschadet. Aber zu
wenig reservierter Platz und darauf folgendes Schreiben irgendwo im
Speicher kann zu längeren Debug Sitzungen führen.
Besten Dank.
Ist es also so richtig?:
*arg[0] ist der Wert des ersten Zeichens von "NEC". Damit wird nur das N
ausgegeben.
arg[0] zeigt auf das erste Byte von "NEC" und Serial.print() gibt das
Array von dort aus aus bis es \0 gefunden hat.
Aber was genau bedeutet nun dereferenzieren?
Für mich hört es sich an, als würde der pointer seine Referenz auf die
Variable verlieren. Das ist doch aber nicht der Fall, oder?
*pointer gibt doch einfach den Wert der Variablen aus, auf die pointer
zeigt.
Georg G. schrieb:>Daher solltest du dein arg[3] noch einmal überdenken.
Warum denn das? char* arg[3] ist ein Array von drei Pointern und ich
habe 3 Teilstrings. Das passt doch.
Robert K. schrieb:> Georg G. schrieb:>>Daher solltest du dein arg[3] noch einmal überdenken.>> Warum denn das? char* arg[3] ist ein Array von drei Pointern und ich> habe 3 Teilstrings. Das passt doch.
Und wo zeigen die 3 Pointer hin ?
Robert K. schrieb:> The D. schrieb:>> Und wo zeigen die 3 Pointer hin ?>> Na das bekommen sie doch durch strtok() zugewiesen.> Oder worauf willst du hinaus?
Und danach machst du strcpy(arg[i], token). Damit kopiert strcpy den
String auf den token zeigt an die Speicherstelle auf die arg[i] zeigt.
Also ins Nirvana weil die pointer nicht initialisiert sind. Du hast
Glück, dass das Programm ueberhaupt was tut.
Robert K. schrieb:> ein Array von drei Pointern und ich> habe 3 Teilstrings. Das passt doch.
Wesentlich sinnvoller wäre Platz für 3 Strings, also arg[3][32] als
Beispiel (und dann natürlich den Rest der Kopierorgie entsprechend
anpassen).
The D. schrieb:> Und danach machst du strcpy(arg[i], token). Damit kopiert strcpy den> String auf den token zeigt an die Speicherstelle auf die arg[i] zeigt.> Also ins Nirvana weil die pointer nicht initialisiert sind. Du hast> Glück, dass das Programm ueberhaupt was tut.
Ok, und wie sollte ich sie am besten initialisieren?
Ich habe gelesen, man sollte pointer immer mit 0 initialisieren.
Aber wenn ich das mach resettet der Controller wenn er in die while
Schleife kommt.
Georg G. schrieb:> Robert K. schrieb:>> ein Array von drei Pointern und ich>> habe 3 Teilstrings. Das passt doch.>> Wesentlich sinnvoller wäre Platz für 3 Strings, also arg[3][32] als> Beispiel (und dann natürlich den Rest der Kopierorgie entsprechend> anpassen).
Nein, sinnvoller wäre es, den arg array aus const pointern bestehen zu
lassen, alle Elemente hübsch auf NULL zu initialisieren, ihnen das
Ergebnis der strtok Operation zuzuweisen und am Ende nicht das free auf
irString zu vergessen.
Robert K. schrieb:> The D. schrieb:>> Und danach machst du strcpy(arg[i], token). Damit kopiert strcpy den>> String auf den token zeigt an die Speicherstelle auf die arg[i] zeigt.>> Also ins Nirvana weil die pointer nicht initialisiert sind. Du hast>> Glück, dass das Programm ueberhaupt was tut.>> Ok, und wie sollte ich sie am besten initialisieren?> Ich habe gelesen, man sollte pointer immer mit 0 initialisieren.> Aber wenn ich das mach resettet der Controller wenn er in die while> Schleife kommt.
Genau, weil die pointer nämlich auf keinen Speicher zeigen. Entweder du
weist allen drei pointern die Adresse eine ausreichend langen char
arrays zu, oder du benutzt kein strcpy().
The D. schrieb:> das> Ergebnis der strtok Operation zuzuweisen
Und wie lang sind dann die Teilstrings, wo enden sie? Das wird nicht
ganz das gewünschte Ergebnis erzielen.
Georg G. schrieb:> The D. schrieb:>> das>> Ergebnis der strtok Operation zuzuweisen>> Und wie lang sind dann die Teilstrings, wo enden sie? Das wird nicht> ganz das gewünschte Ergebnis erzielen.
Lies mal die Beschreibung von strtok durch. strtok modifiziert den
Original-String und fuegt null chars an die Stelle von whitespace ein.
Der gelieferte Zeiger zeigt also auf den Speicher des Originalstrings +
ggf einen offset.
arg[i] zeigt auf Strings innerhalb von irString.
Solange irString nicht durch free freigegeben wird (was irgendwann
pasieren muss), sind auch die Zeiger von arg gültig.
The D. schrieb:> Lies mal die Beschreibung von strtok durch. strtok modifiziert den> Original-String und fuegt null chars an die Stelle von whitespace ein.> Der gelieferte Zeiger zeigt also auf den Speicher des Originalstrings +> ggf einen offset.
Stimmt. Ist hier
http://www2.informatik.uni-halle.de/lehre/c/c_strtok.html, wie ich
finde, recht verständlich beschrieben.
Dirk B. schrieb:>
1
> while(token != NULL){
2
> // strcpy(arg[i], token); falsch
3
> arg[i] = token;
4
> token = strtok(NULL, " ");
5
> i++;
6
> }
7
>
Macht Sinn wenn ich hier
http://www2.informatik.uni-halle.de/lehre/c/c_strtok.html lese.
arg[i] erhält einfach den gleichen Wert wie token und zeigt somit auf
den gleichen Speicherbereich.
Mit strcpy() wird der Inhalt des Speichers kopiert und je nach dem wo
arg[0] hin zeigt könnte es z.B. von arg[1] überschrieben werden.
Richtig so?
Robert K. schrieb:> Mit strcpy() wird der Inhalt des Speichers kopiert und je nach dem wo> arg[0] hin zeigt könnte es z.B. von arg[1] überschrieben werden.> Richtig so?
Theoretisch möglich, aber wahrscheinlicher ist, dass das Programm
abstürzt, weil schon der Versuch an die Adresse in arg[0] einen String
zu kopieren etwas schief geht. Ganz generell ist strtok so sinnvoll wie
ein Blinddarm. Die Funktion modifiziert den Quellstring und speichert
sich in einer internen Variablen, wo die vorherige Operation aufgehört
hat. Damit ist es unmöglich strtok gleichzeitig auf mehreren Strings
anzuwenden. Außerdem würde ich davon abraten auf einem Mikrocontroller
mit beschränkten Ressourcen wie dem Arduino den heap zu benutzen, also
z.B. malloc, free und strdup. Besser du deklarierst alle notwendigen
Variablen direkt selbst und behältst damit die Kontrolle über den
verbrauchten Speicher.
Besten Dank für die Tips!
The D. schrieb:> Ganz generell ist strtok so sinnvoll wie> ein Blinddarm. Die Funktion modifiziert den Quellstring und speichert> sich in einer internen Variablen, wo die vorherige Operation aufgehört> hat. Damit ist es unmöglich strtok gleichzeitig auf mehreren Strings> anzuwenden.
Bei mir bekommt es immer nur den einen String. Daher sollte das kein
Problem sein.
The D. schrieb:> Außerdem würde ich davon abraten auf einem Mikrocontroller> mit beschränkten Ressourcen wie dem Arduino den heap zu benutzen, also> z.B. malloc, free und strdup. Besser du deklarierst alle notwendigen> Variablen direkt selbst und behältst damit die Kontrolle über den> verbrauchten Speicher.
Eigentlich würde ich es jetzt gern so lassen. Funktioniert ja. Oder
meinst du es könnte ernsthaft zu einem Problem werden? Das ganze ein
MySensors Node zum Senden und Empfangen von IR-Signalen. Der bekommt nur
ab und an mal ein paar Strings geschickt um 2-3 Geräte ein- bzw.
auszuschalten.
Ich kann das leider überhaupt nicht einschätzen.
Letztendlich werden die Variablen und Zeiger beim Aufruf der
Empfangsroutine jedes mal neu initialisiert. Das heißt doch eventuell
benutzter Speicher wird wieder überschrieben. Nach meinem Verständnis
sollte das dann keine Probleme geben.
Robert K. schrieb:>> Bei mir bekommt es immer nur den einen String. Daher sollte das kein> Problem sein.
Solange du immer nur einen String zur Zeit zerlegst ist das ok.
>> Eigentlich würde ich es jetzt gern so lassen. Funktioniert ja. Oder> meinst du es könnte ernsthaft zu einem Problem werden? Das ganze ein> MySensors Node zum Senden und Empfangen von IR-Signalen. Der bekommt nur> ab und an mal ein paar Strings geschickt um 2-3 Geräte ein- bzw.> auszuschalten.> Ich kann das leider überhaupt nicht einschätzen.> Letztendlich werden die Variablen und Zeiger beim Aufruf der> Empfangsroutine jedes mal neu initialisiert. Das heißt doch eventuell> benutzter Speicher wird wieder überschrieben. Nach meinem Verständnis> sollte das dann keine Probleme geben.
Du musst au jeden Fall an der richtigen Stelle in deinem code noch
free(irString); aufrufen oder dein uC wird im Nu nicht mehr genug
Speicher haben. Dann liefert strdup einen Null pointer zurueck und dir
fliegt dein Programm um die Ohren.
Der Rat den heap nicht zu benutzen heißt nicht, dass es falsch ist ihn
zu benutzen. Nur wenn du dir als Anfänger schon über die Funktion von
pointern im Unklaren bist solltest du diesen Bereich ersteinmal meiden.