Wie "Daten" speichern?



  • Ich versuche meine Frage einmal an Hand eines Beispieles zu erklären...

    Gegeben sei die Struktur »config«:

    typedef struct {
      char *path;
      ...
    } config;
    

    und die Methode »config_load(char *path)«, welche eine Konfigurationsdatei läd. Die Variable vom Typ »config«, welche die geladenen Daten der Datei dann speichert, soll einem globalem Array »configs« hinzugefügt werden und ein Pointer darauf zurückgeliefert. Bei einem weiterem Aufruf von »config_load« sollen dann zuerst alle bereits geladene Konfigurationen durchsucht werden, um zu prüfen, ob diese bereits geladen wurde (dazu das globale Array).

    Wie man das umsetzen kann, ist mir klar. Es geht mir nun darum, wie man das "typisch" löst (also das cachen der Konfigurationen).

    Meine erste Überlegung sah so aus:

    int nconfigs;
    config *configs;
    
    config *config_load(char *path) {
      // prüfen, ob die Konfigurationsdatei bereits geladen wurde... 
      // wenn nicht: 
      if(nconfigs == 0) {
        configs = malloc(sizeof(config));
      } else {
        configs = realloc(configs, sizeof(config)*(nconfigs+1));
      }
      nconfigs++;
    
      // ... laden ...
    
      return &configs[nconfigs-1];
    }
    

    Das Problem bei der Umsetzung ist aber ja, dass sich die Position von den Konfigurationen bei jedem realloc() ja verändert, wodurch zuvor zurückgegebene Pointer auf falsche Speicherbereiche zeigen ... Deswegen dachte ich mir, man könne ja auch mit IDs arbeiten:

    typedef struct {
      int id;
      char *path;
      ...
    } config;
    
    int nconfigs;
    int curid;
    config *configs;
    
    int config_load(char *path) {
      // prüfen, ob die Konfigurationsdatei bereits geladen wurde... 
      // wenn nicht: 
      if(nconfigs == 0) {
        configs = malloc(sizeof(config));
      } else {
        configs = realloc(configs, sizeof(config)*(nconfigs+1));
      }
      nconfigs++;
      configs[nconfigs-1].id = curid++;
    
      return configs[nconfigs-1].id;
    }
    
    config *config_find(int id) {
      int i;
      for(i = 0; i < nconfigs; i++) {
        if(configs[i].id == id) {
          return &configs[i];
        }
      }
    }
    

    Wobei das wohl nicht wirklich performant wäre und noch eine zusätzliche, globale Variable benötigen würde. Daher die dritte Überlegung: Die globale Variable »configs« speichert nur die Pointer auf die Konfigurationen:

    int nconfigs;
    config **configs;
    
    config *config_load(char *path) {
      config *configuration;
    
      // prüfen, ob die Konfigurationsdatei bereits geladen wurde... 
      // wenn nicht: 
      if(nconfigs == 0) {
        configs = malloc(sizeof(config *));
      } else {
        configs = realloc(configs, sizeof(config *)*(nconfigs+1));
      }
      nconfigs++;
    
      configuration = malloc(sizeof(config));
      configs[nconfigs-1] = &configuration;
    
      // ... laden ...
    
      return &configuration;
    }
    

    Nun habe ich noch "gelesen", dass String-Arrays oft nullterminiert werden, was ja hier im Grunde auch funktionieren sollte:

    config **configs;
    
    config *config_load(char *path) {
      config *configuration;
    
      // prüfen, ob die Konfigurationsdatei bereits geladen wurde... 
      // wenn nicht: 
      if(configs == NULL) {
        configs = malloc(sizeof(config*)*2);
      } else {
        configs = realloc(configs, sizeof(configs)+sizeof(config*)); // oder so...
      }
    
      configuration = malloc(sizeof(config));
      configs+sizeof(configs)/4-2 = &configuration; // oder...
      configs+sizeof(configs)/4-1 = NULL;           // so...
    
      // ... laden ...
    
      return &configuration;
    }
    

    (Funktioniert das überhaupt so?)

    Hm, nun meine eigentlich Frage: Was ist nun die beste Möglichkeit bzw. was ist "typisch"? Oder ist das alles mumpitz?



  • Eine Idee wäre es, den Index in dein Config-Array als ID zu verwenden. Der bleibt nach einem realoc() gleich und du kannst ohne Aufwand die einzelne Konfiguration damit wiederfinden.



  • Das würde dann problematisch werden, wenn Konfigurationen gelöscht werden, denn dann verändern sich ja die Indizes.



  • Na gut, dann eine andere Alternative: Du speicherst deine Konfigurationen nicht in einem Array, sondern in einer (einfach oder doppelt) verlinkten Liste.



  • Well schrieb:

    Das Problem bei der Umsetzung ist aber ja, dass sich die Position von den Konfigurationen bei jedem realloc() ja verändert, wodurch zuvor zurückgegebene Pointer auf falsche Speicherbereiche zeigen ...

    Wie kommst du denn darauf?
    Der Speicherbereichsinhalt wird von realloc bekanntlich nicht geändert, nur bei Bedarf im Ganzen verschoben.



  • Wutz schrieb:

    Der Speicherbereichsinhalt wird von realloc bekanntlich nicht geändert, nur bei Bedarf im Ganzen verschoben.

    Und Zeiger die auf Stellen innerhalb des Bereiches zeigen werden dann ungültig.
    Also muss er alle Zeiger für seine Konfigurationen auch mit "verschieben". Sehr umständlich, sehr aufwändig.



  • Natürlich nicht. Im realloc Speicherbereich stehen die Zeiger drin und werden dadurch natürlich nicht ungültig nur weil sie evtl. irgendwo anders hinkopiert werden.



  • In der ersten Lösung dort oben hatte Well die Konfigurationen als ein dynamisches Array angelegt und Zeiger in dieses Array nach außen gegeben. Und damit hätte er dann irgendwo im Programm Zeiger auf den Bereich, der beim realloc() umkopiert wird.



  • CStoll schrieb:

    Na gut, dann eine andere Alternative: Du speicherst deine Konfigurationen nicht in einem Array, sondern in einer (einfach oder doppelt) verlinkten Liste.

    Danke ... Eine Liste hatte ich hier »auf den ersten Gedanken« verworfen, da es mir irgendwie ... »Unpassend« erschien (an dieser Stelle). Genauer betrachtet scheint das aber gar nicht so doof zu sein. 🙂

    Wutz schrieb:

    Wie kommst du denn darauf?
    Der Speicherbereichsinhalt wird von realloc bekanntlich nicht geändert, nur bei Bedarf im Ganzen verschoben.

    Genau das meinte ich. Entschuldigung, wenn ich mich falsch ausgedrückt haben sollte.


Anmelden zum Antworten