Forum: Mikrocontroller und Digitale Elektronik Teilstring aus String auswählen und in neuen string kopieren, AVR


von Simon (Gast)


Lesenswert?

Hallo,

Ich bin noch recht unerfahren was C-Programmierung angeht und habe hier 
in meiner Arbeit die Aufgabe, eine Funktion zu schreiben, die aus einem 
String einen Teilstring auswählt und ausgibt. Das ganze mache ich mit 
dem Atmel Studio 6.2.

Die Grenzen des auszuwählenden Strings sind die Schlüsselworte "svn" und 
"src".
Ich habe das ganze erst über eine Reihe von for-Schleifen gelöst, was 
schnell und einfach ging und auch funktioniert.
Jetzt würde es mich aber reizen, ob man das nicht schöner auch lösen 
kann.
Ich habe hier im Forum schon was in die Richtung gefunden, 
weitergeholfen hat es mir aber nicht, wie auch sonst nichts auf google.

Hier wäre der Code:
1
#include "version.h"
2
#include "parameter.h"
3
#include "compiler.h"
4
#include "sqInterface.h"
5
#include "stdio.h"
6
#include "string.h"
7
8
9
const char * const getRepository(void)
10
{
11
    static char repositoryStr[150] = "$HeadURL: https://192.168.115.13/svn/project005_Controller/branches/NEON1_1/src/ControllerSolution/ControllerProject/src/sqVersion/version.c $";
12
  char branchStr[50];
13
  uint16_t length;
14
15
  uint16_t *frontBorder = strstr( repositoryStr,"svn"); // vorderes ende des zu kopierenden srtings
16
  uint16_t *backBorder = strstr( repositoryStr,"src"); // hinteres ende
17
  length = backBorder - frontBorder;
18
  branchStr[50] = strncpy(branchStr, repositoryStr + (frontborder + 3), length);
19
  branchStr[length+1] = '\0'; // string terminieren
20
      
21
  return &branchStr;


Am Ende will ich praktisch: "project005_Controller/branches/NEON1_1" als 
Ausgabe haben.
Das Problem ist halt, dass nicht immer den Anfang, Ende und Länge des 
Strings gleich ist.
Der Code ist nicht mehr genau der, den ich anfangs hatte, aber ich habe 
schon so viele Sachen ausprobiert, dass ich nicht mehr sagen kann was 
ursprünglich war. Vielleicht habe ich ja nur einen Pointer an der 
falschen Stelle oder an der falschen Stelle (De)referenziert?


Aus diesem Forum hier habe ich schon einige nützliche Informationen 
bekommen, danke schon mal dafür ;)
Und auch nochmals vielen Dank falls jemand eine Lösung hat!


Gruß

von Markus F. (mfro)


Lesenswert?

Simon schrieb:
> Ich bin noch recht unerfahren was C-Programmierung angeht und habe hier

in diesem Fall solltest Du alle Compiler-Warnungen einschalten, die 
Warnungen (die dein Code, so wie Du ihn gepostet hast, in Hülle und 
Fülle produzieren wird) genau lesen, verstehen und eine nach der 
anderen beheben. Das erzeugt Erfahrung ;).

von Mikro 7. (mikro77)


Lesenswert?

Simon schrieb:

> uint16_t *frontBorder = strstr( repositoryStr,"svn");
> uint16_t *backBorder = strstr( repositoryStr,"src"); // hinteres ende
> length = backBorder - frontBorder;

Wenn denn "svn" und "src" existieren, dann benötigst du den Abstand als 
ein abgerundetes Vielfaches von sizeof(uint16_t)?

Dein Nachfolgender Code sieht eher so aus, als ob du den Abstand in char 
benötigst.

> branchStr[50] =
Das ist das 51. Zeichen. Du hast aber nur 50 Zeichen definiert.

> return &branchStr;

Das ist ein No-Go (auch wenn es häufig funktioniert): branchStr liegt 
nach der Rückkehr der Funktion auf dem freien Stack.

: Bearbeitet durch User
von schnipfel (Gast)


Lesenswert?

> Das ganze mache ich mit dem Atmel Studio 6.2.

Grundsätzliche Empfehlung:
Solche Sachen sind nicht plattformabhängig.
Deshalb zuerst auf dem PC ausprobieren.
Das ist deutlich weniger umständlich als auf einem Controller.

von he? (Gast)


Lesenswert?

>Deshalb zuerst auf dem PC ausprobieren.

Man muss ja nicht auf dem target debuggen, auf dem Simulator sollte 
gehen.
GRuß J

von Dirk B. (dirkb2)


Lesenswert?

stdio.h und string.h sind Standardheader. Die werden beim include mit <> 
eingefasst.

strstr gibt ein char* zurück (kein uint16_t*)

Den Rückgabewert von strncpy brauchst du nicht zuweisen. Das ist der 
erste Parameter. (Der Wert hilft bei der Verkettung von 
Funktionsaufrufen)

Du berechnest length falsch.

frontborder zeigt schon auf den gesuchten String. Das ist kein Offset.

branchStr[length+1]  ist um eins daneben.

Adressen von lokalen Variablen darf man nicht zurück geben, da sie 
danach sofort ungültig werden.

: Bearbeitet durch User
von Der Andere (Gast)


Lesenswert?

Simon schrieb:
> Ich habe hier im Forum schon was in die Richtung gefunden

mal wieder programming by seek, copy and paste
:-(((((

von Der Andere (Gast)


Lesenswert?

Wenn der TO die ersten 30 Seiten hier
https://hassanolity.files.wordpress.com/2013/11/the_c_programming_language_2.pdf
mal durchgearbeitet hätte, dann wären mindestens 80% der Fehler nicht 
passiert.

Aber eine Programmiersprache LERNEN ist heutzutage scheinbar uncool.
Es lebe Guttenberg

von Nop (Gast)


Lesenswert?

Simon schrieb:
> Der Code ist nicht mehr genau der, den ich anfangs hatte, aber ich habe
> schon so viele Sachen ausprobiert, dass ich nicht mehr sagen kann was
> ursprünglich war.

"Probieren" ist mit C der ganz grundsätzlich falsche Weg. Selbst wenn es 
compiliert und läuft, heißt das nicht, daß es immer laufen wird. C geht 
davon aus, daß der Programmierer exakt weiß, was er tut.

Neben den ganzen Anmerkungen, was an dem Code nicht geht: 
Fehlerbehandlung fehlt ja auch noch völlig. Was, wenn der String Deine 
Schlüsselworte schlichtweg nicht enthält?

von Simon (Gast)


Lesenswert?

Also, schonmal danke für die vielen und schnellen Antworten, mach ich 
mich mal ans beantworten ;)

Der Andere schrieb:
> mal wieder programming by seek, copy and paste
> :-(((((

Wie gesagt, ich muss das in der Arbeit machen und das ist nur eine der 
kleinen Aufgaben zwischendurch, weswegen ich da leider auch kein Buch 
durcharbeiten kann, selbst wenns mich interessiert. Daher bleibt mir 
auch nicht viel als es mit seek and copy zu versuchen, aber mit dieser 
Methode verfährt man in der Regel sehr gut. Das Kapitel zu Pointern habe 
ich mir aber tatsächlich durchgelesen aber da wird einem nicht alles 
beim erstem mal klar, bitte um Verständnis ;)
Normal bin ich hier für die MATLAB Programmierung zuständig und da ist 
nunmal einiges anders.



Der Andere schrieb:
> Aber eine Programmiersprache LERNEN ist heutzutage scheinbar uncool.

Ähnliches zu dir, da fehlt mir die Zeit. Die Funktion funktioniert wie 
gesagt mit den for-Schleifen, alles andere hier ist aus reiner 
Interesse, aber dafür hab ich wenig Arbeitszeit :(
Trotzdem werde ich mich mal deinem Dokument annehmen, danke dir auch 
hierfür.


Mikro 7. schrieb:
> Wenn denn "svn" und "src" existieren, dann benötigst du den Abstand als
> ein abgerundetes Vielfaches von sizeof(uint16_t)?

Genau, ich will damit über die Länge nur das benötigte ab Beginn des 
Teils kopieren. length gibt mir praktisch mein hinteres Ende.


Nop schrieb:
> Was, wenn der String Deine Schlüsselworte schlichtweg nicht enthält?

Tut er dank SVN ;)


Mikro 7. schrieb:
>> branchStr[50] =
> Das ist das 51. Zeichen. Du hast aber nur 50 Zeichen definiert.

Stimmt, danke. Korrigiert.


Dirk B. schrieb:
> strstr gibt ein char* zurück (kein uint16_t*)

Korrigiert.

Mikro 7. schrieb:
>> return &branchStr;
>
> Das ist ein No-Go (auch wenn es häufig funktioniert): branchStr liegt
> nach der Rückkehr der Funktion auf dem freien Stack.

Als static deklariert, dann passt das?! ODer muss ich auch das '&' 
weglassen?



Ich habe jetzt eine Lösung hinbekommen, die funktioniert:
1
const char * const getRepository(void)
2
{
3
  static char repositoryStr[150] = "$HeadURL: https://xxx.xxx.xxx.xxx.xxx/svn/zu/kopierender/String/src/ControllerSolution/ControllerProject/src/sqVersion/version.c $";
4
  static char branchStr[51];
5
  uint16_t length;
6
7
  char *frontBorder = strstr(repositoryStr,"svn"); // vorderes ende des zu kopierenden strings
8
  char *backBorder = strstr(repositoryStr,"src"); // hinteres ende
9
  length = (backBorder - frontBorder) - 5;
10
  branchStr[51] = strncpy(branchStr, frontBorder + 4, length);
11
  branchStr[length+1] = '\0'; // string terminieren
12
  
13
  return branchStr;
14
}



Ich hatte übersehen, dass frontBorder ja schon ein Pointer auf die 
Stelle ist, und ich die Zeile "repositoryStr + (frontborder + 3)" nur zu 
"frontBorder + 4" ändern musste und schon hats eigentlich geklappt.

von Nop (Gast)


Lesenswert?

Simon schrieb:
> static char branchStr[51];
>   branchStr[51] = strncpy(branchStr, frontBorder + 4, length);

Branch hat 51 Elemente, aber die Zählung beginnt bei 0. Mehr als 
branch[50] ist buffer overflow.

Zweitens, mach eine Abfrage rein, ob length <= 50 ist. Wenn nicht, 
clippe length auf 50.

von Nop (Gast)


Lesenswert?

Simon schrieb:
> branchStr[length+1] = '\0'; // string terminieren

Achso, und das müßte
branchStr[length+1] = '\0';
sein, weil die Zählung bei 0 losgeht. Hast Du einen String von sagen wir 
3 chars, dann sind die chars an den Positionen 0, 1, 2. Length ist dann 
3. Also muß an Stelle 3 terminiert werden.

Und welchen Sinn (außer dem Buffer Overlow ^^) hat
branchStr[51] = ...
eigentlich? Fragst Du die Stringlänge an der Position später noch ab, 
wie bei Pascal-Strings?

Normalerweise würde man das übrigens auch nicht mit einem static char[] 
machen, sondern der Aufrufer würde einen char * übergeben, in den das 
reinsoll, zusammen mit einer expliziten Information, wie lang der 
übergebene Speicherbereich ist. Sizeof funktioniert nicht mit arrays, 
die an FUnktionen übergeben werden, daher die explizite Längenangabe.

Vorteil: Wenn mehrere Stellen die Funktion aufrufen, kriegen sie alle 
ihr Ergebnis korrekt.

von Mikro 7. (mikro77)


Lesenswert?

1
prompt> gcc -Wall -std=c99 -c foo.c
2
...In function ‘getRepository’:
3
....warning: assignment makes integer from pointer without a cast [enabled by default]
4
branchStr[51] = strncpy(branchStr, frontBorder + 4, length);

Buffer overflow wurde oben schon geschrieben. Es wurde auch bereits 
gesagt was strncpy() zurück liefert. Hast du das gelesen?

Was bezweckst du mit der Zuweisung?

von Der Andere (Gast)


Lesenswert?

Simon schrieb:
> Ich habe jetzt eine Lösung hinbekommen, die funktioniert:

Und bei dem nächsten Trivialproblem doktorst du wieder einen halben Tag 
rum und hoffst dann darauf, dass dir jemand in einem Forum hilft?

Simon schrieb:
> Wie gesagt, ich muss das in der Arbeit machen und das ist nur eine der
> kleinen Aufgaben zwischendurch, weswegen ich da leider auch kein Buch
> durcharbeiten kann, selbst wenns mich interessiert.

Du denkst du kommst schneller voran, wenn du dein Handwerkszeug NICHT 
lernst?
Welcher Vollhonk von Vorgesetzter lässt jemanden ohne Ahnung sowas 
machen?
Der sichere Weg zu Fehlern, und die Realität lehrt, je früher ein Fehler 
entdeckt oder erst gar nicht gemacht wird, desto billiger ist seine 
Beseitigung. Je mehr Zeit in Grundlagen investiert wird, desto 
produktiver wird danach der Einsatz.

Aber es ist deine Arbeit und dein Chef muss mit dem Ergebnis leben...

Viel Spass weiterhin

von Simon S. (simon_s601)


Lesenswert?

Mikro 7. schrieb:
> Buffer overflow wurde oben schon geschrieben. Es wurde auch bereits
> gesagt was strncpy() zurück liefert. Hast du das gelesen?

Hab ich ja, aber nicht genau verstanden was gemeint war, deswegen des 
weiteren gelassen^^


Buffer-Overflow Thematik auch gelesen.
Deklaration als static wird auch überdacht.

Danke euch Nop und mikro77.

Zu dir 'Der Andere':
Immerhin hast du dir die Zeit genommen, also will ich dir auch 
antworten.
Wie gesagt ist meine Aufgabe eine andere, aber das sollte ich nebenbei 
machen und da ich Praktikant bin, und mein Chef weis, dass ich mich da 
nicht auskenne, denke ich wollte er mir nur eine kleine interessante 
Aufgabe zwischendurch geben.
Das war in 6 Monaten das erste Mal, dass ich etwas mit C machen musste, 
mit Pointern habe ich noch nie gearbeitet.
So habe ich das mal als Herausforderung gesehen, und wie oben erwähnt 
war das alleine meine Entscheidung das auf diese Weise zu machen, mit 3 
for-Schleifen war in 30 min erledigt und hat auch gepasst.
Dazu hab ich den Großteil der Arbeit hier in Pausen gemacht, war also 
noch nicht mal Arbeitszeit.
Zwischendurch habe ich die meiste Zeit andere Sachen erledigt, weshalb 
die Antwort etwas auf sich warten ließ.
Natürlich macht es Sinn sich das alles von Grund auf anzueignen, man 
kann nun mal nicht alles können, bzw. hat einfach nicht die Zeit dazu.
Jetzt habe ich mal ein Problem mit Pointern lösen können, was ja ganz 
interessant war.
Zudem hab ich auf google meine Frage einige Male gelesen, aber nie kam 
eine vernünftige Antwort, also hilft das vielleicht nochmal jemanden der 
etwas in der Art implementieren will.

Gruß

von Marc V. (Firma: Vescomp) (logarithmus)


Lesenswert?

Simon S. schrieb:
> Danke euch Nop und mikro77.

 Wobei die beste Antwort schon vorher da war:
Dirk B. schrieb:

von Simon S. (simon_s601)


Lesenswert?

Marc V. schrieb:
> Simon S. schrieb:
>> Danke euch Nop und mikro77.
>
>  Wobei die beste Antwort schon vorher da war:
> Dirk B. schrieb:

Das stimmt ;)
Ich habe nur die beiden erwähnt, weil sie diejenigen waren, die unter 
meiner Antwort kommentiert haben. War zum hervorgeben gedacht, da von 
den beiden konstruktive Sachen kamen im Gegensatz zu den anderen Posts 
;)

von Simon S. (simon_s601)


Lesenswert?

Dirk B.:
Danke dir nochmal, du warst derjenige der das eigentliche Problem 
erkannt hat ;)

: Bearbeitet durch User
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.