dynamisches Array

hallo zusammen,
ich würde gerne eine Feld erstellen in dem die Anzahl der Feldelemente zufällig sein soll.

Ich habe mir das so gedacht:

int zufall = random(2,5);
int zufallsarray[zufall] = {};

und dann später das Feld in einer Schleife mit Werten füllen.

Jetzt kriege ich aber die Fehlermeldung :

error: variable-sized object 'zufallsarray' may not be initialized

was bedeutet das? muss ich meine Idee vielleicht doch mit malloc realisieren?

wäre super wenn mir jemand helfen könnte :slight_smile:

Der C++-Standard sieht keine Arrays dynamischer Länge vor. Willst du solche hinzufügen bedarf es also solcher Dinge wie new oder malloc.

Zum Beispiel so:

void setup() {
 // groesse des arrays ermitteln
 int zufall = random(2,5);

 //erstmal nur ein zeiger auf das array
 int* zufallsarray;
  
  //speicher in der passenden groesse reservieren
  zufallsarray = (int*)malloc(zufall*(sizeof(int)));

  //an allen stellen auf 0 setzen (optional)
  for (int i=0;i<zufall;i++) {
    zufallsarray[0] = 1;
  }
}
...
void loop() {
    ....

Perfekt! Danke :slight_smile: vielen vielen Dank :slight_smile:

//an allen stellen auf 0 setzen (optional)

for (int i=0;i<zufall;i++) {
   zufallsarray[0] = 1;

Ist das so richtig? Wenn ja, muss ich nochmal zurück zu den basic tutorials...

ich hätte jetzt zufallsarray[i] = 0; erwartet...

Deine Fassung sieht auf jeden Fall sinnvoller aus.

o_lampe:

//an allen stellen auf 0 setzen (optional)

for (int i=0;i<zufall;i++) {
   zufallsarray[0] = 1;

Ist das so richtig? Wenn ja, muss ich nochmal zurück zu den basic tutorials...

ich hätte jetzt zufallsarray[i] = 0; erwartet...

Das Füllen mit 0 kann man sich übrigens sparen, wenn man statt malloc die Funktion calloc verwendet, die macht das gleich mit.

Rudi

o_lampe:
Ist das so richtig? Wenn ja, muss ich nochmal zurück zu den basic tutorials...
ich hätte jetzt zufallsarray[i] = 0; erwartet...

Das ist natürlich richtig. Sorry für die Nachlässigkeit, das Beispiel war nur fix aus der Hüfte geschossen. Im Prinzip ging es mir vorwiegend darum, das wie man dynamisch den Speicher für ein Array alloziiert.
Mario.

Ich greife diesen sehr informativen Fred noch mal auf, weil er (fast) genau mein Prob trifft.
Jetzt komme ich progtechnisch aus der Delphi-Ecke und bin des C nicht soooo mächtig. Zum anderen ist mein Mega zum selbstprobieren noch nicht da.
Also hier erstmal ein theoretische Anfrage.
Die Aufgabenstellung mal kurz:

Ich möchte einen Stepper sozusagen eine "Figur" fahren lassen - soll heissen: Einige U's langsam vorwärts, diverse U's schneller vorwärts, dann einige U's gemäßigt rückwärts usw. Dazu bekommt die Steuerung/der Arduino jeweils ein Kommando aus Integern: Richtung, Anzahl Steps, Geschwindigkeit (und ggf.noch ein paar mehr).
Eine "Figur" (oder auch Sequenz) besteht aus aus einer definierten Anzahl gleicher Kommandos, deren Anzahl nicht festliegt !
D.h., es können mal weniger als 10 sein oder auch nur ein Kommando, durchaus aber auch mal mehr. 20, 50, 100 ?

So ein Kommando würde ich dann in eine Struktur verpacken wollen:

typedef struct CMD_record
   {
       int Dir;
       int Steps;
       int Speed;
   };

Das wäre doch so weit OK - oder ?

Nun kommt die Dynamik ins Spiel. Ich bräuchte ein Array aus der o.g. Struct in variabler Länge, da ich wie gesagt nicht weiß, wie lang so eine Sequenz ist/werden kann. Statisch zu groß vereinbart verballer ich entweder immer jede Menge Speicher oder bekomme umgekehrt nie/selten eine komplette Sequenz in den Speicher.
Also liegt der Gedanke nahe, ein dynamischen Array aus o.g. Struktur zur Laufzeit anzulegen.
In Delplhi für mich kein Thema, aber auf dem Arduino mit C .....

Der steuernde PC würde erst mal anfragen, ob ich für "NumSeq" auch genug Speicher habe.
Als (globale) var würde ich definieren:

CMD_record* CMD_Seqence

Wäre folgendes zum "Speicherholen" richtig ?! (mit calloc gleich alles auf 0 setzen)

CMD_Sequence  = (int*) calloc (NumSeq * (sizeof (CMD_Record)));

Ist dieses (int*) vor dem calloc OK so ? Sind ja alles nur int's ....

Ob nun wie auch immer OK oder nicht (ihr werdet mich sicher belehren) - wie bekomme ich heraus, das diese Alloziierung auch erfolgreich war ?
Sprich (noch) genügend Speicher vorhanden und Befehl erfolgreich ?
Oder muss/sollte man das vorher prüfen ? Wenn ja, wie ?

Vorrausgesetzt, alles ist "grün", würde ich dann in meine Seriell-Auswerteroutine die Daten vom PC wie folgt setzen:

for (int i=0; i<NumSeq; i++) {
  /* hier wird jeweils vorher Dir, Steps und Speed ausgewertet */
  CMD_Sequence.Dir[i].Dir = Dir;
  CMD_Sequence.Dir[i].Steps = Steps;
  CMD_Sequence.Dir[i].Speed = Speed;
}

Und zum Schluss: Wie werde ich die alloziierten Daten in CMD_Sequence wieder rückstandsfrei los ?

Zusatzfrage für die Spezi's:
Wenn man solche "Aktionen" z.B. mit/bei mehreren Motoren durchführt, welche alle unabhängig laufen und unterschiedlich lange "Figuren" haben, könnte ich mir sehr gut vorstellen, das irgendwann mal der Speicher in der Belegung etwas "zerklüftet" ist und sich größere Blöcke nicht mehr am Stück belegen lassen.
Managed ein Arduino so etwas selbsttätig oder muss man da mit so was wie in der Art eines Festplattendefragmentieres ran und aufräumen ?

http://www.cplusplus.com/reference/cstdlib/calloc/

Calloc gibt dir null zurück wenn es nicht funktioniert hat und einen void pointer wenn es geglückt ist. Der int Pointer ist falsch, du du ein Array von Pointern auf structs willst und nicht ein array aus integer Pointern.

Das Gegenstück zu malloc/calloc ist "free":
http://www.cplusplus.com/reference/cstdlib/free/

Was theoretisch auch möglich wäre ist ein Vector aus der Standard Template Library:

Sieht Ressourcen-mäßig gut aus:

Dynamic memory usage for a vector is quite good as there is almost no additional overhead over and above the space required for the objects themselves. I have implemented a default allocate-ahead policy of 20 objects which you can change if you want (see later). If you have a rough idea of how many objects you are going to need then you can greatly cut down on memory resizing by calling the reserve(n) function ahead of time.

Statisch zu groß vereinbart verballer ich entweder immer jede Menge Speicher oder bekomme umgekehrt nie/selten eine komplette Sequenz in den Speicher.
Also liegt der Gedanke nahe, ein dynamischen Array aus o.g. Struktur zur Laufzeit anzulegen.
In Delplhi für mich kein Thema, aber auf dem Arduino mit C .....

Auf einem Controller kann man keinen Speicher verballern.
Entweder du hast so viel wie du brauchst, dann reserviere ihn gleich, oder eben nicht.

Natürlich kann man zur Laufzeit ausprobieren, ob malloc funktioniert, aber eigentlich sollte man bei solchen Anwendungen vorher wissen, was geht. Kann auch sein, dass malloc dir zwar einen Speicherbereich zurückliefert, aber wehe du machst danach noch einen Funktionsaufruf.
Der Stack ist nicht so rücksichtsvoll, sondern zerschreibt dir deinen Speicher. Oder durch Schreiben in den dynamischen Speicher machst du dir den Stack kaputt.

Wenn du dir eh Gedanken machst, krieg doch gleich raus, wieviel geht, beleg das statisch und merk dir die Größe als Konstante.

Oder nutzen Controller heutzutage ihren RAM-Speicher, wenn nichts los ist, temporär für einen Bildschirmschoner ?

Das Problem mit dem Stackoverflow hast du mit statischem Speicher natürlich auch, zugegeben. Aber da bist du dann selber schuld.
Ich wollte ja auch nur mal gewarnt haben :wink:

Wir haben auch im Studium in Unterschiedlichen Fächern / andere Professoren gelernt, dass wir niemals dynamische Speicherverwaltung auf einem Mikrocontroller verwenden sollen. 2kB - 8kB Ram sind sehr schnell belegt wenn ein Fehler in der Anwendung auftritt und dann such mal.

Es kam schon schlaue Leute die das versucht haben bei einem Koffer Gebäck System an einem Flughafen. Resultat der Flughafen hat deutlich später eröffnet. Der Schaden ging in die Millionen. Der Fehler lag an einem zu klein gedachten Speicherbereich. Die Suche nach dem Fehler hat Monate gedauert. So wie ich mich dran erinnere war der Speicher nur wenige Bytes zu klein .....

@Serenifly
Also wäre das dann so richtig ?!

CMD_Sequence  = (CMD_Record*) calloc (NumSeq * (sizeof (CMD_Record)));

Das mit den Vektoren check ich (noch) nicht ...

@michael_x
Wenn ich von vornherein wüsste, wieviele Sequenzen für eine Figur kommen, würde ich mir da sicher keinen Kopf machen.
Das soll ja eben der Clou an dieser Steuerung sein - in Kombination mit einem Flag "Endlos" dann auch bis zum Sankt Nimmerleinstag.
Natürlich kann ich auch hingehen und rechen: Was brauch ich für Variablen ? Wie groß könnte der max. Bedarf für den Stack sein, lasse noch ein paar hundert Byte Luft und greife mir dann den Rest (sagen wir einfach mal 6 KB) und hätte dann Platz für max. 1000 Records äh Structs.
Sicherlich funktionell, aber da sträubt sich in mir irgendwie alles.

... aber wehe du machst danach noch einen Funktionsaufruf.
Der Stack ist nicht so rücksichtsvoll, sondern zerschreibt dir deinen Speicher. Oder durch Schreiben in den dynamischen Speicher machst du dir den Stack kaputt.

Natürlich kommt mit Sicherheit danach nicht nur ein Funktionsaufruf ! Wie darf ich "nicht so rücksichtsvoll" verstehen ? Schreibt ein Arduino einfach so planlos in den Speicher und guckt nicht vorher irgendwo nach, ob der schon reserviert ist ?
Das wäre ja fatal, desaströs !

@Jomelo
Ja, eben sind 2-8KB ratzfatz belegt. Vor allem, wenn man es vom PC her so gewohnt, sich mal eben schnell nen Paar MB zu holen ....
Es könnte ja auch durchaus sein, das wenn man noch andere LIBs mit eingebunden hat und diese sich auch noch die einen oder anderen Bytes in unbekannter Menge holen (wollen) sehr schnell ein Prob mit zuvor benannter Rechenweise bekommt und der kleine Kalkulator dann out of memory ist ...

Mich beschleicht da eher so ein Gefühl, dass diejenigen, welche mit erhobenem Zeigefinger vor dynamischer Speicherverwaltung warnen, eher keinen Plan davon haben, wie man es richtig und sicher macht.
Es sei denn, der Arduino macht mit seiner vermeintlich seltsamen Speicherverwaltung (das sage ich nur einfach mal so) einem tatsächlich einen trich durch die Rechnung macht.

PS: Wer bei zu klein dimensioniertem Speicher nicht in der Lage ist, mittels Logik, Debuggern oder im worst-case jeden Schritt protokollieren lassen, so einen Fehler nicht in adäquater Zeit findet, der sollte sich vielleicht mal nach nem anderen Job umsehen. Natürlich bib es fiese Käfer, die sich gut tarnen & verstecken - aber finden tut man die mit Verstand und Logik immer !

Schreibt ein Arduino einfach so planlos in den Speicher und guckt nicht vorher irgendwo nach, ob der schon reserviert ist ?
Das wäre ja fatal, desaströs !

Ich denke mal: ja. Was soll er denn sonst machen ?

Der RAM enthält von der einen Seite her statische Variable, von der anderen den Stack. Dazwischen ist der dynamische Speicher.
Dass (ohne new oder malloc) der Stack und statische Variable sich überschneiden können, hab ich schon erlebt: Führt nicht immer zu einem Reset :wink:

Die wenigsten ( die new / malloc verwenden ), prüfen zur Laufzeit, ob sie den Speicher überhaupt bekommen haben, habe ich den Eindruck...

TERWI:
Das mit den Vektoren check ich (noch) nicht ...

Ein Vektor ist eine Datenstruktur, die einem Array ähnelt, aber die Größe passt sich dynamisch an und man kann Elemente beliebig hinzufügen und entfernen. In C++ ist das Teil der Standard Template Library, aber diese ist in der Arduino IDE standardmäßig nicht enthalten. Wohl auch da sie mit dynamischem Speicher arbeitet. Wurde aber von Usern implementiert.

Ein Vektor hat im Hintergrund ein Array und wird mit einer einstellbaren Standardgröße initialisiert (da nimmt man was man ungefähr erwartet + etwas mehr). Wenn diese Größe erreicht ist, wird ein neues, größeres Array erstellt und das alte hinein kopiert. Das geschieht automatisch im Hintergrund. Damit ist man nicht auf eine Feste Größe beschränkt, aber erkauft sich das mit etwas Overhead. Im Gegensatz zu komplexeren Strukturen hält sich das aber in Grenzen.

Hier ist die Dokumentation der Standard C++ Version:
http://www.cplusplus.com/reference/vector/vector/

Bezüglich deiner Anweisung. Wenn du so ein einzelnes Struct erstellen willst, muss noch das NumSeq raus. Das brauchst du nur wenn so für das ganze Array auf einmal Speicher reservieren willst (Anzahl der Structs * Größe eines Structs). Dann musst du den Pointer aber auch einem Array zuweisen. Dessen Variable ist zwar auch ein Pointer auf das Struct, aber du musst irgendwann mal sowas machen:
CMD_Record CMD_Sequence[10];

Sowie ich das sehe bringst du da zwei Sachen durcheinander. Entweder du hast ein Array aus Structs. Dann musst du mit calloc den gesamten Speicher reservieren und den Pointer einem Array zuweisen. Oder du hast ein Array aus Pointer auf Structs. Dann musst du einmal Speicher für die Pointer allozieren und dann jedes Struct einzeln erstellen (so wie du es jetzt hast, aber ohne das "mal NumSeq"). Das wäre für dich wahrscheinlich praktischer, da die Structs dann irgendwo im Speicher liegen können und das Array kleiner ist (an Speicher, nicht an Länge).

aber du musst irgendwann mal sowas machen:
CMD_Record CMD_Sequence[10];

Da wird der Denkfehler des Delphianers liegen.
"NumSeq" = Anzahl der Struct's ist einfach nur ne globale Variable, die ich mir vom PC für ne Anfrage schicken lassen will, ob ich auch überhaupt so viel Speicher aktuell frei habe.
Wie sag ichs jetzt dem C++-Zeigerverbieger ? In Delphi geht das ganz einfach so:
.... den Record (als Typ) definieren - geht ja in C mit Struct genauso. (s.o.)
Dann ein Array auch als Typ definieren: TCMD_Sequence := array of TCMD-Record;
Eine Variable dazu anlegen (das ist dann eigentlich auch nur ein Zeiger): CMD_Sequence := TCMD_Seqence;
Dann passend Speicher dazu alloziieren mit SetLength(CMD_Sequence, NumSeq) und gut ist das. Dass das auf dem Arduino nicht so klappt ist klar.
Wäre dann ja so was wie ein Vector

Geht so was wie wie: CMD_Record CMD_Sequence[] ohne definition der Werte zur Längenfestlegung ? Und dann ein calloc darauf ?

Wie das nun auch immer zu statten gehen kann/soll/muss: Der letzendliche Zugriff auf dieses gseicherte Kunstwerk aus Daten sollte dann lauten:
CMD_Seqence[x].Dir(Steps, Speed) wobei x dann eine Zählvar. von 0 bis NumSeq-1 ist.

Die Bäume wachsen hier ..... unglaublich. Man sieht gar keinen Wald mehr. :grin:

Geht so was wie wie: CMD_Record CMD_Sequence[] ohne definition der Werte zur Längenfestlegung ? Und dann ein calloc darauf ?

Nein.
Ein calloc liefert einen pointer der gecastet werden muss,
new CMD_Record[ count ] liefert ein Array von CMD_Record , dessen Elemente als CMD_Sequence[ i ] oder über * (CMD_Sequence + i) addressiert werden können.

Statt struct solltest du über class nachdenken, dann kannst du dir wünschen, wie sie initialisert aussehen ( muss ja nicht 0 sein )

Das geht auch in C genauso. Dann hast du ein Array aus Structs. Die Inhalte des Structs liegen dann direkt hintereinander im Speicher. Lediglich die Länge ist halt zur Compile-Zeit vorgegeben. Wenn du da Speicher allozierst dann musst du "Anzahl der Structs * sizeof(Struct)" machen. Sowie du es da stehen hattest. Der Pointer der da von malloc/calloc zurückkommt ist auf struct, da Arrays in C eben nur Pointer auf das erste Element sind. Aber du musst den trotzdem deinem Array zuweisen.

Oder du machst "CMD_Record* CMD_Sequence[10]" (und danach mit memset alles auf NULL zu setzen). Dann hast du ein Array aus 10 Pointern auf Structs. Dabei bist du genauso in der Länge limitiert, aber jeder Pointer hat nur 2 Byte und die Structs müssen nicht existieren(!). Das ließe sich einfacher in ein größeres Array kopieren wenn du mehr willst. Du würdest dann die Structs jeweils einzeln allozieren und die Pointer die du mit calloc bekommst sequentiell in das Array schreiben. Und selbst wenn du 50 Pointer hast, sind das nur 100 Byte und nicht 50 * 3 * 2 Byte, wie wenn du 50 structs mit je 3 Integern erzeugst und dann die Hälfte nicht brauchst.

...... ihr macht mich völlig fertig ! 8) Ja was denn nun ? Geht, aber so nicht, wenn du so, dann auch nur bedingt und wenn so, dann ..... muss ich erst ein paar Mädels casten ?
Das ist ja wie inne "Polletick": Alle wissen genau, worum's geht (!) - aber alle labern endlos drumherum und keiner kommt auf den Punkt. Warum ? Keiner weiß es ....

Gebt's zu: Ihr wollt mich, den alten Mann, doch nur testen, ob ich selber drauf komme ? Ne, Iss klar ..... :smiley:

Schreib doch bitte mal einer von euch beiden oder am besten beide zusammen, wie ich das denn nun konkret in dieser grausigen Sprache C bewerkstelligt bekomme.
Die Struktur ist klar, bzw. die 3 Werte, die ich daraus /setzen lesen will !?
NumSeq ist nach wie vor der vorher ermittelte Multiplikator, wieviele Strukturen ich brauche / haben möchte. Man könnte es auch in NumStruct umbenennen. Vielleicht besser .... Oder NumCMD .... (ist mir absolut wurscht)

Es muss doch irgend wann mal einen Zeiger geben, der auf einen zusammenhängen Speicherbereich zeigt, in dem z.B 123 Strukturen zu je 6 Byte liegen ? Also 738 Byte zusammen.
Wie castet man nun in C ? Wahrscheinlich auch nicht anders als in Delphi. Man nehme einen Typ von "Array of Record", mache eine Variable draus und weise den Zeiger zu. Peng - fertig.
.... ab gehts mit CMD_Seqence[x].Dir(Steps, Speed) = .... (das Beispiel im letzen Postung war falsch !)

Bin mal eben weg, neues Bier holen .... :wink:

Was ich schreibe ist vielleicht mehr Pseudocode:

CMD_Record* CMD_Sequence = (CMD_Record*) calloc (NumSeq * (sizeof (CMD_Record));

Das ist ein Array namens CMD_Sequence aus CMD_Record. Das muss auf CMD_Record Pointer gecastet werden, da CMD_Sequence nur ein Pointer auf das erste Element des Arrays ist.

Oder du machst das:

CMD_Record* CMD_Sequence[123];
memset(CMD_Sequence, 0, 123 * sizeof(CMD_Record*));

Du hast dann erstmal 123 Pointer auf CMD_Records mit je 2 Byte, aber diese Structs existieren noch nicht! Das müsstet du dann nicht unbedingt dynamisch machen (es sei denn du willst doch mal mehr Pointer). Was du hier dynamisch machst ist bei Bedarf structs allozieren und die in das Pointer-Array schreiben.

z.B.

CMD_Sequence[77] = (CMD_Record*) calloc (sizeof(CMD_Record))

Das ist für ein einzelnes Struct, deshalb fällt die Sequenz-Nummer weg. Praktisch würdest du dann irgendwo einen Zähler mitlaufen lassen, der dir angibt wie viele structs existieren und über den das Array adressieren.

Wenn du das Array voll belegst, verbrauchst du natürlich durch die Pointer mehr Speicher. Aber wenn du es nicht voll ausnutzt sparst du welchen. Kommt halt dann drauf an...

Ein dynamisches Array auf Pointer wäre das:

CMD_Record** CMD_Sequence = (CMD_Record**) calloc (NumSeq * (sizeof (CMD_Record*));

Einen Befehl, den du vielleicht noch brauchst:
http://www.cplusplus.com/reference/cstdlib/realloc/
Damit kannst du ein Array vergrößern in dem du es in einen größeren Speicherbereich kopierst (das ist das was ein Vektor automatisch macht).