Frage zu Pointern (Arduino)

OP #4574449
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
int i = 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
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.
OP
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.
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.
OP
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().
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.
1
int i = 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); falsch
9
  arg[i] = token;
10
  token = strtok(NULL, " ");
11
  i++;
12
}
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.
OP
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.
OP
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.

Antwort schreiben

Eine eigene Frage? Dieser Thread ist über 10 Jahre alt. Stell sie in einem eigenen Thread – dort lesen mehr Leute mit, und sie wird öfter und schneller beantwortet.

Neuen Thread starten

Eine Lösung, Ergänzung oder Rückmeldung zu diesem Thread? Dann antworte hier.

Formatierung
  • [c]C-Code[/c]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] – Link zu einem Artikel
  • Verweis auf einen anderen Beitrag: Rechtsklick auf dessen Beitrags-ID (#8078628), „Adresse kopieren“, in den Text einfügen

Mehr zur Formatierung

Längeren Sourcecode als Anhang, nicht in den Text. JPG nur für Fotos und Scans; Zeichnungen und Screenshots als PNG oder GIF. Siehe Bildformate.

Schon registriert? Anmelden

Wird nicht angezeigt. Wir schicken dir einen Link; sobald du damit bestätigst, erscheint dein Beitrag.

Mit dem Abschicken legst du einen kostenlosen Account an und erkennst die Nutzungsbedingungen und die Datenschutzerklärung an.