Forum: Mikrocontroller und Digitale Elektronik C: möchte String Zeichen für Zeichen analysieren und abschneiden


von panca (Gast)


Lesenswert?

Hallo

ich möchte einen String Zeichen für Zeichen parsen und die analysierten 
Zeichen dann abschneiden. Dazu übergebe ich den String einer 
Unterfunktion. Dort drin wird der Zeiger "line" auch schön auf das 
nächste Zeichen gesetzt, in der Main steht der Zeiger dann (beim 2. 
Aufruf) aber wieder am Anfang. Als ob die Unterfunktion nur mit einer 
Kopie arbeitet.
Woran könnte das liegen?
1
void Foo(char* line)
2
{
3
  if (*line== 'A')
4
  {
5
    // tuwas
6
  }
7
  
8
  line++; // 1. Zeichen Abschneiden, restlichen String analysieren
9
}
10
11
void main()
12
{
13
  char line[256] = "HALLO";
14
15
  Foo(linie);
16
  // ich hätte erwartet, dass danach nur noch ein "ALLO" in line steht.
17
  Foo(linie);
18
19
}

von Peter II (Gast)


Lesenswert?

panca schrieb:
> // ich hätte erwartet, dass danach nur noch ein "ALLO" in line steht.

der Zeiger line wird als Wert übergeben und ist damit eine Kopie.

Entweder den neuen Zeiger als Return zurückgeben oder als Zeiger 
übergeben. (ja Zeiger auf Zeiger)

von BlaBla (Gast)


Lesenswert?

No, Foo legt einen neuen Pointer an. Beim verlassen ist alles wieder 
futsch.

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

So wird ein Schuh daraus:
1
char * Foo(char* str)
2
{
3
  if (*str == 'A')
4
  {
5
    // tuwas
6
  }
7
  
8
  str++; // 1. Zeichen Abschneiden, restlichen String analysieren
9
  return str;
10
}
11
12
void main()
13
{
14
  char * line = "HALLO";
15
16
  line = Foo(line); // line zeigt nun auf "ALLO";
17
  line = Foo(line);
18
}

Beachte auch Deine Ungenauigkeit bei der Schreibweise von line und 
linie.

Schnapp Dir am besten ein gutes C-Buch und arbeite das systematisch 
durch. Dir fehlen Grundlagen.

: Bearbeitet durch Moderator
von Der Andere (Gast)


Lesenswert?

Wäre ja auch Blödsinn, denn wenn line verändert würde, dann hätte man 
keinen gültigen Zeiger mehr auf das 256 Byte Array das unter dem Namen 
angelegt wurde.
Du kannst einen 2. Zeiger als returnwert zurückgeben, der auf das in der 
Unterfunktion gefundene Zeichen zeigt

char * Foo(char* line) {
  char *tmp = line;
  ....
  return tmp;
}

von Der Andere (Gast)


Lesenswert?

Frank M. schrieb:
> Schnapp Dir am besten ein gutes C-Buch und arbeite das systematisch
> durch. Dir fehlen Grundlagen.

Richtig!

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Der Andere schrieb:
> Wäre ja auch Blödsinn [...]
> char * Foo(char* line) {
>   char *tmp = line;

Genauso Blödsinn, denn line ist in Foo() lokal und ändert keine 
gleichnamigen Variablen außerhalb dieser Funktion.

Sorry ;-)

: Bearbeitet durch Moderator
von Steffen R. (steffen_rose)


Lesenswert?

Frank M. schrieb:
> Der Andere schrieb:
>> Wäre ja auch Blödsinn [...]
>> char * Foo(char* line) {
>>   char *tmp = line;
>
> Genauso Blödsinn, denn line ist in Foo() lokal und ändert keine
> gleichnamigen Variablen außerhalb dieser Funktion.

Er schrieb, dass der geänderte Zeiger als Rückgabewert bei seinem 
Vorschlag zurückgegeben wird.

Der Andere schrieb:
> Du kannst einen 2. Zeiger als returnwert zurückgeben, der auf das in der
> Unterfunktion gefundene Zeichen zeigt

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Steffen R. schrieb:
> Er schrieb, dass der geänderte Zeiger als Rückgabewert bei seinem
> Vorschlag zurückgegeben wird.

Der geänderte Zeiger muss sogar zurückgegeben werden! Sonst bekommt 
main() immer wieder denselben Pointer zurück und bewegt sich kein Stück 
vorwärts - egal wie oft Foo() aufgerufen wird.

Es soll aber der komplette String "analysiert" werden. Deshalb muss 
Foo() den Pointer hinter dem abgeschnittenen Zeichen zurückliefern, 
damit main() damit den Rest des Strings durchlaufen kann.

Ähnlich funktioniert es mit der Standard-Lib-Funktion strchr(), nur muss 
man hier das Inkrementieren noch selbst machen, um weiterzukommen, denn 
strchr() liefert den Pointer auf das gefundene Zeichen.

: Bearbeitet durch Moderator
von Random .. (thorstendb) Benutzerseite


Lesenswert?

1
bool Foo(char** line)
2
{
3
  if (**line== 'A') {
4
    // tuwas
5
  }  
6
  *line++; // 1. Zeichen Abschneiden, restlichen String analysieren
7
  return true;
8
}
9
10
void main()
11
{
12
  char text[256] = "HALLO";
13
  char *line = text;
14
15
  Foo(&line);
16
  Foo(&line);
17
}

schöner:
1
bool Foo(char*& line)
2
{
3
  if (*line== 'A') {
4
    // tuwas
5
  }  
6
  line++; // 1. Zeichen Abschneiden, restlichen String analysieren
7
  return true;
8
}
9
10
void main()
11
{
12
  char text[256] = "HALLO";
13
  char *line = text;
14
15
  Foo(line);
16
  Foo(line);
17
}

: Bearbeitet durch User
von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Random .. schrieb:
> bool Foo(char** line)
> {
>   if (**line== 'A') {
>     // tuwas
>   }
>   *line++; // 1. Zeichen Abschneiden, restlichen String analysieren
>   return true;
> }

Warum erhöhst Du hier unnötig die Komplexität? Was soll ein 
Rückgabewert, der immer true ist und den Du zudem in main() komplett 
ignorierst? Du kannst doch direkt line als neuen Wert zurückgeben, spart 
ein Sternchen. Dann ist der Rückgabewert wenigstens sinnvoll.

Übrigens:

>   *line++; // 1. Zeichen Abschneiden, restlichen String analysieren

ist ein Fehler, es muss nämlich bei "char ** line" als Parameter dann 
auch heißen:

    (*line)++;

Soviel zu Fehlern, die man sich einhandelt, wenn man es unnötig 
verkompliziert.

: Bearbeitet durch Moderator
von Random .. (thorstendb) Benutzerseite


Lesenswert?

Obs ... Trocken-Coding ...

> Rückgabewert, der immer true ist
zwischendrin gibt es natürlich eine Fehlerbehandlung mit "return false". 
Das wollte ich mit dem "return true" anzeigen.

: Bearbeitet durch User
von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Random .. schrieb:
> zwischendrin gibt es natürlich eine Fehlerbehandlung mit "return false".

Ja, wenn es diese tatsächlich gibt (z.B. es gibt kein weiteres Zeichen 
'A'), dann wäre es eine sinnvolle Methode. Dann muss man den Fehler aber 
auch in der aufrufenden Funktion abfangen und nicht einfach ignorieren 
;-)

Die libc-Funktion strchr() machts anders: Sie liefert dann einfach einen 
Null-Pointer statt den Pointer auf den Reststring (inkl. 'A') zurück.

von Der Andere (Gast)


Lesenswert?

Frank M. schrieb:
> Genauso Blödsinn, denn line ist in Foo() lokal und ändert keine
> gleichnamigen Variablen außerhalb dieser Funktion.
>
> Sorry ;-)

Dafür gabs zwei Gründe:

Für den Anfänger ist vieleicht einfacher nicht implizit den Zeiger auf 
das übergebene Array zu nehmen.
In der Funktion könnte es ja nötig sein, trotzdem noch den Zeiger auf 
den Anfang des Arrays zzu haben.
Also hab ich einen extra Zeiger definiert und zugewiesen.

von Der Andere (Gast)


Lesenswert?

Frank M. schrieb:
> Genauso Blödsinn, denn line ist in Foo() lokal und ändert keine
> gleichnamigen Variablen außerhalb dieser Funktion.

Ah, jetzt habe ich verstanden was du nicht an meinem Posting verstanden 
hast.
Der TO hat die globale Variable
char line[256] = "HALLO";
übergeben und erwartet, daß dieser globale Pointer auf das Array "line" 
dann verändert wird.
Du meintest eher die lokale Übergabevariable "line" in Foo()

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Der Andere schrieb:
> Ah, jetzt habe ich verstanden was du nicht an meinem Posting verstanden
> hast.

Du hast die Funktion Foo() nur unvollständig skizziert:

> char * Foo(char* line) {
>   char *tmp = line;
>   ....
>   return tmp;
> }

 Wie soll man das also allumfänglich verstehen? ;-)

Wo machst Du denn jetzt den abschließenden Increment in Foo()? Mit tmp 
oder mit line? Nach Deiner Skizze weiterhin mit line. Dann ruft main() 
die Funktion Foo() aber immer mit demselben Pointer auf. Das ist nicht 
gerade zielführend.

Am besten schreibst Du mal die komplette Funktion Foo() unter der 
Verwendung der tmp-Variablen hin. Dann kann man auch darüber reden.

: Bearbeitet durch Moderator
von Der Andere (Gast)


Lesenswert?

Frank M. schrieb:
> Wie soll man das also allumfänglich verstehen? ;-)

Wo ist der Grund, daß ich den Rest der Funktion des TO nochmal 
abschreiben soll.
Ausserdem wollte ich einen Hinweis geben, und nicht eine vollständige 
Lösung zu dem noch nicht mal bekannten wirklichen Problems des TO 
hinschreiben.
Denn dann schreibt er ab und lernt nichts daraus.

Aber wenn du das machen möchtest, bitte schön :-)

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Der Andere schrieb:
> Wo ist der Grund, daß ich den Rest der Funktion des TO nochmal
> abschreiben soll.

Okay, dann mache ich das mal für Dich. Das Ergebnis wäre dann:
1
void Foo(char* line)
2
{
3
  char *tmp = line;
4
5
  if (*line== 'A')
6
  {
7
    // tuwas
8
  }
9
  
10
  line++; // 1. Zeichen Abschneiden, restlichen String analysieren
11
  return tmp;
12
}

1) line++ am Ende hat weiterhin null Effekt.

2) Es wird der Original-Pointer wieder zurückgegeben, ein wiederholter
   Aufruf von Foo(line), wie es der TO will, führt nur dazu, dass
   Foo() immer wieder denselben Teil von line durchackert, ohne recht
   vorwärtszukommen.

> Denn dann schreibt er ab und lernt nichts daraus.

Der Lerneffekt Deiner Funktionsskizze ist leider Null.

von PittyJ (Gast)


Lesenswert?

Nimm C++. Da gibt es std::string mit entsprechenden Funktionen. Und den 
String einfach als Referenz übergeben, da wird dann in der Funktion 
keine Kopie sondern das Original geändert.

Kostet zwar Platz und Rechenzeit, aber man braucht nicht denken.

von Random .. (thorstendb) Benutzerseite


Lesenswert?

Frank M. schrieb:
> Übrigens:
> Das Ergebnis wäre dann:
>
1
> void Foo(char* line)
2
> {
3
>   char *tmp = line;
4
>   ...
5
>   return tmp;
6
> }
7
>

Richtig wäre:
char* Foo(char* line)

:-p :-)

: Bearbeitet durch User
von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Random .. schrieb:
> Richtig wäre:
> char* Foo(char* line)

Ja, zum Beispiel :-)

Deshalb schrieb ich ja schon ganz weit oben:

Frank M. schrieb:
> So wird ein Schuh daraus:
> char * Foo(char* str)

Aber trotzdem: die Berichtigung syntaktischer Fehler reicht hier bei 
weitem nicht.

von Peter D. (peda)


Lesenswert?

Wenn immer nur um 1 hochgezählt wird, könnte das auch einfach der 
Aufrufer machen.

Oder man übergibt die Adresse des Stringpointers:
1
static uint8_t search_cmd( char *buf[], char *cmds[] )
2
{
3
  char *pbuf, *pcmd;
4
  uint8_t n;
5
  
6
  for( n = 0; *cmds[n] != '\0'; n++ ){                  // empty string = end of table
7
    pcmd = cmds[n];                                     // point to command
8
    pbuf = *buf;                                        // point to buffer
9
// ...
10
  *buf = skip_blanks( pbuf );
11
  return n;
12
}
13
14
static uint8_t parse( char *buf )
15
{
16
  uint8_t icmd;                                         // command index
17
  uint8_t ipar;                                         // parameter index
18
  
19
  icmd = search_cmd( &buf, cmd_str );                   // parse command name (required)
20
  ipar = search_cmd( &buf, cpar_str );                  // parse argument name
21
// ...

von panca (Gast)


Lesenswert?

> Schnapp Dir am besten ein gutes C-Buch und arbeite das systematisch
> durch. Dir fehlen Grundlagen.
Ich war mir schon beim Schreiben der Überschrift sicher, dass wieder 
diese blöde Antwort kommt. Warum wird bei C Fragen immer auf das Buch 
verwiesen?

Ich hab 4 C Bücher hier. Galileo Press und K&R. Und hatte die vor Jahren 
auch durch, aber trotzdem hat mich das konrete Beispiel verwirrt.

von W.S. (Gast)


Lesenswert?

panca schrieb:
> Ich war mir schon beim Schreiben der Überschrift sicher, dass wieder
> diese blöde Antwort kommt. Warum wird bei C Fragen immer auf das Buch
> verwiesen?

Weil du einer Idee nachläufst, die nicht wirklich nachvollziehbar 
aussieht und bei den Anderen eher verständnisloses Kopfschütteln 
verursacht. Ich verstehe den Sinn deiner Zeilen auch nicht, denn du 
willst was parsen und das offenbar einzeln für jedes Zeichen einzeln - 
wozu?

Wenn es das ist, was du vorhast, warum erwartest du dann, daß aus deinem 
HALLO ein ALLO wird?

Also, in den meisten Fällen wollen die Leute eine Zeichenkette nach 
Token auseinandernehmen. Das bedeutet, daß man 1. Leerzeichen übergeht, 
2. die an der dann aktuellen Stelle anstehenden Zeichen mit einem 
zweiten String vergleicht und 3. (zumeist) auf ein nachfolgendes 
Trennzeichen abtestet.

Also sowas wie "if (PI*Ottokar) soll dann die Tokens

if
Klammerauf
PI
mal
Ottokar
Klammerzu

ergeben. Und das Leerzeichen nach if wird übergangen.

Also: Drücke dein Problem lieber so aus, daß man den Wunsch dahinter 
verstehen kann.

W.S.

von hunted troll (Gast)


Lesenswert?

Naja, manchmal schadet es nicht wenn man sich nochmals den K&R 
reinzieht, den hast du dann eh in 1 Stunde durch.

Zu diesen Thema würde ich dir auch Literatur zu Regulär Ausdrücke, 
Endliche Automaten und Kontextfrei Sprachen empfehlen.

Das Drachenbuch:
Compilerbau 1 + 2  von  Alfed V. Aho/Ravi Sethi/Jeffrey D. Ullmann
Grundlagen der Theoretischen Infromatik mit Anwendungen Gottfied Vossen 
/Kurt Ulrich Witt

Dazu solltest du dann flex und bison kennen lernen.

von Peter D. (peda)


Lesenswert?

hunted troll schrieb:
> Dazu solltest du dann flex und bison kennen lernen.

Man muß ja nicht gleich übertreiben.

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.