Länge eines "dynamischen" String Arrays ermitteln?

Hallo Zusammen,
folgender Beispiellose funktioniert problemlos:

char* myStrings[]={};

void setup(){
Serial.begin(115200);

myStrings[0] = "String 1";
myStrings[1] = "String 2";

}

void loop(){


for (int i = 0; i < 2; i++){
   Serial.println(myStrings[i]);

   delay(500);
   }

   
}

Dabei kann ich meinem Array (myStrings) beliebig viele Strings hinzufügen. Natürliche könnte ich diese auch in die geschweiften Klammern schreiben, nur für mein späteres Vorhaben werden die Strings zur Programmlaufzeit generiert und dann eingefügt. In der Zählerschleife möchte ich nun anstatt der zwei die “dynamische” Anzahl an Strings in meinem Array angeben.

Ich wollte die Anzahl wie folgt ermitteln.

int size = sizeof(myStrings);

Stehen die Strings in der geschweiften Klammer und rechne ich den Wert entsprechend um, funktioniert dies auch. Leider funktioniert es nicht, wenn die geschweiften Klammern leer bleiben und ich erst im Setup entsprechende Strings hinzufüge.

Was kann ich nun machen?

Die Strings zur Kompilierzeit bekanntgeben! Kann auch Platzhalter sein "..."

Dabei kann ich meinem Array (myStrings) beliebig viele Strings hinzufügen.

Und da geht die Sache schon völlig schief. Nein kannst du nicht! Du hast ein leeres Array aus Zeigern. Die ganzen Zuweisungen darauf gehen auf ungültigen Speicher. Zumindest das Zeiger Array muss eine definierte Länge haben.

const char* myStrings[10];

const weil String Literale const sind

sizeof() liefert übrigens die Länge in Bytes! Wenn man die Anzahl der Elemente möchte muss man noch durch die Größe eines Elementes dividieren.

Okay, danke schonmal, das klappt erstmal soweit, aber ist noch nicht die vollständige Lösung

const char* myStrings[10]={""};
int anzahl;

void setup(){
Serial.begin(115200);

myStrings[0] = "String 1";
myStrings[1] = "String 2";
myStrings[2] = "String 3";
myStrings[3] = "String 4";

anzahl = sizeof(myStrings) / sizeof(myStrings[0]);

delay(10);
Serial.print("Anzahl Strings in Array: ");
Serial.println(anzahl);

}

void loop(){

for (int i = 0; i < anzahl; i++){
   Serial.println(myStrings[i]);

   delay(500);
   }
 
}

Nun habe ich quasi 10 Elemente vorgeben. Ich weiß aber beim Kompilieren noch nicht ob ich 2,3 oder 100 Strings in meinem Array brauche. Kann ich diese Angabe nicht umgehen? Habe gelesen man kann auch undefinierte Arrays haben, aber dennoch müsste ich dann wissen wie viele Strings zum Laufzeit-Zeitpunkt in diesem enthalten sind um die Zählerschleife zu definieren.

Vergiss die dynamische Speicher Verwaltung auf den kleinen AVR. Du kommst in des Teufels Küche.

Könnte es auch wie folgt machen:

const int ArrayLength = 10;
const char* myStrings[ArrayLength]={""};
int anzahl;

void setup(){
Serial.begin(115200);

myStrings[0] = "String 1";
myStrings[1] = "String 2";
myStrings[2] = "String 3";
myStrings[3] = "String 4";

delay(10);
}

void loop(){

for (int i = 0; i < ArrayLength; i++){
   
   if (myStrings[i] == NULL) {
    
   } else {
   Serial.println(myStrings[i]);  
   delay(500);  
   }
 
   }
 
}

Nun werden nur beschriebene Plätze im Array behandelt und die 10 könnte ich auf 500 o.ä. setzen. Welche Maximale Array Länge ist denn noch tragbar, ohne Probleme hervorzurufen?

Das ist völlig überflüssig:

={""};

Globale Variablen werden immer auf 0 initialisiert.

Nun habe ich quasi 10 Elemente vorgeben. Ich weiß aber beim Kompilieren noch nicht ob ich 2,3 oder 100 Strings in meinem Array brauche. Kann ich diese Angabe nicht umgehen?

Nicht mit statischen Arrays und dynamischer Speicher ist wie gesagt problematisch. Auch wenn du schon statischen Speicher nicht richtig beherrscht!

Du kannst dir eine Funktion schreiben, die über das Array iteriert und solange eine Variable inkrementiert bis ein NULL-Zeiger kommt. Dann hast du die Anzahl der tatsächlich zugewiesenen Strings. Aber du kommst so nicht darum herum die Größe des Arrays zu definieren.

Welche Maximale Array Länge ist denn noch tragbar, ohne Probleme hervorzurufen?

Kommt darauf an wie groß deine Strings sind. Die landen alle im RAM. Dazu belegt jeder Zeiger 2 Bytes.

Oder du verwendest PROGMEM: https://www.arduino.cc/en/Reference/PROGMEM Die Syntax für das Array aus Strings ist da aber noch umständlicher.

Okay danke dir für die Infos!

Welche Maximale Array Länge ist denn noch tragbar, ohne Probleme hervorzurufen?

Das kann dir keiner sagen. Nicht mal zur Laufzeit. Der RAM wird auch für den Stack gebraucht, wenn Funktionen aufgerufen werden (Rücksprung-Adresse, Aufruf-Parameter, lokale Variable).

Ich weiß aber beim Kompilieren noch nicht ob ich 2,3 oder 100 Strings in meinem Array brauche

Und was machst du zur Laufzeit, wenn du mehr bräuchtest, aber nicht hast? Wenn du nichtmal weisst, wie viele Texte es werden, weisst du sicher auch nicht, wie lang sie werden sollen? Wo kommen die Texte übrigens her, wenn sie beim Kompilieren noch nicht bekannt sind?

Wenn du nichts anderes sagst, ist ein "Arduino" übrigens was atmega328p-basiertes mit 2048 byte RAM. Und solch ein kleiner 8-bit Controller kann zwar ein wenig mit Texten arbeiten, aber Textverarbeitung ist nicht seins.

Ich nutze einen ESP8266 und die Strings sind “Nutzerinformationen”. In diesem Falle sind es Geräte die später gelistet werden. Zu jedem Gerät welches der Nutzer hinterlegen möchte, speichere ich 7 Strings ab (z.B. Name, Adresse, Art, etc.). Wird ein neues Gerät hinzugefügt hänge ich es im Array einfach hinten an. Deswegen weiß ich vorab nicht wie viele Strings ich brauche :slight_smile:

Geräte... Und, was passiert bei einem Stromausfall? Die ganzen hundert eingetragenen Geräte weg?

Ich nutze einen ESP8266

Klar, so Kleinigkeiten nennt man immer zuletzt.... 100 Leute polieren ihre Kristallkugel... wie man 100 Strings in einen AVR bekommen soll.... Und dann: Dann verwendet das Wesen auf der anderen Seite des Internets, ein Dingen mit 3 MByte freiem Flash

Mein Tipp: Nutze eine feste Datensatzgröße Von mir aus auch eine SD Karte mit 16GByte freiem Flash

Hallo

das führt zu einer Idee. Wenn du weißt welche Informationen du pro Gerät/User speichern möchtest, dann ist das doch schon fertig.
Die Informationslängen legst du fest. Das musst du machen.
Zum Bsp. Namen sind max. 20 Zeichen lang, Straßennamen sind max. 30 Zeichen lang usw.
Die Daten legst du auf der Speicherkarte in einer Textdatei an, die du jederzeit ändern kannst.
Der µC liest diese nur aus und sucht nach dem passenden Eintrag/Zeile.
Dann solltest du im µC nur eine Speicherreservierung benötigen für max. eine gesamte Zeilenlänge, also für einen Eintrag.

Ich komme leider doch nicht weiter. Habe heute alles versucht und es geht nicht.
Hier noch einmal ein besseres Beispiel:

char* devices[] = {"",  "Wohnzimmer Licht", "1", "1234", "1233", "24", "1", "340", 
                                  "LED Sideboard", "1", "1119539", "1119548", "24", "1", "180",
                                  "Phiilips Colors", "1", "1119683", "1119692", "24", "1", "180",
                                  "Stereo Anlage", "1", "1120003", "1120012", "24", "1", "180",
                                  "Deckenlicht", "1", "1121539", "1121548", "24", "1", "180",                                      
                                  "Phiilips", "1", "34545", "1119692", "24", "1", "180",
                                  "Stereo Anlage", "1", "85940", "3234", "24", "1", "180",
                                  "Phiilips Colors", "1", "231", "1119692", "24", "1", "12",
                                  "Test Gerät", "1", "22346899", "23455", "24", "1", "949",
                                  "Fernseher", "1", "78494", "8899", "24", "1", "485",                           
                  };
                                                          
void setup(){
  
Serial.begin(115200);
Serial.println(""); Serial.println("");

delay(10);

int leer;

 for (int i=0; i <= XXXXX; i++){

  if (devices[i] == NULL) {
    Serial.println("Leer");
  } else {
    Serial.println(devices[i] );
  }

 }

}

void loop(){

}

Ich habe einen Char “devices”, welchen ich mit Strings UND Integern gemischt fülle. Dabei kann die Länge jedes Strings und damit auch die Bit-Größe variieren. Ebenfalls möchte ich NICHT zählen müssen wie viele Elemente in dem Array enthalten sind und möglichst auch keine Größe des Arrays angeben. Sprich die eckigen Klammern sollen leer bleiben.

Ziel soll in diesem Beispiel sein, dass mir eine Schleife jedes Element aus meinem Array in die Konsole schreibt. Dafür verwende ich Serial.println(devices*)[/b], gebe als Schleifen-Startwert die 0 an und muss nun einen Endwert definieren. Lasse ich die Schleife z.B. bis 10.000 Laufen, erhalte ich einen Laufzeitfehler, da es in dem Array z.B. kein Element 9320 gibt. Wie ermittle ich also die Anzahl an Elementen korrekt, oder verhindere in der Schleife Fehler?*

Ich habe einen Char "devices", welchen ich mit Strings UND Integern gemischt fülle

Was? Das sind alles Strings! Wenn du Strings und Integer mischen willst dann verwende ein Array aus structs (ein struct das einen String und ein oder mehrere Integer enthält). Du programmierst einfach wild irgendwas und hoffst dass es geht

Um die Anzahl der Elemente in einem Array zu berechnen dividiert man sizeof() des Arrays durch sizeof() eines Elements

Das ist ja "nur" ein eindimensionales Array. Und damit "So" eine unglückliche Wahl.

Mannooo...

Du hast 3 Mega Byte SPIFFS oder? Schaffe ordentliche Strukturen und stopfe es da rein!

Was hält dich davon ab?

Unkenntnis? Dann will ich dir gerne helfen!

Bockigkeit? Dann mach alleine...

Ich bin etwas verblüfft das dass geht. Mir ist klar das die Elemente alle als Strings behandelt werden. Meine Aussage bezog sich lediglich auf meine EINGABEN die sowohl ganzzahlig Numerisch (Integer) sein können als auch aus Zeichen bestehen (Strings). Zurück zum Thema.

Wenn vorgeschlagen klappt es in der Tat:

 for (int i=0; i <= sizeof(devices) / sizeof(devices[0]) -1; i++){

  if (devices[i] == NULL) {
    Serial.println("Leer");
  } else {
    Serial.println(devices[i] );
  }

 }

Doch wie funktioniert das Mathematisch? SizeOf(Array) gibt mir doch die verwendete Speichergröße des Arrays wieder und wenn ich dies durch die Speichergröße eines beliebigen Elementes teile, gehe ich doch implizit davon aus, dass jedes Element gleich groß ist, oder? Was ist denn wenn in einem Array nur eine einstellige Zahl drin steht und in einem anderen Element stehen 10.000 Zeichen drin?

combie: Das ist ja "nur" ein eindimensionales Array. "So" eine unglückliche Wahl.

Mannooo...

Du hast 3 Mega Byte SPIFFS oder? Schaffe ordentliche Strukturen und stopfe es da rein!

Was hält dich davon ab?

Unkenntnis? Dann will ich dir gerne helfen!

Bockigkeit? Dann mach alleine...

Haha, gute Aussage. Ich habe vorhin schon etwas zu Structs gesucht, aber keinen einfachen Artikel gefunden. Das Ding ist nicht meine Faulheit, sondern das ich nicht viel Zeit für mein Projekt habe und dann sinniger Weise mit halbwegs bekannten Arbeite, sodass ich zumindest ein Ergebnis erhalte. Wenn dann alles läuft kann ich in einem Zweiten Durchgang alles optimieren, aber im Moment interessiert mich nur der Erfolg :P

Wenn du einen guten Artikel zu Structs empfehlen kann, wäre ich dir sehr dankbar wenn du ihn verlinken könntest :)

Ich empfehle C von A bis Z für die Grundlagen. Du musst das Buch ja nicht auswendig lernen aber mal ein paar Sachen durchlesen könnte hilfreich sein.

Da steht auch was zu Arrays drin, die Du anscheinend auch nicht verstanden hast. So ganz ohne Kenntnisse wird das auch mit Hilfe nichts, besonders nicht schnell.

Gruß Tommy

gehe ich doch implizit davon aus, dass jedes Element gleich groß ist, oder?

Bei einem Array sind immer alle Elemente gleich groß. Wie soll das anders sein? Du hast ein Array aus Zeigern (wie lange die Strings dahinter sind spielt keine Rolle). Jeder Zeiger hat 2 Bytes (auf einem 8 Bit Prozessor. Auf einem 32 Bit Prozessor wären es 4 Bytes)

Wenn du einen guten Artikel zu Structs empfehlen kann, wäre ich dir sehr dankbar wenn du ihn verlinken könntest

Ein struct ist nur ein eigener Datentyp. In C++ ist ein struct auch nichts anderes als eine Klasse in der alle Member standardmäßig public sind

struct Element
{
   const char* name;
   unsigned long value1;
   unsigned int value2;
};

Element data[] = 
{
   { "Name 1", 1, 2 },
   { "Name 2", 3, 4 },
   
};

Was ist daran so kompliziert dass man eine Anleitung genau dafür braucht. Ein Array aus structs verhält sich nicht anders als ein Array aus irgendwas anderem.