String in Char ohne Bufferoverflow

Hi,

ich bin seit gestern total auf dem Schlauch! Ich versuche verzweifelt eine Funktion zu schreiben die "Strings" in "Char" umwandeld (für die LCD4Bit Lib).

void print_display(char* myline1, char* myline2)
{
    lcd.clear();
    lcd.printIn(myline1);
    lcd.cursorTo(2, 0);  //line=2, x=0
    lcd.printIn(myline2);
}

Ich habe schon alles mögliche probiert, ziel ist es "String" anstelle von "Char" zu übergeben damit ich einfacher Zeichenketten zusammenfügen kann (funktioniert leider nicht korrekt):

char myline1[15];
char myline2[15];

void print_display(String myStringline1, String myStringline2)
{
    myStringline1.toCharArray(myline1,15); 
    myStringline2.toCharArray(myline2,15); 

    lcd.clear();
    lcd.printIn(myline1);
    lcd.cursorTo(2, 0);  //line=2, x=0
    lcd.printIn(myline2);
}

Das Hauptproblem ist die Funktion nicht weiß wie lang der String ist, vielleicht hat ja jemand eine Idee :cold_sweat:

erf2012:
[...]

Ich habe schon alles mögliche probiert, ziel ist es "String" anstelle von "Char" zu übergeben damit ich einfacher Zeichenketten zusammenfügen kann (funktioniert leider nicht korrekt):

char myline1[15];

char myline2[15];

void print_display(String myStringline1, String myStringline2)
{
    myStringline1.toCharArray(myline1,15);
    myStringline2.toCharArray(myline2,15);

lcd.clear();
    lcd.printIn(myline1);
    lcd.cursorTo(2, 0);  //line=2, x=0
    lcd.printIn(myline2);
}




Das Hauptproblem ist die Funktion nicht weiß wie lang der String ist, vielleicht hat ja jemand eine Idee :cold_sweat:

Und warum übergibst du ihm nicht einfach die korrekte Länge?
Also z. B.

    myStringline1.toCharArray(myline1, myStringLine1.length());

Wobei du übrigens noch die Deklaration von myline1 und myline2 ändern muss, du brauchst ein Zeichen mehr, wenn dein String genau 15 Stellen lang ist. Mit char[15] darf die max. Länge nur 14 Zeichen sein.

Rudi

Ja das Problem ist ja dann das ich je nach Funktionsaufruf unterschiedlich "langen" Speicher mit Char[] reservieren muss, oder?

Oder könnte ich das

 myStringline1.toCharArray(myline1, myStringLine1.length());

direkt so in die Funktion schreiben. Das würde aber doch zu einem Bufferoverflow führen wenn ich bei jedem Aufruf den Speicher neu reservieren würde (außer wenn ich diesen irgendwie freigeben kann was glaube ich hier nicht funktioniert).

Das ist eben mein Problem :frowning:

Hm,

so ganz verstehe ich dein Problem nicht.

Da die Deklaration

char myline1[16];

außerhalb ("global") ist, reservierst du genau einmal Speicher und zwar genau 16 Bytes (max. 15 Zeichen Nutzdaten + '\0' als Abschluss für Strings).
Aber du musst natürlich nicht alle 15 Zeichen ausnutzen - du kannst auch nur z. B. davon davon nutzen - wichtig ist lediglich, dass die max. Länge nicht überschreitest, also z. B. versuchst, 20 Zeichen reinzuschreiben, dann überschreibst du dir nämlich irgendwo Speicher

Du könntest als Alternative die Deklaration von myline1 in die Funktion print_display verlagern, das ändert aber grundsätzlich nichts, außer dass der Speicher bei jedem Aufruf auf dem Stack erzeugt wird - aus softwaretechnischer Sicht gehört die Deklaration auch dort hin, da de Speicher nur in dieser Funktion benutzt wird.

Un du könntest natürlich den Speicher wirklich dynamisch reservieren, d. h. abhängig von der Länge von myStringline1 wird auch nur soviel Speicher belegt, wie unbedingt nötig - aber das macht das alles nur unnötig kompliziert - es geht hier ja nicht um gigantische Datenmengen, wir reden hier von ingesamt 32 Bytes, da lohnt sich eine dynamische Speicherverwaltung einfach nicht.

Rudi

void* malloc(int size) und free(void*) sind in diesem Fall Dein Freund. Damit reservierst Du dynamisch Speicher und gibst ihn wieder frei.
Soweit ich weiss, klappt das auch bei der avr-libc.
Mario.

mkl0815:
void* malloc(int size) und free(void*) sind in diesem Fall Dein Freund. Damit reservierst Du dynamisch Speicher und gibst ihn wieder frei.
Soweit ich weiss, klappt das auch bei der avr-libc.
Mario.

Und den Aufwand treiben wegen 32 Bytes - wenn wir mal annehmen, dass die beiden String im Durchschnitt zu 50% voll sind, also dann nur noch 16 Bytes insgesamt?

Sorry, da ist der Aufwand viel zu groß, wenn man noch die Fehleranfälligkeit bedenkt - da erf2012 nach einer Lösung gefragt hat, kennt er also die dynamische Speicherverwaltung nicht - meine Meinung ist, dass es reicht, die beiden Variablen lokal in display_print zu verschieben und gut ist;-)

Alles anders ist mit Kanaonen auf Spatzen zu schießen.
Rudi

Für diesen Fall mag das richtig sein. Allerdings gibt es keine Angaben zu den Längen der Strings, daher sollte man gut überlegen wieviel "wertvollen" RAM man fix an ein Array bindet. Zum anderen wollte ich nur eine alternative Lösungsmöglichkeit aufzeigen, die sicher in andreren Situationen hilfreich sein kann. Wir sind ja schliesslich alle daran interessiert zu lernen.
Mario.

Also ich würde das gerne dynamisch machen, so schwer scheint das ja rein theoretisch nicht zu sein, also bisher scheint es zu funktionieren, aber nur so (damit alle 16 Zeichen angezeigt werden):

//Neue variante
void displayString(String myStringline1, String myStringline2)
{
    char myline1[17];
    char myline2[17];

    myStringline1.toCharArray(myline1,17); 
    myStringline2.toCharArray(myline2,17); 
    
    lcd.clear();
    lcd.printIn(myline1);
    lcd.cursorTo(2, 0);  //line=2, x=0
    lcd.printIn(myline2);
}

@mkl0815

void* malloc(int size) und free(void*) sind in diesem Fall Dein Freund. Damit reservierst Du dynamisch Speicher und gibst ihn wieder frei.
Soweit ich weiss, klappt das auch bei der avr-libc.
Mario.

Ich komme mit den Pointern immer durcheinander, wie müsste ich das denn Synthaxtechnisch schreiben?

Leider kann ich mit der oben genannten Funktion keine Strings mit Variablen kombinieren:

        displayString(String("< Temperatur "), "das ist ein test");

Das funktioniert^^

Aber sobald ich versuche mit einem String Addition Operator noch einen Wert hinzuzufügen (trotz kleiner als 16 Byte ist) gibt es einen Überlauf.

void displayString(String myStringline1, String myStringline2)
{
    char *myline1;
    char *myline2;

    myline1 = malloc(myStringline1.length() + 1);
    myline2 = malloc(myStringline2.length() + 1);
    myStringline1.toCharArray(myline1, myStringline1.length() + 1);   // *)
    myStringline2.toCharArray(myline2, myStringline2.length() + 1);   // *)
    
    lcd.clear();
    lcd.printIn(myline1);
    lcd.cursorTo(2, 0);  //line=2, x=0
    lcd.printIn(myline2);
    free(myline1);
    free(myline2);
}

An den beiden mit *) markierten Stellen bin ich mir nicht ganz sicher, ob das " + 1" wirklich benötigt wird.

Da kommt dann das heraus:

rtChicken.cpp: In function 'void displayStringAlloc(String, String)':
Menutools:55: error: invalid conversion from 'void*' to 'char*'
Menutools:56: error: invalid conversion from 'void*' to 'char*'

Wie kann denn malloc eigentlich einen Pointer haben wenn void die Rückgabe ist :fearful:

So müsste es erstmal was den Speicher angeht richtig sein:

void displayStringAlloc(String myStringline1, String myStringline2)
{
    char *myline1;
    char *myline2;

    myline1 = (char*)  malloc(myStringline1.length() + 1);
    myline2 = (char*)  malloc(myStringline2.length() + 1);
    myStringline1.toCharArray(myline1, myStringline1.length() + 1);   // *)
    myStringline2.toCharArray(myline2, myStringline2.length() + 1);   // *)
    
    lcd.clear();
    lcd.printIn(myline1);
    lcd.cursorTo(2, 0);  //line=2, x=0
    lcd.printIn(myline2);
    
    free(myline1);
    free(myline2);
}

Jetzt ist nur die Frage ob ich jetzt noch das "\0" heranhängen muss oder nicht - also ich kann schonmal den String mit Zahlen kombinieren, was schonmal super ist! :slight_smile:

erf2012:
Da kommt dann das heraus:

rtChicken.cpp: In function 'void displayStringAlloc(String, String)':

Menutools:55: error: invalid conversion from 'void*' to 'char*'
Menutools:56: error: invalid conversion from 'void*' to 'char*'




Wie kann denn malloc eigentlich einen Pointer haben wenn void die Rückgabe ist :fearful:

Die Rückgabe ist nicht void, sondern void *, das ist was ganz was anderes! void * bedeutet, dass das ein generischer Pointer ist, derzu allen Pointern kompatibel ist.

erf2012:
[...]
Jetzt ist nur die Frage ob ich jetzt noch das "\0" heranhängen muss oder nicht - also ich kann schonmal den String mit Zahlen kombinieren, was schonmal super ist! :slight_smile:

Einfach mal verschieden lange Strings ausprobieren, auch bis zum Maximum von 16 Zeichen gehen - wenn das alles funktioniert, dann sollte es wohl passen.

Rudi

Vielen dank, es klappt jetzt, so scheint es jedenfalls :slight_smile:

Und was hast du jetzt gegenüber

//Neue variante
void displayString(String myStringline1, String myStringline2)
{
    char myline1[17];
    char myline2[17];

    myStringline1.toCharArray(myline1,17); 
    myStringline2.toCharArray(myline2,17); 
    
    lcd.clear();
    lcd.printIn(myline1);
    lcd.cursorTo(2, 0);  //line=2, x=0
    lcd.printIn(myline2);
}

gewonnen (abgesehen von der höheren Komplexität des Codes mit malloc und free)?

Das hat bei mir leider nicht richtig funktioniert.