PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : Char - Array und Pointer



Moppi
27.10.2019, 19:41
Hallo,

hier mal in Beispiel, dass ich gerne erklärt hätte:



char* myArray[128];

//Funktion
void xxx(char** ar){
ar[0] = irgendeineZeichenkette;
}

//Funktionsaufruf
xxx(myArray);


Es geht jetzt nicht darum, warum ich das jetzt genau so geschrieben habe (das ist, weil es anders nicht möglich war).
Es ist so.
Der Code funktioniert am Ende.

Ich möchte eigentlich ein Array anlegen, dass Platz für 128 Zeichenketten bietet.
Klar ist mir, dass der Compiler nicht wissen kann, wie lang eine Zeichenkette sein kann, wenn er auf dieses Konstrukt trifft: myArray[128]
Aber was passiert jetzt genau, bei char* myArray[128]? Bekomme ich hier ein Array mit reserviertem Speicherplatz für 128 Pointer (char*)?

Dann muss ich das einer Funktion übergeben, in der noch etwas mehr passiert, als das, was jetzt hier steht - eine Funktion, die ein char* erwartet: strtok()
Aus diesem Grund komme ich über die ausgeworfenen Fehlermeldungen zu dem char** . Das funktioniert zwar, aber bei char** weiß ich nicht mehr, was es bedeuten soll. Soll das ein Zeiger auf einen Zeiger sein?

Ich hoffe, dass mir das irgendeiner richtig erklären kann, zurzeit blicke ich da noch nicht durch!



MfG


PS: Falls das Thema hier deplaziert ist, evtl. bitte verschieben nach: Forum (https://www.roboternetz.de/community/forum.php) -> Microcontroller (https://www.roboternetz.de/community/forums/13-Microcontroller) -> Software, Algorithmen und KI

Ceos
28.10.2019, 06:40
Aber was passiert jetzt genau, bei char* myArray[128]? Bekomme ich hier ein Array mit reserviertem Speicherplatz für 128 Pointer (char*)?

Soll das ein Zeiger auf einen Zeiger sein?

korrekt

Du kannst Pointer auf Pointer auf Pointer auf Pointer ... biegen wie du lustig bist :)

Beim dereferenzierne musst du dann nur auf die Anzahl der Sternchen achten damit du am Ende auch beim Ergebnis ankommst und nicht versuchst das Array von Pointern als String auszugeben.

Wenn du aber eine Zeichenkette einfügen möchtest, musst du diese irgendwo anders definieren und dann den Pointer darauf in dem Array speichern oder per malloc erzeugen und später wieder freigeben (oder gleich auto::pointer nehmen, aber ich wollte hier nciht nach C++ abdriften)

Moppi
28.10.2019, 06:56
Nach einiger Recherche gestern Abend noch, soll das so sein, wie Du gesagt hast. Allerdings verstehe ich es noch nicht.
Normal habe ich einen Speicherbereich, mit einem Bezeichner dazu, um den anzusprechen.
Das würde ich evtl. so machen: char myArray[128]
Aber ich muss char* .. schreiben ?

Ich muss mein dickes C++-Buch raus holen, steht ja im Schrank. Hatte schon ganz vergessen, über die Jahre.
Vielleicht finde ich dort auch was über strtok().


Beim dereferenzierne musst du dann nur auf die Anzahl der Sternchen achten

Das funktioniert dann merkwürdiger Weise mit: Serial.println(myArray[x]);





MfG

Ceos
28.10.2019, 07:03
Das funktioniert dann merkwürdiger Weise mit: Serial.println(myArray[x]);


Das ist ja auch richtig, weil println() einen char* erwartet und du das x-te element vom typ char* übergibst!

Ein string ist ein Array aus Pointern und der Bezeichner eines Array, also "char Bezeichner[5]" ist selbst ebenfalls ein Pointer, also "Bezeichner" ist vom Typ char*, weil du die Array notation mit "[ ]" angefügt hast.

Ich bin noch nicht ganz durchgestiegen wo genau die Frage liegt, vielleicht könntest du mir die Frage noch ein wenig einengen :) oder ich brauch noch ne Tasse Kaffe (Scheiß Zeitumstellung, bringt meine innere Uhr ausm Takt)

Moppi
28.10.2019, 07:58
Das Problem liegt darin, dass ich noch nicht weiß, welche Funktionen/Methoden was für Parameter erwarten oder was sie zurückgeben.

eigentlich ist es einfach, ich wollte so was, wie das:




myString = "Hallo, dass bin ich, weil ich hier bin.";
myArray = myString.split(',');

Ausgabe = "";

for(i = 0; i< myArray.length(); i++) Ausgabe+=myArray[i]+'\n';

alert(Ausgabe);



Mehr wollte ich nicht. Bloß dass ich es jetzt nicht mit JavaScript, sondern mit C zu tun habe.
Aber wenn ich das in C machen will, muss ich mich da reinarbeiten, nutzt nichts.

Ich fand dann nicht split() sondern nur strtok().
strtok() gibt offenbar einen Zeiger auf eine Zeichenkette zurück, was man bei C wissen muss, weil man sonst damit nicht weiter arbeiten kann. Weil in C überall die Datentypen anzugeben und ob das Zeiger, Adressen oder was anderes sind. Das kann ich aber nur, wenn ich eben genau weiß, welche Funktion was genau zurückgibt und erwartet.

Sinn und Zweck der Übung ist es, von einer langwierig, für Einsteiger eher komplizierten und zeitraubenden Programmierung mit der Arduino-IDE, auf eine einfache Lösung zu kommen, wo man problem- und lösungsorientiert arbeiten kann und sich nicht überwiegend mit der Parameterübergabe und Fehlermeldungen des Compilers beschäftigen muss.




MfG

HaWe
28.10.2019, 09:23
for(i = 0; i< myArray.length(); i++) Ausgabe+=myArray[i]+'\n';
das geht allerdings nicht in C, sondern nur in C++

eine Übersicht über cstring Funktionen (ANSI/ISO C) findest du hier:
http://www.cplusplus.com/reference/cstring/

entspr. Funktion einfach anklicken, dann kommt eine Beschreibung mit code example, z.B:
http://www.cplusplus.com/reference/cstring/strtok/

- - - Aktualisiert - - -

falls du ein paar cstring Funktionen suchst, die dort nicht enthalten sind, findest du hier vlt ein paar passende:



// Library: stringEx.h
// extended C-string manipulation
//
// (C) dsyleixa 2015-2019
// freie Verwendung für private Zwecke
// für kommerzielle Zwecke nur nach Genehmigung durch den Autor.
// Programming language: Arduino Sketch C/C++ (IDE 1.6.1 - 1.6.3)
// protected under the friendly Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License
// http://creativecommons.org/licenses/by-nc-sa/3.0/ //




#ifndef STRINGEX_H
#define STRINGEX_H


//------------------------------------------------------------

char * sprintDouble();
char * strinsert();
char * strdelnpos();
char * strpatch();
char * substr();
int16_t strchpos();
int16_t strstrpos();
char * stradd();
char * cstringarg();



//----------------------------------------------------------------------------
// cstring functions
//----------------------------------------------------------------------------

char * sprintDouble(char* s, double val, int width, int prec, bool sign) {
char sbuf[20] ="\0";
strcpy(s, "\0");
dtostrf(val, width, prec, s);

if(sign && val>0) {
for (int i=width-1; i>=0; i--) {
if(s[i]==' ') {s[i]='+'; break;}
}
}
return s;
}



//------------------------------------------------------------

char * strdelnpos( char * source, int16_t pos, int16_t sublen ) { // delete a substr in string at pos
int srclen;
char * sret = source;

srclen = strlen(source);

if( pos > srclen ) return sret;
if( sublen > srclen-pos ) sublen = srclen-pos;
memmove( source+pos, source+pos+sublen, srclen+sublen);
source[srclen-sublen]= '\0';
return sret;
}



//------------------------------------------------------------

char * strpatch( char * source, char * sub, int16_t pos ) { // patch string by substr at pos
int16_t srclen, sublen;
char * sret = source;

srclen = strlen(source);
sublen = strlen(sub);

if( pos+sublen > srclen) return sret; // size/position error
memcpy(source+pos, sub, sublen);

return sret;
}



//------------------------------------------------------------

char * substr ( char * source, char * sub, int16_t pos, int16_t len ) { // get substr of source at pos by length
char *sret = sub;

if ( (pos+len) > strlen(source) ) len = strlen(source)-pos; // cut away if too long
sub = strncpy(sub, source+pos, len);
sub[len] = '\0';

return sret;
}




//------------------------------------------------------------

int16_t strchpos( char * str, char ch ) { // find 1st occurence of char ch in string str
int16_t len, i=-1;

len=strlen(str);
if(len==0) return i;
for(i=0; i<len; ++i) {
if(str[i]==ch) break;
}
return i;
}




//------------------------------------------------------------

char * stradd(char * s, int n, ...) // "adds strings" s=sumstring , n=number_in_list, ... = var string list
{
va_list vlst;
int i;

char * bufptr;
char * retptr = s;

va_start(vlst, n);
for (i=1; i<=n; ++i) {
bufptr = va_arg(vlst, char *);
strcat(s, bufptr);
}
va_end(vlst);
return retptr;
}



//------------------------------------------------------------

int16_t strstrpos(char * haystack, char * needle) // find 1st occurance of substr in str
{
char *p = strstr(haystack, needle);
if (p) return p - haystack;
return -1; // Not found = -1.
}



//-------------------------------------------------------

const int MAXLEN = 1024;
const int TOKLEN = 64;

//-------------------------------------------------------

char * cstringarg( char* haystack, char* vname, char* sarg ) { // search for &vname=sarg or ?vname=sarg, returns sarg
int i=0, pos=-1;
unsigned char ch=0xff;
const char* kini = "&"; // start of varname: '&'
const char* kin2 = "?"; // start of varname: '?'
const char* kequ = "="; // end of varname, start of argument: '='
char needle[TOKLEN] = ""; // complete pattern: &varname=abc1234

strcpy(sarg,"");
strcpy(needle, kini);
strcat(needle, vname);
strcat(needle, kequ);
pos = strstrpos(haystack, needle);
if(pos==-1) {
needle[0]=kin2[0];
pos = strstrpos(haystack, needle);
if(pos==-1) return sarg;
}
pos=pos+strlen(vname)+2; // start of value = kini+vname+kequ
while( (ch!='&')&&(ch!='\0') ) {
ch=haystack[pos+i];
if( (ch=='&')||(ch==';')||(ch=='\0') ||(ch=='\n') ||(ch=='\r')
||(i+pos>=strlen(haystack))||(i>TOKLEN-1) ) {
sarg[i]='\0';
return sarg;
}
if( (ch!='&') ) {
sarg[i]=ch;
i++;
}
}
return sarg;
}


//------------------------------------------------------------
#endif
//------------------------------------------------------------
//------------------------------------------------------------

Ceos
28.10.2019, 09:27
Ausgabe+=myArray[i]+'\n'

DAS geht so leider nicht, in reinem C gibt es keine dynamischen Strings, du müsstest also entweder hybrid mit ein bisschen C++ und String als Klasse arbeiten oder deine "Ausgabe" als Array bestimmter größe Vorinitialisieren damit genug Platz reserviert ist um alle Zeichen aufnehmen zu können.

Es gibt Funktionen wie strncat und co. aber ein simples Ausgabe+=irgendwas+"noch was statisches"; dürfte dir dein compiler eigentlich schon um die Ohren hauen.

http://www.cplusplus.com/reference/cstring/strtok/

strtok sucht halt nach dem übergebenen token und gibt dir einen Pointer innerhalb des char arrays den du dann mittels memcpy oder strcpy (oder strncpy) in ein anderes array kopieren kannst (gibt aber auch diverse andere string methoden in c)

Aber

Das kann ich aber nur, wenn ich eben genau weiß, welche Funktion was genau zurückgibt und erwartet.

Das ist die Kunsst zu googeln oder man holt sich ein gutes Buch mit inhaltsverzeichnis wenn man offline arbeiten will. Ich kann dir auch nciht aus dem FF sagen welche Funktion wie zu bedienen ist und Methoden die ich vor 3 Jahren verwendet habe muss ich auch heute immer noch nachschlagen weil man sich nciht alles merken kann. Es ist aber immer gut in Bild im Kopf zu haben wie der Speicher verawltet wird statt sich nur auf die Verwendung der EMthoden zu konzentrieren!

Ich schreib hier mal nur meine persönliche Sichtweise auf nach der ich arbeite bzw. wie ich es mir vorstelle wenn ich von einem single Thread Prozess ausgehe oder wie es halt im µController tickt, solange ich keine Interrupts einsetze!

JEDER Variable die ich anlege, wird beim compilieren vom Linker im RAM eine feste Position vorgegeben (Optimierung lasse ich mal außen vor).
Diese Variablen haben dann eine feste Größe und im Fall von Arrays sind das dann halt N viele Variablen hintereinander.

Wenn ich dynamisch arbeiten will muss sich jemand (im Fall von C ich selbst) darum kümmern diesen Platz zu reservieren und auch wieder freizugeben!

Wenn ich aber nciht mit malloc, calloc und free arbeiten möchte (ist halt absolut pain in the butt) muss ich halt entsprechend große Puffer Arrays vorhalten in denen ich dann arbeite und mir meine Ergebnisse dann irgendwo hin kopieren.

Wenn Interrupts oder Multithreading dazu kommen wirds nochmal spannender, dann musst du sicherstellen dass eine Schleife die in einem Arrray etwas macht nicht z.B. durch einen Interrupt unterbrochen wird, in welchem neue DAten ins Array geschrieben werden.

Wenn ich jetzt mal Java als Referenz nehme, wenn ich eine Liste von Bytes habe (als Ersatz für das char Array) und über diese mit einem Iterator (intelligenter Pointer der sich selbst weiterschieben kann) zugreife, dann aber ein anderer Prozess diese Liste manipuliert, schmiert mir der nächste Zugriff auf den Iterator mit der "Concurrent Modification" Exception ab, also Konkurrierender Zugriffsfehler.

In Java kann man dann das Modell der Liste ändern und eine Threadsafe Implementation nehmen ... im Einfachsten Fall wird eine Kopie deiner Liste erzeugt, über die der Iterator läuft, während die eigentlichen Daten verändert werden können. du kannst dann nur nicht erwarten, dass der Iterator sich ebenfalls verändert.

in C++ nimmt man die String Klasse, weil diese bereits dynamischen Speicher einsetzt (Aber immernoch das Problem mit "new" und "delete" hat statt malloc und free)

Oder man kann die auto:: pointer (das leerzeichen ist nur weil er sonst einen smiley draus macht) library benutzen (bim Namen bin ich mir nicht sicher) weil dir dort viel Arbeit abgenommen wird.

Strings in C sind gruselig finde ich persönlich.

HaWe
28.10.2019, 09:38
....wobei hinzukommt, dass (groß geschrieben)
String
eine spezielle Arduino-Klasse ist, die C++ "benutzt",

hingegen (klein geschrieben)
string
eine allgemeine C++ std Klasse ist, genau: std::string

Vergeicht man String mit string, fallen einem manche Gemeinsamkeiten, aber auch erhebliche Unterschiede auf, und std::string ist deutlich mächtiger als String und braucht deutlich mehr Speicher.


zur Unterscheidung sollte man die ISO C char arrays
char * mystrp;
char mystra[100]:
als cstrings bezeichnen, um Verwechslungen zu vermeiden

Moppi
28.10.2019, 09:45
Ja, danke für den Link, HaWe!!

Dann werde ich dort mal öfter reinschauen.

Allerdings, was strtok() tut, habe ich dann rel. schnell - auch wegen der Fehlermeldungen - herausgefunden. Jetzt, wo ich es nachlesen kann, deckt sich das mit meiner Vorstellung über strtok().

@Ceos
Das mit google ist so eine Sache. Dort finde ich schon längere Zeit nichts wirklich Brauchbares. Mit Pech ist unter den ersten Links Werbung (dh. die sind wenig oder gar nicht relevant) und unter den anderen Ergebnissen werden die auch nicht nach meiner Suche richtig gewichtet, jedenfalls nicht so, wie ich das erwarten würde. Wenn ich zum Beispiel "(String)" eingeben (auch gerne in Gänsefüßchen eingefasst), erhalte ich eben nicht überwiegend Erklärungen zu "String" oder "(String)" oder String und Compilern wie C/C++, sondern vornehmlich irgendeinen Unfug, der daraus entsteht, dass man in meiner Suche nach Wörtern sucht, mittels derer sich Geld verdienen lässt, was dazu führt, dass ich Kaufangebote über Strings bekomme. Probier es mal aus! Es ist grausam geworden mit google, finde ich!






MfG

HaWe
28.10.2019, 09:48
das mit anklicken von strtok war ntl nur ein Beispiel, das tolle an cplusplus.com sind die code-Beispiele zu allen einzelnen Funktionen und die Übersicht über ähnliche, zusätzliche und weiterführende Funktionen.

google macht es doppelt schwierig, weil es auch nicht Groß/Kleinschreibung unterscheidet, und String ist eben etwas anderes als string.

Moppi
28.10.2019, 09:51
Interessant: Arduino-IDE kennt printf()

Ich frage mich nur, wo geht die Ausgabe dann hin? Geht die nicht normal auf einen Standardstream oder verwechsel ich da was.
Wenn der nämlich printf kennt, könnte ich auch viele Beispiele aus meinem C-Buch verwenden/nachvollziehen.



MfG

Ceos
28.10.2019, 10:05
....wobei hinzukommt, dass (groß geschrieben)
String
eine spezielle Arduino-Klasse ist, die C++ "benutzt",

hingegen (klein geschrieben)
string
eine allgemeine C++ std Klasse ist, genau: std::string


Danke für den Hinweis, ist mir irgendwie garnicht aufgefallen, weil ich strings gerne meide :P

HaWe
28.10.2019, 11:11
printf verschwindet bei Arduino-AVR (und vermutlich auch bei ARM Cortex) Boards im Nirwana, denn dort existiert auch kein stdout.
esp hat da sicher andere Einstellungen, denn hier gibt es auch ein stdio.h (edit: dennoch fraglich, ob auch vollständig wie bei Posix) und hier wird stdout in die Serial Konsole umgeleitet (IIRC).
Man kann allerdings sprintf() mit Serial.print verwenden (akzeptiert String und cstring), muss aber bei der sprintf-Float-Formatierung ("%f") bei AVR boards aufpassen ("%f" funktioniert aber bei ARM Cortex und bei esp)

std::string gibt es auch nicht bei Arduino, außer ggf. bei esp (freeRTOS <string> IIRC).
aber man benutzt ja meist als Arduino-User das abgespeckte/veränderte String.
Wenn du, Moppi, allerdings von Arduino IDE/API weg willst, gibt es ntl auch kein String mehr.

Ceos
28.10.2019, 12:09
gibts dann auch kein
http://www.cplusplus.com/reference/memory/auto_ptr/

in arduino?! halte ich jetzt schon für etwas krass, hätte geadcht das arduino eigentlich die volle c++ unterstützung mitbringt O_o

PS: scheint als ob auto_ptr teil von std geworden ist ... wie die zeiten sich ändern
PPS: in c11 sogar deprecated da heißt es http://www.cplusplus.com/reference/memory/unique_ptr/

ich habe gerade gänsehaut dass mich das zeug bald einholen wird Q_Q

HaWe
28.10.2019, 12:15
nein, Arduino bringt nirgends die komplette C++14 std lib oder C99 mit Posix-Definitionen mit, wegen dem micktigen RAM bei AVRs. Bei ARM Cortex wird es sukzessive besser.
esp aber hat Erweiterungen, z.B. auch pthread und std::thread.

schreib doch mal ein kleines Programm mit auto_ptr, ich teste es mal.

Ceos
28.10.2019, 12:20
weist du wie lang es her ist dass ich c++ syntax in meinem gedächtnis gegen python eingetauscht habe? XD ich bekomm ja nichtmal mehr ein schulbeispiel zusammen ohne googeln :P
ich wünschte ich könnte mich noch an die korrekte syntax erinnern eine klasse zu definieren in c++

ich preogrammiere auch mittlerweilen fast ausschließtlich in reinem C auf µControllern, weil alles andere am RAM scheitert (malloc calloc und co. sind auch tabu)
oder micropython wenns ein ARM >512k flash ist, weils einfach so urig einfach und bequem ist :D

HaWe
28.10.2019, 12:27
das wiederum ist der Vorteil vom Arduino API, das ein abgespecktes C++14 benutzt, angepasst auf AVR, Intel Galileo, ARM Cortex M0, M3, M4 und ESP 8266/32
Ich wüsste aber auch keinen Vorteil, den auto_ptr für Arduino User bieten sollte. Ich habe es mit dem testen ja auch nur angeboten, weil du selber auto_ptr vorgeschlagen hattest.

HaWe
28.10.2019, 15:20
zurück zum TOP:

char** myarray;
Bekomme ich hier ein Array mit reserviertem Speicherplatz für 128 Pointer (char*)?
nein, reservierten Speicherbereich hast du nicht, du musst dafür jeden der 128 char arrays bei der Deklaration mit festen cstrings vor-belegen
char** myarray = {"dies ist der 1. cstring","dies ist der 2. cstring","dies ist der 3. cstring","dies ist der 4. cstring",...}
- sonst schreibst du wild ins RAM hinein.
char** myArray ist ja nur eine Anfangs-Adresse für eine beliebige Liste unbekannter Länge mit 1 Pointer auf eine (einzelne) char-Speicheradresse,
und
char* myArray[128] ist nur eine Anfangs-Adresse für eine 128er Liste mit je 1 Pointer auf eine (einzelne) char-Speicherzelle

Den reservierten Speicher hast du nur mit
char myArray[128][100]; // 128 x char array zu je 100 char Länge

wenn du dies an eine Funktion übergeben musst, die nur char* oder char** akzeptiert, kannst du type-casten

(char**)myarray // wandelt myarray[][] vorrübergehend in char** myarray um
(char*)myarray[] // wandelt myarray[][] vorrübergehend in char* myarray[] um


du kannst auch einen 2-dim array definieren (zur variablen Belegung mit cstrings) und einen pointer auf ein solches Konstrukt:

char myArray[128][100]; // 128 x char array zu je 100 char Länge
char **ppArr;

dann kannst du auch die Speicheradressen einander zuweisen:
ppArr= (char**)myArray;
und dann ppArr an deine Funktion als Parameter übergeben.

Moppi
28.10.2019, 15:30
Danke für Deine Ausführungen!

Das war das, was ich machen wollte. Inzwischen bin aber unsicher, ob ich damit besser gestellt bin.

strtok() gibt nur Zeiger zurück. Dann müsste ich die Zeichenketten erst ins Array kopieren, um sie später auszuwerten. So weit ich gesehen habe, kann ich die aber auch genügend über div. Funktionen und Pointer auswerten.

Denke ich so richtig?

Wobei ich mich noch frage, was strtok() genau macht. Ich bekomme dort verschiedene Zeiger auf die Teilzeichenketten zurück. Erstellt strtok() jetzt selber schon ein Array mit den Zeichenketten, zu denen ich nur die Pointer bekomme?
Bei dieser Funktionalität steige ich noch nicht richtig durch.



MfG

HaWe
28.10.2019, 15:37
nein, strtok ermittelt eine gefundene Speicheradresse eines cstrings (edit: ) vor/bis zur ersten angegebenen delimiter-Bruchstelle,
und der Ausgangs-cstring wird entsprechend heruntergekürzt (der Rest-cstring hinter dem 1. delimiter):



char * strtok ( char * str, const char * delimiters );

Parameters

str
C string to truncate.
Notice that this string is modified by being broken into smaller strings (tokens).
Alternativelly, a null pointer may be specified, in which case the function continues scanning where a previous successful call to the function ended.
delimiters
C string containing the delimiter characters.
These can be different from one call to another.


Return Value
If a token is found, a pointer to the beginning of the token.
Otherwise, a null pointer.
A null pointer is always returned when the end of the string (i.e., a null character) is reached in the string being scanned.

ersetze mal im Beispiel printf durch Serial.println und main() durch void setup() und du wirst sehen, was passiert:

Example
http://www.cplusplus.com/reference/cstring/strtok/


/* strtok example */
#include <stdio.h>
#include <string.h>

int main ()
{
char str[] ="- This, a sample string.";
char * pch;
printf ("Splitting string "%s" into tokens:\n",str);
pch = strtok (str," ,.-");
while (pch != NULL)
{
printf ("%s\n",pch);
pch = strtok (NULL, " ,.-");
}
return 0;
}




Output:


Splitting string "- This, a sample string." into tokens:
This
a
sample
string

Moppi
28.10.2019, 16:10
Ich habe jetzt folgendes ausprobiert:

Das ursprüngliche Char-Array mit strtok() bearbeitet.
Die zurückgegebenen Pointer gespeichert.
Das ursprüngliche Char-Array mit einem andern String überschrieben und die gespeicherten Pointer von strtok() zur Ausgabe verwendet.

Jetzt stellt sich das so dar:

Es wird kein zusätzlicher Speicher durch strtok() reserviert, um die Teilzeichenketten woanders zu speichern.
strtok() fügt an den Trennzeichen der Tokens offenbar ein Endezeichen (0) ein, also werden dann die Trennzeichen überschrieben mit 0. Der ursprüngliche Inhalt des Char-Array wird also überschrieben.
Wenn nun mittels Serial.println(xxx[index]); die Teilzeichenketten ausgegeben werden, funktioniert das mit dem ursprüngliche Char-Array, bearbeitet von strtok(), wie erwartet.
Wenn das Char-Array überschrieben wurde, werden trotzdem (aufgrund der Pointer in das Array) Teilzeichenketten ausgegeben und zwar jeweils von der Position, die durch den Pointer im Array bestimmt ist, bis zum Ende des Char-Arrays (bzw. bis ein Endezeichen - vermutlich 0 - auftritt).

Die Pointer von strtok() zeigen in das Char-Array, das von strtok() bearbeitet wurde.
Ist daher mit den Zeigern eine sparsame Methode und durchaus nicht unpraktisch, solange das Char-Array nicht erneut überschrieben wird, bevor die Tokens alle verarbeitet sind. Was ansich auch eine Falle beim Programmieren sein und zu unerwarteten Ergebnissen führen kann.

HaWe
28.10.2019, 16:13
ja, ganz genau, genau das meinte ich oben mit "ermittelt eine Speicheradresse" und "kürzt den rest-cstring herunter" (der cstring-Anfangspointer wird einfach ein Stück erhöht).
willst du die tokens zwischenspeichern, kannst du sie auf andere char* Variaben übertragen oder per strcpy/strncpy in char[] hineinkopieren.
Willst du den Ausgangs-cstring behalten, musst du ihn ebenfalls zwischenspeichern.

Moppi
28.10.2019, 16:23
Ja, so siehts aus.

Danke Dir!

- - - Aktualisiert - - -

Daher muss ich aber die Pointer auch nicht speichern, das ist unnütz. Wenn, müsste ich die Teilzeichenketten speichern, wie Du beschrieben hast .
Ich muss für jede weitere Teilzeichenkette einfach strtok() erneut aufrufen und bekomme den nächsten Zeiger.

Ceos
28.10.2019, 17:19
Ich wüsste aber auch keinen Vorteil, den auto_ptr für Arduino User bieten sollte

du musst nicht selber new und delete auf den Objekten machen die du auf dessen Basis erstellst und er trackt für dich die Zugriffe wenn cih den unique_ptr richtig verstanden habe. Das Problem bei Objektorientiertem C++ mit New ist, dass du selber sicherstellen musstd ass du jedes Objekt sauber löschst bevor du die referenz darauf freigibst und der unique_ptr macht das alles für dich wenn du z.B. ein array von "MyString : std::unique_ptr" erstellst.

Gerade weil ohne tieferes Wissen da viel Potential für crash drin steckt, vor allem auf einem uC

da ist jedes "Sandardmittel" recht ... wenns denn drin wäre :P

HaWe
28.10.2019, 17:54
jaaaa - das wäre tatsächlich ein Vorteil!

@moppi: ich meinte mit Teilzeichenketten die tokens, und das sind ja Pointer auf char 8)

Moppi
28.10.2019, 18:05
ich meinte mit Teilzeichenketten die tokens

Das hoffe ich! Nur Tokens ist mir zu english. Deutsch kennt Worte, die einen Sachverhalt besser/präziser beschreiben können. Ich bleibe deshalb eigentlich lieber bei Deutsch.

:)

HaWe
28.10.2019, 18:11
stimmt
aber wegen der tokens heißt die Funktion ja nun auch strtok ;)
Teilzeichenketten konnte man offenbar auch missverstehen als Rest-Strings ;)
Aber gut, das wir jetzt wissen was wir meinen und meinen was wir wissen ;)

Moppi
28.10.2019, 19:29
Ich suche noch einen Link zu: Wie erweitere ich zur Laufzeit ein Objekt/Klasse um Eigenschaften?
In meinem Buch kommt OOP leider nicht vor.
Alles sonst habe ich so weit gefunden, was man im Quelltext (Klasse) anlegt, einbindet.
Suche noch eine Beschreibung, wie man eben, wenn das Programm läuft, einer Klasse oder Struktur eine Eigenschaft (also z.B. einen Wert oder Zeichenkette mit Bezeichner) hinzufügt.
Ich bezweifle, das sowas wie: meinObjekt.Name2 = "Müller"; einfach so funktionieren wird, wenn Name2 nicht schon in meinObjekt existiert hat.
Aber vielleicht funktionbiert es ja auch mit: meinObjekt.Name2 = new String("Müller"); ???


MfG

HaWe
28.10.2019, 20:18
Ich Suche noch einen Link zu: Wie erweitere ich zur Laufzeit ein Objekt/Klasse um Eigenschaften?
In meinem Buch kommt OOP leider nicht vor.
Alles sonst habe ich so weit gefunden, was man im Quelltext (Klasse) anlegt, einbindet.
Suche noch eine Beschreibung, wie man eben, wenn das Programm läuft, einer Klasse oder Struktur eine Eigenschaft (also z.B. einen Wert oder Zeichenkette mit Bezeichner) hinzufügt.
Ich bezweifle, das sowas wie: meinObjekt.Name2 = "Müller"; einfach so funktionieren wird, wenn Name2 nicht schon in meinObjekt existiert hat.
Aber vielleicht funktionbiert es ja auch mit: meinObjekt.Name2 = new String("Müller"); ???


erweitern von Klassen durch weitere Variablen oder Methoden zur Laufzeit ist meines Wissens nicht möglich.
Diese müssen schon bei der Deklaration fest stehen, genau wie man keine zusätzlichen Zeilen zur Laufzeit in eine Funktion schreiben kann, keine array-Größen erhöhen/verlängern kann und keine zusätzlichen undeklarierten Variablen nachträglich zur Laufzeit einfügen kann, weder global noch lokal in Funktionen oder Strukturen.

Stelle dir ein Objekt vereinfacht als C-Struktur vor, nur sind jetzt zusätzlich zu Variablen jetzt auch noch Funktionen enthalten.

meinObjekt.Name2 = "Müller";

geht also nur, wenn ein Objekt
class foo{}
bereits per Deklaration eine Variable enthält
public:
String Name2 ;

dann geht nach Instanziierung per
foo meinObjekt;
meinObjekt.Name2 = "Müller";


PS:
es ist analog zu Strukturen:


typedef struct
{
String Name1, Name2;
} foo;

foo meineStruct;
meineStruct.Name2= "Müller";

auch hier kann man zur Laufzeit keiner Variablen Name3 etwas zuweisen.

Moppi
28.10.2019, 21:13
Ich nenne mal als Beispiel JavaScript. Hier ein Link dazu: https://developer.mozilla.org/de/docs/Web/JavaScript/Guide/Mit_Objekten_arbeiten
Das was dort beim Erstellen von Objekten aufgezeigt ist, geschieht zur Laufzeit. Es können auch stets neue Eigenschaften, die im Quellscript noch nicht enthalten sind, später hinzugefügt werden. So weit ich etwa weiß, geht das auch mit JSON. Dass man Objekte importieren kann. Objekte, die während der Erstellung des Quelltextes gar nicht bekannt sind, werden zur Laufzeit erstellt.




MfG

Moppi
29.10.2019, 04:49
Ich habe alles ausprobiert, was mir so einfiel und gesucht und gesucht. Ich habe nichts Brauchbares gefunden. Es scheitert immer daran, dass man im C-Quelltext für Arduino nicht eine Eigenschaft (Variable) einem Objekt nachträglich hinzufügen kann, die nicht irgendwie definiert ist. Ich glaube der Lösungsansatz in diese Richtung liegt bei verketteten Liste .. da war mal was ...
Nach kurzem Anschauen ist es das, was man dafür benötigt, um dynamisch (übergeordnete) Objekte zur Laufzeit abzubilden / zu erstellen / zu verändern. Ich nenne die mal übergeordnet, weil sich so Strukturen (Objekte wenn man so will) aus Klassen (die im Quelltext definiert wurden) zusammensetzen und verwalten lassen.

Klebwax
29.10.2019, 07:15
Ich nenne mal als Beispiel JavaScript.

JavaScript wird interpretiert. Da baut der Interpreter zur Laufzeit Strukturen und Objekte. C bzw C++ wird compiliert. Da baut der Compiler die Strukturen und der Linker plaziert sie im Speicher. Daher kann zur Laufzeit nichts geändert werden. Da sind auch aus Variablennamen längst Speicheradressen geworden.


I... im C-Quelltext für Arduino nicht ....

Das gilt immer für C, hat mit Arduino nichts zutun.

MfG Klebwax

Moppi
29.10.2019, 08:08
So weit war ich bei C/C++ noch nicht durchgestiegen. Ich kann mir bis jetzt noch nicht vorstellen, dass tatsächlich keinerlei Möglichkeit besteht, Objekte nachträglich zu erweitern. Wenn das aber so ist, Pech gehabt.
Liegt vielleicht daran, dass in C/C++ unter OOP verstanden wird, dass man die Programmierung (Erstellen, Warten, Pflegen des Quelltextes) vereinfacht; ich dagegen Objekte aber als Objekte betrachten und verändern möchte, die man auslagern und bei bedarf wieder eingliedern kann, ebenso dass sie grundsätzlich veränderbar sind (zumindest Eigenschaften hinzuzufügen und zu entfernen) und zwar jederzeit.

Das JavaScript eine Interpretersprache ist, ist schon klar. Bloß mir noch nicht, wie genau dort auf unterster Ebene das Objekthandling implementiert wird. Mir kommt der Gedanke auf, dass dies nicht anders gemacht wird, als es sich hier gerade mit C aufzeigt. Man weiß ja von Maschinencodeseite, dass es grundsätzlich möglich ist, auch zur Laufzeit neuen Maschinencode zu generieren, der auch ausgeführt werden kann - also sich selbst verändernder Code. Dass das in geschützen Modi der CPUs ab 80386 anders oder zumindest nicht mehr so einfach jederzeit möglich ist, weil es eben Privileglevel gibt, ist eine andere Sache.

Bei C/C++ gibt es std::list
So kann man zur Laufzeit Kopien von Objekten erstellen und diese untereinander per Listen verketten und so verwalten.



MfG

HaWe
29.10.2019, 08:31
nein, mit C++ auf Microprozessor-Basis (AVR, ARM, ESP) sehe ich keine Chance.
Eventuell geht es über Betriebssysteme mit dynamischen Link-Libraries (DLL wie bei Windows), wobei eine DLL zur Laufzeit erzeugt und dann neu eingelinkt wird...
Aber Interpreter wie Javascript arbeiten komplett anders als executables aus C++ Programmen, wie Klebwax schon schrieb, und Interpreter-Code ist auch um 10er-Potenzen (1/100 oder gar 1/1000) langsamer.
OOP sagt auch nur aus, dass grundsätzlich objektorientiert programmiert werden kann (Objekte, Instanzen, Vererbung) und sagt nichts darüber aus, was man damit machen kann oder auch nicht.

Ceos
29.10.2019, 08:38
Ich verstehe gerade nicht warum man ein Objekt zur Laufzeit überhaupt einen neuen Member hinzufügen muss.

Wenn man weis was man möchte braucht man doch zur Laufzeit nichts hinzufügen!? In Java (Am Beispiel modded Minecraft) kann man Klassen und daraus resultierende Objekte manipulieren und neue Member hinzufügen weil man zum Beispiel die Funktion einer Tür erweitern möchte ohne eine gänzlich neue Türklasse zu erstellen. Aber da geht es ja nur darum die Funktionalität zu erweitern und das patchen der Klassen funktioniert auch nur solange gut, wie von der Klasse auch noch kein Obejkt erzeugt worden ist!


meinObjekt.Name2 = "Müller"; einfach so funktionieren wird, wenn Name2 nicht schon in meinObjekt existiert hat.
Aber vielleicht funktionbiert es ja auch mit: meinObjekt.Name2 = new String("Müller"); ???


Wenn es darum geht, warum nicht einfach

meinObjekt.Namen[2] = "Müller";
wobei Namen ein Listen-Objekt ist

Moppi
29.10.2019, 08:48
Wenn man weis was man möchte braucht man doch zur Laufzeit nichts hinzufügen!?

Doch schon. Es gibt ganz einfache Beispiele, wie die einer Schallplattensammlung. Wo Du einfach neue Objekte hinzufügen möchtest, ohne dass das z.B. auf 50 Objekte beschränkt wäre. Während Schallplatten bestimmte Informationen hergaben, die man in Eigenschaften eintragen könnte, könnte es sein, dass CDs noch eine weitere Eigenschaft haben, die man auch gerne hinzufügen würde, ohne den Programmcode dafür ändern zu müssen.



MfG

Ceos
29.10.2019, 08:53
Wo Du einfach neue Objekte hinzufügen möchtest, ohne dass das z.B. auf 50 Objekte beschränkt wäre.

Exakt das habe ich doch gerade beschrieben?! Ein std::list Objekt ist dynamisch, man kann also jederzeit (solange der RAM es hergibt) neue Objekte hinzufügen (bei einem µC würde ich aber eine LinkedList nehmen, keine Ahnung was das Äquivalent in C++ ist)


Während Schallplatten bestimmte Informationen hergaben, die man in Eigenschaften eintragen könnte, könnte es sein, dass CDs noch eine weitere Eigenschaft haben, die man auch gerne hinzufügen würde, ohne den Programmcode dafür ändern zu müssen.

Öhm wie wäre es wenn du dann einfach der Schallplatte beibringst dass sie sich selbst erklärt?! Warum müssen die Informationen über die Schallplatte in der "Liste" doppelt gespeichert werden, das ist doch Speicherverschwendung und die Liste interessiert nicht ob das eine Schallplatte aus Gold, Nickel, Paltin oder Uran ist oder nach Einhornpups duftet, solange sie mit der grundlegenden Schallplatte-Klasse kompatibel ist oder?

HaWe
29.10.2019, 09:29
@moppi:
bei deinem Schallplatten-Beispiel änderst du nicht das Objekt selber, sondern fügst nur dynamisch einen neuen Eintrag oder mehrere hinzu. Das ist genau das, was Adressen-Datenbanken machen, wenn du eine neue Person mit einer neuen Anschrift und Tel.-Nr. hinzufügst. Die Anzahl der Einträge ist nach oben offen, wenn du sie dynamisch erzeugst.
Du fügst dann aber kein neues Objekt (keine neue Klasse, keine neue Instanz) hinzu, für das z.B. auch die Anzahl der PCs und TVs im Haushalt oder die qm der Wohnung eingegeben werden kann.
Für ganz neue Felder musst du das Objekt von vornherein mit diesem Feld konstruieren und dann neu kompilieren.

https://www.eu-datenbank.de/datenbanken-programmierung/c-datenbanken/

Moppi
29.10.2019, 09:43
Für ganz neue Felder musst du das Objekt von vornherein mit diesem Feld konstruieren und dann neu kompilieren.

Das haben wir weiter oben schon festgestellt, entspricht nicht so unbedingt meiner Vorstellung von Objektorientierung.

Stell Dir nur vor, Du könntest als Mensch dein Smartphone nicht händeln. Warum? Weil als Du geboren wurdest, das noch nicht existierte und das Gehirn nicht in der Lage ist, sich darauf einzustellen, weil die Struktur, um ein Smartphone zu handeln und Informationen darüber zu speichern, ja noch nicht existieren konnte. Du könntest es zwar sehen, aber nie zuordnen was es ist, bzw. wie man es bedient und was man damit tun kann; Du müsstest erst Nachkommen haben, deren Gehirn dann drauf ausgerichtet / eingestellt ist, weil es sich komplett neu entwickelt, also sozusagen neu kompiliert wird.

MfG

- - - Aktualisiert - - -

Aber: so weit ich gelesen habe, ist OOP dafür gedacht, die Softwareentwicklung zu vereinfachen. Nicht um selbstmodifizierenden Code zu erstellen.

Ceos
29.10.2019, 09:52
Deine Vorstellung von selbstmodifizierendem Code ist grundlegend falsch!
Wenn wir mal dein Beispiel nehmen ist die Programmiersprache (und der daraus generierte Code) deine DNA, welche bestimmt wie diene Zellen konstruiert werden und wie sie sich verhalten.
Das was uns lernfähig macht ist das Objekt "Neuron" in dem Sinne, welches flexibel konfiguriert werden kann und im Zusammenhang des großen Ganzen erlaubt Informationen auf Programmebene zu verarbeiten.

Der Kernel in Linux kann sich grundsätzlich (auf Codelevel) nicht selbst umschreiben! Es sind fest geschriebene Programme, welche es erst erlauben dass ein Teil des Speichers modifiziert werden kann und dass du einen Teil des Binärcodes zur Laufzeit austauschen kannst, aber auch erst wenn alle Abhängigkeiten davon freigegeben worden sind oder du braucht einen Neustart.

HaWe
29.10.2019, 10:26
entspricht nicht so unbedingt meiner Vorstellung von Objektorientierung.
Ich schließe mich Ceos an: deine Vorstellung von OOP entspricht nicht den tatsächlichen Gegebenheiten,
und ob C++ nun deiner Vorstellung entspricht, das mag sein wie es will.
Aber C++ ist genau wie Delphi zweifelsfrei eine OO-Programmiersprache (ursprünglich: "C mit Objekten" bzw. "Pascal mit Objekten", frag die Enwickler Bjarne Stroustrup und Niklaus Wirth), und wenn du darüberhinaus gehende Möglichkeiten suchst, um zusätzliche Variablen-Bezeichner während der Laufzeit erzeugen zu können, musst du die Programmersprache wechseln, ggf eben Javascript.

Moppi
29.10.2019, 10:32
Der Kernel in Linux kann sich grundsätzlich (auf Codelevel) nicht selbst umschreiben! Es sind fest geschriebene Programme, welche es erst erlauben dass ein Teil des Speichers modifiziert werden kann und dass du einen Teil des Binärcodes zur Laufzeit austauschen kannst, aber auch erst wenn alle Abhängigkeiten davon freigegeben worden sind oder du braucht einen Neustart.

Jeder Code der durch eine CPU interpretiert wird und also eine Aktion bewirkt, kann sich grundsätzlich selbst umschreiben. Jedenfalls ist das seit den 8086 so. Dafür existieren alle notwendigen Maschinenbefehle. Ob er das zur Laufzeit darf, liegt daran, welche Privilegien ein Software-Entwickler vorgesehen hat; anders gesagt: welche Möglichkeiten er durch Privilegierungsmaßnahmen verbietet. Falls es Maschinencode für eine bestimmte CPU gibt, der das nicht kann, dann bitte mal hier, bei der Gelegenheit die CPU und Hersteller nennen!

Nachtrag
----------
Was durchaus richtig ist, das ein Teil eines Maschinenprogramms, der sich gerade in der Abarbeitung befindet, sich nicht selbst verändern darf ("kann"). Es muss erst zu einem anderen Bereich im Programm gewechselt werden, wo die Veränderung stattfindet, um dann zurückzukehren.

MfG

HaWe
29.10.2019, 10:54
Jeder Code der durch eine CPU interpretiert wird...
per C++ und einem C++-Compiler erzeugter Code wird nicht interpretiert. Er wird in den Speicher eines Prozessors geladen, alloziert einen gewissen Speicher für Code und statische Variablen im Stack und für dynamische Variablen auf dem Heap und hat eine feste Einsprungadresse. Variablendeklarationen (d.h.: ihre Speicheradressen, mehr ist es ja nicht) liegen im stack und können zur Laufzeit nicht verändert und auch keine weiteren hinzugefügt werden.
Aber letztlich sind deine Vorstellungen ein Streit um des Kaisers Bart und es hat auch nichts mit OOP an sich zu tun. ;)

Moppi
29.10.2019, 10:56
Ein Maschinencode, meinetwegen 0C oder EA, ist nur ein Datum. Mehr nicht. Der Code selber tut gar nichts, solang der nicht von der CPU geladen und zur Verarbeitung der ggf. dem Datum nachfolgenden Daten oder bereits geladener Daten herangezogen wird.



MfG

Ceos
29.10.2019, 10:57
das ist so nicht richtig Moppi, du hast einen Programmcounter, welcher dem Prozessor sagt wo die nächste Instruktion steht, die Instruktionen entscheiden dann darüber wohin der Programmcounter wandert und was sonst noch so im Prozessor passiert.

Die Instruktionen stehen bei einem µC meistens in einem Flashspeicher oder im RAM, bei einem PC ausschließlich im "Speicher" (gesamtheit aus Cache, RAM und was da sonst noch an Magie drin steckt für mehr Speed)

Du kannst also nur dann ein Programm sich selbst modifizieren lassen, wenn du ihm beibringst wie es den Speicher modifiziert.

Jetzt kommen wir mal aus Maschinencode zurück zur Programmiersprache, in einer Programmiersprache hast du Regeln für Syntax und Grammatik die ein Compiler (nicht NUR Compiler, auch Linker etc., eh hier einer Spitzfindig wird) dann zu Maschinencode macht.

Wenn ich also "Code" selbst umschreiben lassen möchte, muss es in den Regeln der Sprache und der enthaltenen Unterprogramme eine entsprechend Entwickelte Routine geben die es dir erlaubt Zugriff auf den Speicher mit dem gespeicherten Maschinencode zu nehmen und diesen zu ändern.

C++ ist eine Sprache die üblicherweise von einem Compiler zu Maschinencode übersetzt wird, während bei Java z.B. vorher Bytecode generiert wird der dann wie JavaScript interpretiert wird.

Ich kann den Bytecode im RAM verändern und damit sein Verhalten nachdem er neu interpretiert worden ist, genau so wie cih den JS im Speicher ändern kann bevor daraus vom Interpreter der veränderte Maschinencode gemacht wird.

Du vermischst hier Hochsprachen und Maschinencode.


Das was du möchstest erreichst du nur wenn du ein sich selbst modifizierendes Programm IN einer Programmierrsprache deiner Wahl schreibst.

Es ist von der Srpache und der bereitgestellten Librarys/Unterprogrammen abhängig in wie weit du das im Rahmen der Programmiersprache selbst schon erschlagen kannst, aber C++ kann jedenfalls nicht seine Klassen zur Laufzeit umschreiben.

Gönn dir mal ein Buch zu Rechnerarchitektur und Compilern

HaWe
29.10.2019, 11:13
ich finde, es wird jetzt äußerst off-topic und andere Fragen sollte man in einem neuen Topic diskutieren.

Moppi
29.10.2019, 11:15
Man muss doch nachfragen, ob etwas mit einer Programmiersprache neu implementiert werden muss oder ob die Sprache an sich schon Möglichkeiten bietet, das, was man möchte zu tun. Um mehr ging es doch nicht.





MfG

HaWe
29.10.2019, 11:21
ja, hmm, ok, evtl, aber die Frage wurde ja schon beantwortet:
aus C und C++ compilierte Executables können ihren eigenen Programmcode nicht während der Laufzeit ändern und daher können auch keine neuen Variablen während der Laufzeit hinzugefügt werden.

Moppi
29.10.2019, 11:31
Nochmal nachgeschoben:


Das was du möchstest erreichst du nur wenn du ein sich selbst modifizierendes Programm IN einer Programmierrsprache deiner Wahl schreibst.

Das ist das, was ich schon, im größeren Rahmen, getan habe. Nun stellte sich für mich die Frage, ob es auch für die kleinen µC, programmiert auf C-Basis, eine effektivere Möglichkeit gibt (weil die langsamer sind und weniger Speicherplatz zur Verfügung haben). Scheint aber so nicht zu sein, dafür gibt es z.B. verkette Listen. Nu muss ich sehen, ob ich Willens bin, daraus etwas zu machen.




MfG

- - - Aktualisiert - - -


ja, hmm, ok, evtl, aber die Frage wurde ja schon beantwortet

Ja, war sie meiner Meinung nach schon, wenn vielleicht auch an anderer Stelle.



MfG

- - - Aktualisiert - - -

Weil ich das ganz gut erklärt fand, hier mal ein Link:

https://www.virtual-maxim.de/dynamische-datenstrukturen-%E2%80%93-einfach-verkettete-liste/

HaWe
29.10.2019, 11:45
Weil ich das ganz gut erklärt fand, hier mal ein Link:

https://www.virtual-maxim.de/dynamische-datenstrukturen-%E2%80%93-einfach-verkettete-liste/
ja, danke für den Link, das ist gut erklärt!
Und es funktioniert genau wie schon oben geschrieben wurde, über dynamische Erzeugung neuer Variablenwerte
Listenelement *neuesListenelement = new Listenelement(film);
genau wie man sonst auch per new einen neuen Variablenwert dynamisch auf dem Heap erzeugt, aber eben ohne die Struktur selber zu verändern
Listenelement(Film film)
{
this->film.genre = film.genre;
this->film.jahr = film.jahr;
this->film.titel = film.titel;
this->nachfolger = NULL;
}

Ceos
29.10.2019, 13:00
der Vorteil einer verketteten Liste ist weniger Speicherverwürfelung, erzeuge ich eine Liste mit 10 Elementen und ist diese dynamisch aber im Speicher als Array konzipiert, ist der Zugriff auf einzelne Elemente schneller als bei eienr verketteten Liste. Eine Änderung der größe bedingt dann allerdings dass man genug Platz übrig haben muss und ggf. die ganze Liste irgendwo neu hinkopieren muss.

Eine verkettete Liste muss nur das Speicher-Objekt erzeugen und die Kettenglieder verlinken, daher habe ich das extra erwähnt, allerdings auf kosten der Suchzeit nach Einzel-Elementen, da man erst die Elemente einzeln über die Kettenglieder ablaufen muss (gerade auf dem meist begrenztem RAM eines µC)

Rand Info: C# und Java brauchen kein "klassisches" Speicher Management bei dem man sich selbst um das löschen der Objekte kümmern muss, die lassen das Objekt einfach liegen und ein Mechanismus genannt Garbage Collector räumt hinterher auf, indem er zur Laufzeit (ich meiner hier die Zeit wenn die Applikation arbeitet und nicht pausiert ist) die Objekte im Speicher prüft ob noch Refereenzen darauf existieren und diese ggf. zum löschen markiert. In einem kurzen Zeitfenster, in dem die Applikation dann angehalten wird, löscht er die Objekte aus dem Speicher. (ACHTUNG: das war sehr vereinfacht und umfasst nichtmal 10% der tatsächlichen Speicheverwaltung oder Nebenwirkungen die damit einhergehen)

HaWe
29.10.2019, 15:16
Noch ein paar pros/cons dazu:
Wegen des begrentzen RAMs bei Arduinos ist die Anzahl der verketteten Listen-Einträge ebenfalls nur recht klein und wird nicht zu einem Geschwindigkeitsproblem führen.
Blättern vor/zurück lässt sich mit einer doppelt verketteten Liste erhöhen.
Zeitkritsch sind eher schon SD-Lade- und Speichervorgänge, aber auch das wird sich bei der zwangsläufigen Listenkürze in Grenzen halten.
Da man solche Listeneinträge sicher nicht jede Sekunde immer wieder neu erzeugt und andere löscht, wird es vermutich auch nicht massive Speicherfragmentierung oder gar Speichergarbage geben, und bei jedem Neustart wird ja auch immer wieder neu aufgeräumt, also ist man wohl mit new/delete auf der hinreichend sicheren Seite, während andererseits auto_ptr etc. wieder deutlich höheren Speicherbedarf hat.