C Speicher in Funktion reservieren?!

"Abstürzen" ist recht wahrscheinlich, wenn du über das Array hinaus schreibst. Das passiert bei dir ja eventuell weil der String plus Abschlußzeichen nicht rein passt.

Wenn du den Speicher ständig zuweist und freigibst und das vielleicht noch an unterschiedlichen Stellen in mehreren Funktionen/Routinen, dann wird der Speicher möglicherweise fragmentiert. Dies kann zu Problemen führen.
 
Moin.
Habe es jetzt noch einmal mit der Funktion "malloc()" probiert. Leider klappt das nicht. Ich muss doch irgendwas beim handling falsch machen?


CodeBox C

[I]int8_t[/I] srchCmd(char *inBuff, char *srchCmd)

{
char *cmdBeginn = [I]NULL[/I];
char *rawData = [I]NULL[/I];
[I]uint8_t[/I] cmdStrLen = [I]strlen[/I](srchCmd);
[I]uint8_t[/I] rawData_ = 0;
if (inBuff == [I]NULL[/I] || srchCmd == [I]NULL[/I])
return -1;

/*

* how much raw data we will received?
*/
rawData = [I]strchr[/I](srchCmd,'x');
if(rawData != [I]NULL[/I])
while(*rawData++ == 'x')rawData_++;

/*

* internal command buffer
*/
char *cmdBeginn_ = malloc(sizeof(char)*([I]strlen[/I](srchCmd)+1)); 

if (cmdBeginn_ == [I]NULL[/I])
return -1;

[I]strcpy[/I](cmdBeginn_,srchCmd);

/*

* terminate cmd string
*/  

cmdBeginn_[cmdStrLen-rawData_] = '\0';

/*

* search beginning from our final command string
*/    

cmdBeginn = [I]strstr[/I](inBuff,cmdBeginn_);
if(cmdBeginn == [I]NULL[/I])
return -2;


/*

* terminate end of searched string
*/
cmdBeginn[cmdStrLen] = '\0';

/*

* copy the command to buffer
*/
[I]strcpy[/I](inBuff,cmdBeginn);

[I]free[/I](cmdBeginn_);

return 0;


}
 
Hallo Jan,

ich bräuchte mehr Zeit um mich mehr mit deinem Code auseinanderzusetzen zu können :)

Wenn du malloc ersetzt und das Array zur Compilezeit definierst ...
char cmdBegin_[30] zum Beispiel,
funktioniert es dann?


Was mir aufgefallen ist: Zeile 44
Im Fehlerfall brichst du die Funktion ab. Zuvor war malloc erfolgreich, den allokierten Speicher gibst du nicht wieder frei.
Das ist ein Problem, erst recht dann, wenn die Funktion öfter aufgerufen wird.

Offtopic:
Wenn du den Code in den Editor einfügst, könntest du die kursive Formatierung vorher entfernen. Der Code lässt sich so schwer lesen.
Du kannst ggf. zuvor in einen einfachen Texteditor einfügen und dann davon wieder in die Zwischenablage kopieren.
 
Wenn du malloc ersetzt und das Array zur Compilezeit definierst ...
char cmdBegin_[30] zum Beispiel,
funktioniert es dann?

Hallo Dirk, ja dann funktioniert es. Eigentlich brauche ich malloc auch überhaupt nicht, da ich ja weiß wie viele bytes ich speichern muss. Möchte es halt nur testen und was daraus lernen.

Eine Frage habe ich noch zusätzlich..

Wenn ich der Funktion als zweites Argument z.B "Test" übergebe, ohne das in einen Speicher vorher zu schreiben also direkt den String als Parameter und versuche später in der Funktion was an diesem String zu machen, stürzt das Programm ab, speichere ich es z.B in einem Array ab und versuche es dann, klappt es. Wieso? Wird das in einem besonderen Speicher geschrieben?
 
Janiiix3 schrieb
>> Wenn ich der Funktion als zweites Argument z.B "Test" übergebe,

Du übergibst der Funktion nicht "Test", sondern einen Zeiger auf "Test".
Da der Compiler so gut wie möglich optimiert, kopiert er das Argument natürlich nicht ins RAM, sondern setzt an dieser Stelle einen Zeiger auf die Zeichenkette "Test" im Flash ein.
Wenn Du der Funktion also einen unveränderlichen Text übergibst, brauchst Du dich auch nicht zu wundern, wenn bei einem Schreibversuch auf diesen Text irgendetwas unerwartetes passiert...
 
Janiiix3 schrieb
>> Wenn ich der Funktion als zweites Argument z.B "Test" übergebe,

Du übergibst der Funktion nicht "Test", sondern einen Zeiger auf "Test".
Da der Compiler so gut wie möglich optimiert, kopiert er das Argument natürlich nicht ins RAM, sondern setzt an dieser Stelle einen Zeiger auf die Zeichenkette "Test" im Flash ein.
Wenn Du der Funktion also einen unveränderlichen Text übergibst, brauchst Du dich auch nicht zu wundern, wenn bei einem Schreibversuch auf diesen Text irgendetwas unerwartetes passiert...
,
Und wo legt der Compiler das beim PC ab?
 
Janiiix3 schrieb
>> Wenn ich der Funktion als zweites Argument z.B "Test" übergebe,

Du übergibst der Funktion nicht "Test", sondern einen Zeiger auf "Test".
Da der Compiler so gut wie möglich optimiert, kopiert er das Argument natürlich nicht ins RAM, sondern setzt an dieser Stelle einen Zeiger auf die Zeichenkette "Test" im Flash ein.
Wenn Du der Funktion also einen unveränderlichen Text übergibst, brauchst Du dich auch nicht zu wundern, wenn bei einem Schreibversuch auf diesen Text irgendetwas unerwartetes passiert...

Das was Du schreibst ist prinzipiell richtig, aber im Fall des AVR GCC falsch, denn solange man einen String explizit nicht als Konstante markiert / Dekoriert, wird der zwar im Flash abgelegt aber beim init (Start des Programmes) dennoch in das RAM kopiert.

(Siehe auch http://www.nongnu.org/avr-libc/user-manual/group__avr__pgmspace.html)

Dazu muss man verstehen, dass der AVR eine sog. Harvard Architektur ist. Das heisst er hat getrennte Busse für Daten und den Programmbereich.

Beim AVR können im RAM (veränderbar und bei Powerfail flüchtig) nur DATEN liegen, aber niemals ausführbarer Code!

Dies ist eine ganz bewusste Designentscheidung der AVR Entwickler. Im Flash (nichtflüchtig und nur durch explizites umprogrammieren änderbar) liegen der Programmcode, aber es können da auch unveränderliche Daten liegen! Letztere nennt sich auch der PGM (Program Memory) Space

Dann gibt es noch das EEPROM, das als nichtflüchtiger Datenspeicher fungieren kann. Aber lassen wir den hier mal außen vor.
Wo was liegt, entscheidet der Compiler und dann letzten Endes legt es der Linker fest.
Freilich liegen konstante Daten (Strings, Tabellen, etc.) erst auch mal im Flash, denn die müssen initial erst mal da sein.

ABER der GCC erzeugt beim AVR einen speziellen init code, der noch vor dem Eintritt von main ausgeführt wird.
Dieser init code kopiert Strings und andere (pseudo) Konstante Daten erst mal ins RAM (= Data Space) und dann wird die CPU darauf ganz einfach zugreifen können.

Das funktioniert immer. Aber es hat teils gravierende Nachteile:
  1. Man verschwendet redundant wertvollen Speicher im RAM, weil da alle Konstanten hinkopiert werden, die ja eh schon im FLASH liegen.
  2. Die Daten sind (obschon man sie als const gekennzeichnet hat) nicht wirklich Konstant, weil sie im RAM liegen und damit veränderlich sind.
  3. Die Initialisierung dauert ein wenig wegen des Kopiervorganges.
Wie bekommt man die Daten nun aber dazu wirklich konstant zu sein?

Nun man sagt (Dekoriert die Daten) dem GCC beim Definieren der Ressourcen, dass man die ausschließlich im Flash (PGM Space) haben will. Dann werden die beim Start NICHT ins RAM kopiert!

Gehen wir mal aus, wir wollen die Stings ausschließlich read only im PGM-Space (Flash) haben:

Dann würde man statt statt "old school"


CodeBox C
const char* const kTestString = "This is a test string";

schreiben:


CodeBox C
#include <avr/pgmspace.h>
...
PROGMEM const char* const kTestString = "This is a test string";


Wir erinnern uns: Harvard Architektur -> getrennte Busse für Daten und PGM Space. Und genau da liegen nun nach dem Dekorieren mit PROGMEM auch die konstanten Daten! Der gcc startup init code wird da dann auch nichts mehr ins RAM kopieren.

Deshalb kann der CPU Kern ohne spezielle Adressierungsmechanismen dann aber auf die Daten auch nicht aus dem FLASH zugreifen (weil wir ja getrennte Busse haben!). Hierzu hat der AVR CPU Kern aber spezielle Befehle, die es ihm erlauben, Daten aus dem PGM Space zu laden, wo ja sonst eigentlich nur Programmcode liegt.

Die avrlibc (die man bei Arduino immer dabei hat) sind im header avr/pgmspace.h einige Funktionen deklariert, die einen Zugriff auf dauen im PGM Space erlauben, also Daten, die man wie oben gezeigt mit PGMSPACE gekennzeichnet hat.

Bitte http://www.nongnu.org/avr-libc/user-manual/group__avr__pgmspace.html anschauen!

Beispiel:



CodeBox C
#include <avr/pgmspace.h>
...
PROGMEM const char* const kTestString = "This is a test string";

...schreibt man später im Code...


CodeBox C
// Temp Buffer to copy the constant data from PGM space to.
char ramBuffer[256];

// Copy the data from the PGM (Flash) Space into the RAM.
strcpy_P(ramBuffer, kTestString);

// oder besser - um Buffer Overflow zu vermeiden:
strlcpy_P(ramBuffer, kTestString, sizeof(ramBuffer));


Das Ganze erfordert etwas Übung, aber sich Gedanken zu machen, wo was liegt lohnt sich! Ganz besonders bzgl. der Aspekte Speicherverbrauch im RAM und dem Schutz der konstanten Daten vor Überschreiben.

Gerade janiix3 macht hier wie ich gesehen habe ganz eklatante und gravierende Fehler, die sicher auch damit zu tun haben, dass man erst mal versanden haben muss, wo ein Compiler Daten ablegt - ob diese konstant sind oder nicht... Aber dazu werde ich unten bei janixx3 Code noch mehr schreiben ;-)

In dem Sinne - Happy coding!
 
Zuletzt bearbeitet:
Moin.
Habe es jetzt noch einmal mit der Funktion "malloc()" probiert. Leider klappt das nicht. Ich muss doch irgendwas beim handling falsch machen?
...

Hallo Jan,

Du machst hier einige teils gravierende Fehler:

1. Du schreibst den NUL Terminator völlig ins Nirvana (kritischer Bug)


CodeBox C
/*
* terminate cmd string
*/
cmdBeginn_[cmdStrLen-rawData_] = '\0';


rawData_ ist ein POINTER (eine Addresse). cmdStrLen repräsentiert dagegen eine Länge.
Somit ist der effektive index = length - Address?! Das ist Nonsens und zeigt im Speicher irgendwo hin!
Hätte Dein Zielsystem eine MMU würde das sicher einen Segmentation (Speicherzugriffsverletzung auslösen)!

Hast Du die Pointerarithmetik wirklich verstanden? ;)

2. Du schreibst in einen Bereich wo auch konstante Daten liegen können.


CodeBox C
/*
* copy the command to buffer
*/
strcpy(inBuff,cmdBeginn);


Wer sagt, dass inBuff nicht auf Konstante, also unveränderliche Daten zeigt?
Du änderst die Daten aber im übergebenen Buffer!

Beim AVR würde das sogar funktionieren (siehe auch meinen Beitrag zu Daten im PROGMEM, oben), weil die Konstanten ins RAM kopiert werden, aber das Vorauszusetzen ist nicht schön!

"In Place" Veränderungen sollten entsprechend dokumentiert werden! Vielleicht hat es der Aufrufen nicht gerne, wenn seine Strings, die er übergibt verändert werden?

Ich denke, Du solltest Dein Interface für die Funktion ein wenig überdenken überarbeiten. ;)

3. Potentieller memory leak im Falle eines "String match"


CodeBox C
/*
* search beginning from our final command string
*/
cmdBeginn = strstr(inBuff,cmdBeginn_);
if(cmdBeginn == NULL)
    return -2;


Wer gibt in dem Fall, dass der suchstring nicht gefunden wurde den Speicher frei, den Du weiter oben mit malloc reserviert hast?! Der Code wird Dir mit den Zeit das ganze RAM wegfressen... ...bis nichts mehr da ist und der malloc() mit einem NULL zurückkehrt ;-)

Also das free in dem Zweig nicht vergessen!

Fix:



CodeBox C
cmdBeginn = strstr(inBuff,cmdBeginn_);
if(cmdBeginn == NULL){
    free(cmdBeginn_);
    return -2;
}


Happy Codin'!
 
Zuletzt bearbeitet:
1. Du schreibst den NUL Terminator völlig ins Nirvana (kritischer Bug)





CodeBox C und C++ C C++1234/* * terminate cmd string */cmdBeginn_[cmdStrLen-rawData_] = '\0';



rawData_ ist ein POINTER (eine Address). cmdStrLen repräsentiert dagegen eine Länge.

Somit ist der effektive index = length - Address?! Das ist Nonsens und zeigt im Speicher irgendwo hin!

Hätte Dein Zielsystem eine MMU würde das sicher einen Segmentation (Speicherzugriffsverletzung auslösen)!



Hast Du die Pointerarithmetik wirklich verstanden?



Ich bin mitten drin es zu verstehen. In Bezug auf deiner Aussage, muss ich wirklich zugeben, dass die Namenswahl meiner Variablen nicht gerade gut ist. Das habe ich bereits überarbeitet.


2. Du schreibst in einen Bereich wo auch konstante Daten liegen können.





CodeBox C und C++ C C++1234/* * copy the command to buffer */strcpy(inBuff,cmdBeginn);



Wer sagt, dass inBuff nicht auf Konstante, also unveränderliche Daten zeigt?

Du änderst die Daten aber im übergebenen Buffer!



Beim AVR würde das sogar funktionieren (siehe auch meinen Beitrag zu Daten im PROGMEM, oben), weil die Konstanten ins RAM kopiert werden, aber das Vorauszusetzen ist nicht schön!



"In Place" Veränderungen sollten entsprechend dokumentiert werden! Vielleicht hat es der Aufrufen nicht gerne, wenn seine Strings, die er übergibt verändert werden?



Ich denke, Du solltest Dein Interface für die Funktion überdenken. ;-)



Auch hier, Ich bin mitten drin mich in der Sprache „C“ zu schulen.
Wie kann ich das verhindern? Ich persönlich weiß, dass im Puffer „inBuff“ keine Konstanten enthalten sind. Kann ich das in der Funktion abfragen/abfangen? Wenn ja, wie?


3. Potentieller memory leak im Falle eines "String match"





CodeBox C und C++ C C++123456/* * search beginning from our final command string */cmdBeginn = strstr(inBuff,cmdBeginn_);if(cmdBeginn == NULL) return -2;



Wer gibt in dem Fall, dass der suchstreng nicht gefunden wurde den Speicher frei, den Du weiter oben mit malloc reserviert hast?! Der code wird Dir mit den Zeit das ganze RAM wegfressen... ;-)



Also das free in dem Zweig nicht vergessen!



Habe es jetzt gelassen mit der Funktion „malloc()“ war nur zum testen und verstehen.


Hier der aktuelle Code



CodeBox C
 int8_t srchCmd(char *inBuff, const char *srchCmd)

{

                /*

                *             Zeiger auf den Beginn unserem Kommando (fals vorhanden!).

                */

                char *cmdBeginnPtr    = NULL;

               

                /*

                *             Erwarten wir Nutzdaten, wird ein Zeiger auf das Erste

                *             Nutzdatenbyte gesetzt.

                */

                char *rawDataPtr      = NULL;

               

                /*

                *             Bestimmt die Länge unseres zweiten Parameters (srchCmd).

                *             Damit wir später unseren "gefundenen" Kommando String terminieren können.

                */

                uint8_t srchCmdStrLen = strlen(srchCmd);

               

                /*

                *             Zählt (falls vorhanden..) die Nutzdaten die Empfangen werden sollen.

                *             Damit wir diese später verarbeiten können.

                */

                uint8_t rawDataBytes  = 0;

               

                /*

                *             Wurde als Parameter ein "NULL" Zeiger übergeben,

                *             so beenden wir die Funktion vorzeitig.

                */

                if (inBuff == NULL || srchCmd == NULL)

                return -1;

 

                /*

                *   Wie viele Nutzdaten erwarten wir im aktuellen String?.

                *             Die Anzahl der Nutzdaten wird anhand vom Buchstaben 'x'

                *             bestimmt.

                */

                rawDataPtr = strchr(srchCmd,'x');

                if(rawDataPtr != NULL)

                while(*rawDataPtr++ == 'x')rawDataBytes++;

         

                /*

                *    Aktuelles Kommando was gesucht wird, erst einmal zwischen speichern

                *              damit wir später genau diesen Puffer bearbeiten können.

                */

                char cmdBuff[26] = "";

                strcpy(cmdBuff,srchCmd);


                /*

                *   Wenn wir Nutzdaten empfangen haben, terminieren wir den String

                *             nach dem letzten für uns relevanten Datenbyte.

                */

                if (rawDataBytes > 0)cmdBuff[srchCmdStrLen-rawDataBytes] = '\0';

 

                /*

                *             Suchen wir nun nach unserem Kommando in unserem Empfangspuffer (inBuff)..

                *             Hierzu wird der String der aktuell als Parameter "srchCmd"

                *             übergeben wurde im Empfangspuffer (inBuff) gesucht.

                *

                */     

                cmdBeginnPtr = strstr(inBuff,cmdBuff);

                if(cmdBeginnPtr == NULL)

                return -1;

                 

                /*

                *    Zum Schluss, terminieren wir noch das Ende von unserem Kommandopuffer.

                *              Damit bezwecken wir das die nachfolgenden Zeichen abgeschnitten werden

                *              und nicht weiter verarbeitet werden.

                */

                cmdBeginnPtr[srchCmdStrLen] = '\0';

 

                /*

                *             Haben wir unsern Kommando String gefunden, speicheren wir diesen in

                *   unserem Eingangs(Empfangspuffer(inBuff) Puffer ab damit wir diesen

                *             später evtl. noch weiterverarbeiten können.

                *

                *             Sollte kein Kommdo mit "srchCmd" übereinstimmen, wird der Eingangspuffer(inBuff)

                *             unbearbeitet zurück gelassen.

                *

                */

                strcpy(inBuff,cmdBeginnPtr);

     

                return 0;

}
 



P.S

Danke das du dir den Code angeschaut hast und Anmerkungen gemacht hast. Damit kann ich sehr gut lernen. Danke!
 
Ich bin mitten drin es zu verstehen. In Bezug auf deiner Aussage, muss ich wirklich zugeben, dass die Namenswahl meiner Variablen nicht gerade gut ist. Das habe ich bereits überarbeitet.

Auch hier, Ich bin mitten drin mich in der Sprache „C“ zu schulen.

Wie kann ich das verhindern? Ich persönlich weiß, dass im Puffer „inBuff“ keine Konstanten enthalten sind. Kann ich das in der Funktion abfragen/abfangen? Wenn ja, wie?
Nun, so gesehen hast Du es - Deiner Funktionssemantik nach (der Inhalt der Daten, auf die inBuff zeigt, wird durch die Funktion verändert) - schon richtig gemacht!

Ich möchte das noch mal beschreiben, weil const meiner Beobachtung nach viel zu selten benutzt wird!

Es gibt nämlich in C das wunderbare Schlüsselwort const (welches Du ja im aktuellen Code auch vorbildlich genutzt hast). Mit dem kann man dem Compiler sagen, dass etwas konstant ist und er wird dann auch während des Compilierens dies prüfen.

Dabei hat man bei Pointern die Möglichkeit zu bestimmen, ob:



| Pointer ist | Inhalt, auf den der Pointer zeigt ist | Schreibweise | Alternativ |
|----------------|---------------------------------------|----------------------------|-----------------------------|
| nicht konstant | nicht konstant | char* pointer | |
| nicht konstant | konstant | char const * pointer | const char * pointer |
| konstant | nicht konstant | char * const pointer | |
| konstant | konstant | char const * const pointer | const char * const pointer |


Es ist also bei Pointern wichtig wo das Schlüsselwort steht!

  1. Steht es links vom Stern (const char* pointer), dann bedeutet das "Konstantheit auf die Daten, auf die Pointer zeigt". Das bedeutet man darf darf den Inhalt, auf die der Pointer zeigt nicht schreiben. Man darf sie nur lesen. Der Pointer selbst darf aber verändert werden. Konkret: Ein pointer = newPointer, oder aber auch pointer++ (u.Ä.) ist erlaubt. Ein *pointer=newData (oder pointer[idx]=newData) wird der Compiler aber bemängeln.
  2. Steht es rechts vom Stern (char* const pointer), dann bedeutet das "Konstantheit auf den Pointer selbst bezogen". Das bedeutet man darf auf den Inhalt, auf die der Pointer zeigt schreiben. Man darf sie nur lesen. Der Pointer selbst darf aber nicht verändert werden. Konkret: Ein pointer = newPointer, oder aber auch pointer++ (u.Ä.) wird der Compiler bemängeln. *pointer=newData (oder pointer[idx]=newData) ist aber erlaubt.
  3. Steht es links und rechts vom Stern (const char* const pointer), dann bedeutet das "Konstantheit auf den Pointer und auch auf dessen Inhalt". Das bedeutet man darf weder auf den Inhalt, auf die der Pointer zeigt schreiben, noch darf man den Pointer selbst verändern! Konkret: Weder ein pointer = newPointer, noch ein pointer++ (u.Ä.), noch ein *pointer=newData (oder pointer[idx]=newData) ist erlaubt! All das wird der Compiler bemängeln. Wozu braucht man das? Nun, um z.B. Konstante Pointer auf Konstante Daten zu haben (z.B. Pointer auf konstante Strings).

Folgende Beispiele



CodeBox C
#include <stdio.h>
#include <stdlib.h>

/*!
 * Copy the content of a string to another string (bad Version).
 * \note This version is considered to be bad because the function (internally) allows a
 * modification of the contents of \p srcPtr that is probably not intented!
 *
 * \param destPtr A Pointer to the _destination_ memory to copy the contents of \p srcPtr to.
 * \param srcPtr A Pointer to the _source_ memory to copy the contents to \p destPtr from.
 */
void my_StringCopy_Bad1(char* destPtr, char* srcPtr) {
    do {
        *(destPtr++) = *srcPtr;

        /* Assume that the following line is a bug.
         * The Compiler will not blame a write to the source Buffer because it is
         * __not__ marked as const */
        *srcPtr = 'x';
    } while ('\0' != *(srcPtr++));
}

/*!
 * Copy the content of a string to another string (good Version).
 * \note This version is considered to be good because the function (internally) __does not__
 * allow a modification of the contents of \p srcPtr because its content is marked as __const__
 *
 * \param destPtr A Pointer to the _destination_ memory to copy the contents of \p srcPtr to.
 * \param srcPtr A Pointer to the _source_ memory to copy the contents to \p destPtr from.
 *        The pointer may be changed __but not__ the content it points to!
 */
void my_StringCopy_Good(char* destPtr, const char* srcPtr) {
    do {
        *(destPtr++) = *srcPtr;

        /* Assume that the following line is a bug.
         * Here, the Compiler __will__ blame a write to the source Buffer because it is
         * marked as const
         *
         * For example: The GCC will blame "error: read-only variable is not assignable
        *srcPtr = 'x';"
         */
        *srcPtr = 'x';
    } while ('\0' != *(srcPtr++));
}

int main(int argc, char* argv[])
{
    char destBuffer[256];
    my_StringCopy_Bad1(destBuffer, "I'm the source string!");
    my_StringCopy_Good(destBuffer, "I'm the source string!");
    return EXIT_SUCCESS;
}



Übrigens gibt es in C++ noch eine weite Variante, wo man mit const ausdrücken kann, dass eine Methode keine Members ändert. Aber wir sind hier ja in C und es ist somit eine andere Geschichte... ;-)

Habe es jetzt gelassen mit der Funktion „malloc()“ war nur zum testen und verstehen.

Hier der aktuelle Code



CodeBox C
 int8_t srchCmd(char *inBuff, const char *srchCmd)
...
 

P.S
Danke das du dir den Code angeschaut hast und Anmerkungen gemacht hast. Damit kann ich sehr gut lernen. Danke!

Das freut mich zu hören! Man kann nur lernen, wenn man Fehler macht.

Wenn ich mir den Code ansehe, den ich am Anfang meiner Programmierzeit geschrieben habe, dann muss ich oft schmunzeln und wundere mich, dass der leif. Das war aber seinerzeit auf dem Amiga und der hatte keinen Speicherschutz ;-D

Ich schreibe solche Algorithmen meist zuerst auf dem PC (oder in meinem Fall auf dem Mac), denn diese haben eine MMU die bei schwerwiegenden Scpeicherschutzverletzungen schreien. Außerdem verfügen diese Entwicklungsumgebungen meist über gute Debugger, mit denen man prima alle Variablen und den jeweiligen Speicherinhalt anschauen kann...

Happy Codin'!
 
Zuletzt bearbeitet:
  • Like
Reaktionen: TommyB
Super erklärt alles!
Ich finde es echt Lobenswert, dass ihr euch hier alle so viel Zeit und Herzblut nehmt, damit einer meiner es auch versteht.
Aktuell bin ich auch auf den PC gekommen, habe gemerkt das man dort viel besser Fehler ausfindig machen kann und diese auch besser nachvollziehen kann.
Gerade am Anfang. Die Funktion "printf" ist aktuell mein bester Freund :-D.

Diese Funktion habe ich mir auch nur geschrieben, damit ich einen ganzen Puffer (bei mir ein serieller UART) aufeinmal durchsuchen kann.
Egal wo dieser String steht, egal was danach kommt, wird nur der String gesucht. Mit 'x' kann ich dann z.B noch angeben ob ich dahinter irgendwelche Int Werte erwarte.

Für den Anfang tut es was es soll und ist mir ein echt guter Helfer. Nutze sie für die Kommunikation mit einem Bluetooth Modul (HC06)..

Kurze Nebenfrage..
@hdusel bist du Berufs Programmierer oder eher Hobby?
 
@hdusel bist du Berufs Programmierer oder eher Hobby?
Ich bin Hardware- und Embedded-SW Entwickler bei einer Münchner Firma die medizintechnische Produkte herstellt. Ursprünglich komme ich aus dem FPGA / ASIC Geschäft und kenne mich auch mit programmierbarere Logik (FPGAs, CPLD's) aus. Dann war ich einige Jahre im Automotive Geschäft als SW-Entwickler tätig (AUTOSAR, OSEK, CAN, MOST...)

Zu Haus habe ich hier alles mögliche an Hardware (von AVR, 8051, m68k, Coldfire, Renesas RX, x86, ARM, Zynx...)

Weiteres steht in meinem Profil...
 
Sehr schön! Finde ich gut.
Hast wohl auch richtig was auf dem Kasten .
Kannst du vielleicht auch noch ein gutes Buch aus deiner Sicht zum Thema C empfehlen?
 
Sehr schön! Finde ich gut.
Hast wohl auch richtig was auf dem Kasten .
Kannst du vielleicht auch noch ein gutes Buch aus deiner Sicht zum Thema C empfehlen?
Hmm C habe ich seinerzeit (1987) auf dem Amiga gelernt und bin dann bald auf C++ umgeschwenkt.

Deshalb kann ich Dir so aus dem Stand kein C-Buch vorschlagen...

Übrigens programmiere ich auf dem AVR ausschliesslich in C++. Der "Mythos", dass C++ den Code aufbläht ist falsch.

Man muss halt auf Exceptions und RTTI verzichten, dann kann man damit geniale Sachen machen.

Wo es geht "Call by (const) reference" statt by value, wenn man Objekte übergibt. Dann bleibt der Code schön schlank!

Referenzen z.B. machen den Code um vieles einfacher zu lesen, oder smart Objects wie Guards machen den Code stabiler und leserlicher. Oder die Tatsache dass man Definitionen erst dann im Code macht, wenn man sie braucht, macht den Code eleganter, oder Typed enums, Move ctors....

Was ich damit sagen will: Die Lernkurve bei C++ ist freilich höher, da man auch bei C++ erst mal C verstanden haben muss, aber man kann dann auch wirklich schöne Sachen machen. Und wenn man dann sogar C++11/14/17 auf dem System anwenden kann, dann wird es richtig freudig.

Ich wollte Deinen Algorithmus mal unter C++ umsetzen, doch leider unterstützt die AVR Toolchain keine STL (Standard Template Library). Das sind u.A. Datencontainer (Stringklassen, Assoziative und nicht assoziative Datenkontainer (maps, lists, arrays)) welche einem das Leben - gerade beim Parsen von String leichter machen. Aber wie gesagt - leider unterstützt die AVG GCC Toolchain das nicht :-/


Was verwendest Du auf dem Atmel für eine Entwicklungsumgebung? AVR-Studio, oder Arduino (GCC)?

Übrigens verwende ich CMake + einen Cross GCC 7.2.0 für AVR für meine Projekte (auch für den AVR). und lasse dann cmake ein makefile erzeugen...
 
Zuletzt bearbeitet:
Was verwendest Du auf dem Atmel für eine Entwicklungsumgebung? AVR-Studio, oder Arduino (GCC)?
Ja genau ich verwende das ATMEL Studio 7.x.x.
Bis jetzt bin ich damit auch immer gut gefahren. Für meine Bedürfnisse reicht das aus.
Auf dem PC verwende ich aktuell DEV C++. Damit kann man beide Sprachen programmieren.

Ist schon echt ne geile Sache, mit dem parsen von Strings.
 
Da @Janiiix3 ja hier lernen will, möchte ich ein paar Sachen nicht unkommentiert lassen.
Dazu muss man verstehen, dass der AVR eine sog. Harvard Architektur ist. Das heisst er hat getrennte Busse für Daten und den Programmbereich.

Beim AVR können im RAM (veränderbar und bei Powerfail flüchtig) nur DATEN liegen, aber niemals ausführbarer Code!

Dies ist eine ganz bewusste Designentscheidung der AVR Entwickler. Im Flash (nichtflüchtig und nur durch explizites umprogrammieren änderbar) liegen der Programmcode, aber es können da auch unveränderliche Daten liegen! Letztere nennt sich auch der PGM (Program Memory) Space
Erstens:
Mir ist schon klar wie Du das meinst (und wie es Jan sicher auch verstanden hat), aber genau genommen stimmt das nicht.
Was ist denn für Dich "nicht ausführbarer Code"? Es gibt doch gar keinen nicht ausführbaren - jeder nicht verwertbare Opcode wird als NOP ausgeführt.
Sowohl im SRAM als auch im Program Flash stehen irgendwelche Bytes (beim Flash als 16bit-word organisiert).
Der Knackpunkt ist, das der Instruction-Decoder nur auf den Flash zugreift, nie auf den SRAM. Es werden also immer nur die Daten aus dem Flash als Instruction interpretiert und ausgeführt - egal was auch immer da jetzt gerade drinn steht.

Steht also im Flash zB der lustige String "Janiiix3", und der Programmzeiger landet da, macht der Controller als nächste Schritte:
  • Rechenregister R20 wird mit der dezimalen Konstante 26 verORt
  • Rechenregister R22 wird mit der dezimalen Konstante 158 verORt
  • Rechenregister R22 wird mit der dezimalen Konstante 153 verORt
  • Rechenregister R26 wird mit der dezimalen Konstante 56 verglichen
"Janiiix3" ist also durchaus "ausführbarer Code" :p

Zweitens:
Die Daten im Flash sind bis auf wenige Ausnahmen(*) durchaus änderbar - sonst könnte man die ja nur einmal programmieren...
Bei einigen geht das nur "von außen" (High-Voltage Reset, oder während eines Reset usw), bei einigen geht es aber auch "von innen", also durch das Programm selbst. Auf diesem Wege kann(!) man also durchaus Daten im Flash ändern (der Sinn sei dahingestellt).

OT @hdusel : Es gibt ja auch Controller, bei denen der Flash in den SRAM remapped ist, und wie SRAM ausgelesen werden kann (beim ATtiny102/104 steht, daß load und store gehen, aber das könnte auch ein Fehler im Datenblatt sein). Wie wäre das zu deklarieren?

(*): Der UR-ATtiny10 zB. sollte nur einmal beschreibbar sein (also ohne (Page-)Erase-Funktion). Sicher auch noch andere...
 
Zuletzt bearbeitet:
Erstens:
Mir ist schon klar wie Du das meinst (und wie es Jan sicher auch verstanden hat), aber genau genommen stimmt das nicht.
Was ist denn für Dich "nicht ausführbarer Code"?

Es ist sicher "niemals ausführbarer Code" gemeint.
Havard Architektur: SRAM = Datenspeicher, Flash Memory = Programmspeicher
Selbst wenn Opcodes im SRAM vorhanden sind, lassen sich diese nicht ausführen.

bei einigen geht es aber auch "von innen", also durch das Programm selbst. Auf diesem Wege kann(!) man also durchaus Daten im Flash ändern (der Sinn sei dahingestellt).
Da fällt mir zum Beispiel ein Bootloader ein.
 
Es ist sicher "niemals ausführbarer Code" gemeint.
Wäre aber auch nicht besser..
Der Instruction Decoder könnte den Opcode ja verarbeiten - nur eben nicht von dort (direkt) laden. Prinzipiell können(!) die Daten zur Laufzeit aber aus dem SRAM (oder woher auch immer) in den Flash übertragen werden, und dann dort als Daten und/oder auszuführender Code bereitstehen.
Ja, wie auch beim Bootloader - geht ja auch ohne Boot...
 
Die Aussage war ja ...
Beim AVR können im RAM (veränderbar und bei Powerfail flüchtig) nur DATEN liegen, aber niemals ausführbarer Code!

Das ist doch völlig richtig.

Wenn du so argumentierst ...
Wäre aber auch nicht besser..
Der Instruction Decoder könnte den Opcode ja verarbeiten - nur eben nicht von dort (direkt) laden. Prinzipiell können(!) die Daten zur Laufzeit aber aus dem SRAM (oder woher auch immer) in den Flash übertragen werden, und dann dort als Daten und/oder auszuführender Code bereitstehen.

dann wären ggf. auch die Zahlen auf meinem Notizzettel oder die digitale DVBT2 Übertragung vom Miss Marple Krimi ausführbarer Code, man muss ihn nur in das Flash Memory bringen. ;)

Aber ob diese Argumentation für Jan hilfreich ist, zu verstehen, dass RAM beim AVR nur für Daten und niemals für ausführbaren Programmcode verwendet wird ...
 
Wenn du so argumentierst
Eben...
Egal was im Flash steht - der Decoder kann es immer(!) ausführen (und sei es nur als "No Operation", einen Zyklus aussetzen). Es gibt keinen Absturz, keinen Bluescreen, keine Fehlermeldung.

Was ich in hdusels Beitrag betonen wollte war, daß der Eingang des Instruction Decoders mit dem Flash-Bus verbunden ist, und nicht mit dem Datenbus. Das wurde ja dort auch angedeutet. Aber wenn es sich bei der Bytefolge xyz im Flash um ausführbaren Code handelt, dann handelt es sich bei derselben Folge im SRAM, Eeprom, oder mit Bleistift auf einen Zeitungsrand gekritzelt auch um ausführbaren Code. Wenn Du sie in den Flash überträgst, kann sie auch ausgeführt werden.

Und der zweite Punkt war, daß eben doch Daten in den Flash geschrieben werden können, auch zur Laufzeit (ja, nicht bei allen Controllern). Man kann(!) also auch Variablen im Flash anlegen. (Unabhängig davon, daß es in Hinsicht auf maximale Schreibzyklen und Pagesize und Prozedur im allgemeinen wenig Sinn macht - es ist moglich!)

Aber ob diese Argumentation für Jan hilfreich ist, zu verstehen
Es ging (meiner Meinung nach) darum zu verstehen, wie die Maschine, die den Code ausführen soll, funktioniert/aufgebaut ist.
Und das beinhaltet neben "getrennte Speicher (Busse) für Daten und Programm" eben noch etwas mehr. Daß der Decoder nur den Inhalt des Program-Flash interpretieren kann, aber daß es eben auch Möglichkeiten gibt, beliebige Daten (von beliebigen Adressen) aus dem Program-Flash zu lesen (das war ja der eigentliche Beitrag von hdusel) - aber es gibt eben auch die Möglichkeit(!), diese Adressen zu beschreiben (es müßten also nicht zwingend Konstanten sein).
 

Über uns

  • Makerconnect ist ein Forum, welches wir ausschließlich für einen Gedankenaustausch und als Diskussionsplattform für Interessierte bereitstellen, welche sich privat, durch das Studium oder beruflich mit Mikrocontroller- und Kleinstrechnersystemen beschäftigen wollen oder müssen ;-)
  • Dirk
  • Du bist noch kein Mitglied in unserer freundlichen Community? Werde Teil von uns und registriere dich in unserem Forum.
  •  Registriere dich

User Menu

 Kaffeezeit

  • Wir arbeiten hart daran sicherzustellen, dass unser Forum permanent online und schnell erreichbar ist, unsere Forensoftware auf dem aktuellsten Stand ist und der Server regelmäßig gewartet wird. Auch die Themen Datensicherheit und Datenschutz sind uns wichtig und hier sind wir auch ständig aktiv. Alles in allem, sorgen wir uns darum, dass alles Drumherum stimmt :-)

    Dir gefällt das Forum und unsere Arbeit und du möchtest uns unterstützen? Unterstütze uns durch deine Premium-Mitgliedschaft!
    Wir freuen uns auch über eine Spende für unsere Kaffeekasse :-)
    Vielen Dank! :ciao:


     Spende uns! (Paypal)