Multi-Pointer-Array?



  • Hallo Leute

    Bitte nicht am interessanten Titel erschrecken 😉

    Also, wie ihr sicher schon alle erraten habt, habe eine kleineres Problem. Ich schreibe zur zeit ein kleines Programm unter Windows mit der Win32-API. Für dieses Programm will ich jetzt eine Art Configurations-File (etwa vergleichbar mit einem *.ini File). Aus diesem File möchte ich für verschiedene Variabeln von verschiedenen Datentypen die Werte lesen. Ich habe sofort an ein zweidimensionales Array gedacht, bei welchem ich den Pointer auf ein string, also den namen der Variable im Conf-File, in der ersten "Spalte" habe, und den Pointer auf die eigentliche Variable im Programm in der zweiten "Spalte" habe. So könnte ich einfach das File Zeile für Zeile einlesen, prüfen ob ein Variablename vorkommt oder nicht (anhand der string in der ersten Spalte des Arrays) und wenn eine gültige Variable im File gefunden wird ihren Wert aus dem File lesen und in der dazugehörigen Variable im Programm über den Pointer in der zweiten Spalte des Arrays abspeichern. Das wäre sehr simpel und übersichtlich (aus meiner Sicht). Nun habe jetzt das Problem, dass meine Variablen im Programm unterschiedliche Datentypen haben und ich kein Ahnung habe, wie ich Pointer unterschiedlicher Datentypen in ein und das selbe Array reinkriege.

    Ich hoffe ihr habt mein Problem verstanden und könnt mir helfen oder habt eine schlaue Alternative, ich bin ja sicher nicht der erste, der so was ähnliches versucht 😉

    Ich bin für jede Hilfe dankbar,

    mit freundlichen Grüssen

    Sputnik



  • willst du die datei auch von hand editieren können, oder reicht es, wenn man sie mit dem programm lesen kann?



  • Also zunächst würde ich kein mehrdimensionales Array verwenden, sondern ein Array von struct's (oder in C++ eine (multi)map<>).

    Zweitens: Zeiger auf beliebige Datentypen kannst du als "void*" anlegen, wenn du nur eine begrenzte Menge an möglichen Datentypen hast, bietet sich auch eine union an - aber in beiden Fällen solltst du dir irgendwo vermerken, welcher Datentyp tatsächlich vorliegt:

    typedef struct value
    {
      enum{v_str,v_int,v_dbl} typ;
      union
      {
        char*  sval;
        int    ival;
        double dval;
      } val;
    } value;
    
    typedef struct entry
    {
      char* name;
      value val;
    } entry;
    
    entry config[MAX_CONFIG_SIZE];
    


  • Sputnik schrieb:

    Ich schreibe zur zeit ein kleines Programm unter Windows mit der Win32-API. Für dieses Programm will ich jetzt eine Art Configurations-File (etwa vergleichbar mit einem *.ini File).

    win32 hat fertige funktionen dafür '(Read/Write)PrivateProfileXXX', auch eine, mit der man binärdaten in ini-files schreiben und lesen kann.
    --> http://msdn2.microsoft.com/en-us/library/ms725502.aspx
    🙂



  • Ich sehe ihr habt meine Frage verstanden und möchte erst mal euch für eure Bemühungen Danken.

    @schalt it all out:
    Ich möchte die Datei von Hand editieren können.

    @CStoll:
    Hmm, da sind wir auf einen heissen Spur. Das mit dem Array von Struct und dem union leuchtet mir auch sogleich ein. Komisch dass ich nicht selber au diese Idee gekommen bin.
    Ich verstehe leider aber nicht, für was der enum gut sein soll. Kannste das bitte mal genauer erklären?

    Kann ich das nicht so machen? Verzeihung, bin leider kein C-Guru.

    #define MAX_VARS 30
    
    typedef union
    {
        int *pInt;
        unsigned long int *pLUInt;
        double *pDouble;
    }
    MultiPointer;
    
    typedef struct
    {
        char *string;
        MultiPointer VarPointer;
    }
    ConfVar;
    
    ConfVar ConfVariables[MAX_VARS];
    

    Aber jetzt muss ich ja trotzdem den Datentyp des Pointers noch wissen, sonst weiss ich ha immer noch nicht, wo ich den abspeichern soll.

    Also wenn ich jetzt einem void * Pointer ein xbeliebiger anderen Pointer zuweise (zb int 😉 dann muss ich hier nix fürchten? Das funzt einfach so?

    Ein Beispiel:

    int nNumber = 100;
    void *voidPointer;
    
    voidPointer = &nNumber;
    
    printf("Der Inhalt der Variable auf die der void * zeigt: %i", *voidPointer);
    

    stdout:
    Der Inhalt der Variable auf die der void * zeigt: 100

    Stimmt das?

    @Undertaker:
    Ist nett das es so was gibt, aber ich möchte das selbst implementieren.
    Gründe:
    1. Damit ich den Code auch unter Linux verwenden kann.
    2. Damit ich weiss, wie man so was machen kann.
    3. Man soll die Datei von Hand editieren können, was im Bin-Modus nicht so nett ist.

    Nochmals Danke für eure Antworten

    mit freundlichen Grüssen

    Sputnik



  • Sputnik schrieb:

    ...
    Ich verstehe leider aber nicht, für was der enum gut sein soll. Kannste das bitte mal genauer erklären?
    ...

    Aber jetzt muss ich ja trotzdem den Datentyp des Pointers noch wissen, sonst weiss ich ha immer noch nicht, wo ich den abspeichern soll.

    Jetzt schau dir bitte nochmal das enum an, dann erübricht sich der zweite Punkt. Es ist nämlich genau dafür, das du weißt welcher Typ abgelegt wurde, und im Code entsprechend (z.B. über switch) entscheiden kannst.

    Sputnik schrieb:

    Also wenn ich jetzt einem void * Pointer ein xbeliebiger anderen Pointer zuweise (zb int 😉 dann muss ich hier nix fürchten? Das funzt einfach so?

    Ein Beispiel:

    int nNumber = 100;
    void *voidPointer;
    
    voidPointer = &nNumber;
    

    Vorsicht: Du kannst zwar eine Pointerzuweisung machen, aber wenn nNumber den gültigkeitsbereich verlässt ist der Inhalt der Speicherzelle (auf die der Pointer zeigt) undefiniert.

    Sputnik schrieb:

    printf("Der Inhalt der Variable auf die der void * zeigt: %i", *voidPointer);
    

    stdout:
    Der Inhalt der Variable auf die der void * zeigt: 100

    Wenn du schon nicht weißt was für ein Datentyp hinter den void* steckt, wie soll der Kompiler es erraten? void* lässt sich nicht rückauflösen, da es ein Pointer auf einen Beliebigen Typ ohne Typinformation ist.

    cu André



  • Sputnik schrieb:

    3. Man soll die Datei von Hand editieren können, was im Bin-Modus nicht so nett ist.

    wenn winapi binärdaten in eine .ini schreibt, dann sind das ein haufen hexziffern mit einer prüfsumme dahinter. lässt sich also alles mit einem texteditor betrachten.
    🙂



  • Sputnik schrieb:

    @CStoll:
    Hmm, da sind wir auf einen heissen Spur. Das mit dem Array von Struct und dem union leuchtet mir auch sogleich ein. Komisch dass ich nicht selber au diese Idee gekommen bin.
    Ich verstehe leider aber nicht, für was der enum gut sein soll. Kannste das bitte mal genauer erklären?

    Aber jetzt muss ich ja trotzdem den Datentyp des Pointers noch wissen, sonst weiss ich ha immer noch nicht, wo ich den abspeichern soll.

    Genau für diesen Zweck ist der enum da - dort mußt du mitprotokollieren, welches der union-Elemente gerade gültig ist. (d.h. wenn du etwas in (z.B.) val.dval einträgst, setzt du parallel dazu typ auf v_dbl - bzw. prüfst bei der Ausgabe, welches der Elemente eigentlich gültig ist)

    (btw, Build-in-Typen sind klein genug, um direkt in die union gepackt zu werden ;))

    Also wenn ich jetzt einem void * Pointer ein xbeliebiger anderen Pointer zuweise (zb int 😉 dann muss ich hier nix fürchten? Das funzt einfach so?

    Du kannst einen void* auf beliebige Daten zeigen lassen - für die Auswertung mußt du dann jedoch wissen, was dort drin stand, sonst kommt Müll heraus:

    int i;
    void* vptr = &i;
    double* dptr=vptr;//erlaubt, aber unsinnig (in C++ nur mit explizitem Cast)
    


  • Also ich denke ich habe dank euch jetzt eine Lösung für mein Problem. Unterdessen habe ich auch kappiert für was der enum da ist (habe vorher noch nie solche Listen benutzt in C --> schon wieder was gelernt).

    Ich denke ich werde es so machen:

    typedef struct
    {
        enum
        {
            pINT,
            pLONG,
            STR
        }
        var_type;
    
        union
        {
            char *string;
            int *pToInt;
            long int *pToLong;
        }
        pToVar;
    }
    MultiPointer;
    
    typedef struct
    {
        char VarName[MAX_CHARS];
        MultiPointer xPointer;
    }
    ConfVar;
    
    ConfVar ConfVariable[MAX_CONF_SIZE];
    int fOnOff;
    
    /* Initialisation meiner conf-Variable die im File unter dem Namen "MyIntName"
       anzutreffen ist (könnte so aussehen: MyInName = 1;). Abspeichern will ich
       den Wert 13 in der Variable fOnOff.                                        */
    strcat(ConVariable[0].VarName, "MyIntName");
    ConfVariable[0].xPointer.var_type = pINT;
    ConfVariable[0].xPointer.pToVar.pToInt = &fOnOff;
    

    Wenn ich alles richtig verstanden habe, so kann ich jetzt nurnoch den Namen "MyIntName" im File suchen, im dazugehörigen Struct den Datentyp den ich in ConfVariable[0].xPointer.var_type gespeicher habe detektieren und dann das 0 unter dem richtigen Pointer abspeichern (hier mit ConfVariable[0].xPointer.pToVar.*pToInt = 0). Ist das korrekt?

    mfg

    Sputnik



  • Nochmal: Du brauchst dort keine Zeiger auf int oder double in die union zu packen - das bringt dir nur unnötigen Aufwand, weil du sicherstellen mußt, daß diese Zeiger auch immer auf eine gültige Variable verweisen.



  • Also wiso muss ich dort speziell überwachen wenn ich im Union einen Zeiger mache? Das tue ich ja sonst auch nicht. Wäre nett, wenn man mir das erklären könnte.

    Hmm, wenn ich dort einen Zeiger machen könnte der auf eine beliebige schon bestehende Variable im System Zeigt, so wäre das sehr optimal gewesen, da alle diese Konfigurations-Variablen ja schon existieren und ich hätte sie dann nur noch mit dem richtigen Namen im Conf-File verknüpfen müssen (siehe Beispiel von vorher).

    Das scheint ja nicht möglich zu sein, ohne zusätzlichen Aufwand zur überwachung meines Pointer in der Union. Jetzt stellt sich meine Frage, ob ich das ganze dann umgekehrt machen kann.

    Hier der Code:

    typedef struct
    {
        enum
        {
            vINT,
            vLONG,
            vSTR
        }
        var_typ;
    
        union
        {
            char szString[MAX_CHARS];
            int nInt;
            long int lLong;
        }
        value;
    }
    MultiPointer;
    
    typedef struct
    {
        char VarName[MAX_CHARS];
        MultiPointer xValue;
    }
    ConfVar;
    
    ConfVar ConfVariable[MAX_CONF_SIZE];
    int *pfOnOff;
    
    /* Initialisation meiner conf-Variable die im File unter dem Namen "MyIntName"
       anzutreffen ist (könnte so aussehen: MyIntName = 1;). Abspeichern will ich
       den Wert 13 in der Variable fOnOff.                                        */
    strcat(ConVariable[0].VarName, "MyIntName");
    ConfVariable[0].xValue.var_typ = vINT;
    pfOnOff = &ConfVariable[0].xValue.value.nInt;
    
    /* einen Wert in die angelegte Conf-Variable schreiben */
    *pfOnOff = 1;   /* <-- ist das so korrekt? */
    

    Darf ich das so machen und kann jetzt die Variable über pfOnOff ansprechen wie ich das im Beispiel-Code mache?

    Nochmals vielen Dank für eure Geduld (vorallem die von CStoll).

    mfg

    Sputnik



  • den *pOnOff brauchste nicht. geht direkt so:

    ConfVariable[0].xValue.value.nInt = 1;
    

    🙂



  • Apeman schrieb:

    den *pOnOff brauchste nicht. geht direkt so:

    ConfVariable[0].xValue.value.nInt = 1;
    

    🙂

    Das weiss ich. Jetzt musst du dir aber vorstellen, dass ich ja keine Ahnung habe, in welchen Struct in meinem Array jetzt die gesuchte Variable habe. Ich müsste dann jedes Mal wenn ich den wert der Variable ändern will alle meine Structs durchsuche, weil ich die gesuchte Conf-Var ja nur anhand dem String in der Struktur identifizieren kann. Das würde mich jedes mal viel Zeit kosten da ich ja Strings vergleichen müsste und das Ganze ist über String sehr mühevoll zu handeln. Da ist es schon viel einfacher, wenn ich einfach schnell ein Pointer auf das richtige Element in der richtigen Struktur anlege, dann kann ich die Variable in meinem Programm bequem abfragen und bei bedarf auch wieder neu setzte.

    mfg

    Sputnik



  • Sputnik schrieb:

    Also wiso muss ich dort speziell überwachen wenn ich im Union einen Zeiger mache? Das tue ich ja sonst auch nicht. Wäre nett, wenn man mir das erklären könnte.

    In Zeigern steht nichts anderes als eine Speicheradresse. Was konkret in dieser Speicherzelle steht ist wiederum etwas anderes. Falls das, was ursprünglich in dieser Speicherzelle stand, ungültig wird (näheres dazu unten) ist der Inhalt undefiniert. Es kann zufälligerweise noch der alte Wert drinne stehen, muss aber nicht.

    Der Gültigkeitsbereich (Scope) von Variablen ist dir hoffentlich bekannt. Ich hoffe ich mache hier keine Fehler, da ich aus dem C++, nicht dem C-Umfeld komme:

    int main()
    {
      int* pa = 0;
      {
        int a = 4;
        pa = &a;
      }  // a verlässt seinen Gültigkeitsbereich
      // <-- pa zeigt auf ungültigen Bereich
      return 0;
    }
    

    Wenn du wiederum den Speicher über dynamische Speicherverwaltung (malloc...) allozierst, dann bleibt der Wert gültig, muss aber später auch wieder freigegeben werden.

    cu André



  • Mir ist schon klar, dass es solche Scopes gibt. Vorallem das angeführte Beispiel mit der Funktion ist für mich sonnenklar, aber trotzdem danke für die Erklärung.

    Was mich momentan mehr interessiert ist, ob es klappt, wenn ich nacher ein Pointer auf das Element der Union anlege. Ich persönlich sehe keinen Grund, weshalb nicht, da ich ja auch auf ein Element eines Struct problemslos ein Pointer anlegen kann, oder täusche ich mich da? Ich wäre sehr dankbar, wenn jemand mit wirklich Ahnung zu meinem letzten Beispiel-Code stellung nehmen könnte.

    mfg

    Sputnik



  • Sputnik schrieb:

    Ich wäre sehr dankbar, wenn jemand ... zu meinem letzten Beispiel-Code stellung nehmen könnte.

    das geht so. du kannst pointer auf union-elemente zeigen lassen. alles kein problem.
    🙂



  • Hätte mich auch arg gewundert, wenn das anders gewesen wäre. Aber ich musste mich schon ein paar mal in meiner noch jungen "Prigrammiererkarriere" wundern...

    Ich möchte noch einmal allen für ihre Hilfe danken, vorallem CStoll, der mir eine grosse Hilfe war auf dem Weg zu meiner Lösung des Problems. Ich denke, mein Problem hat sich jetzt gelöst, ich werde es jetzt so implemetieren können.

    mit freundlichen Grüssen

    Sputnik



  • Undertaker schrieb:

    ...

    Stimmt es, dass du dich früher mal mis2com genannt hast?



  • @pale dog? schrieb:

    Undertaker schrieb:

    ...

    Stimmt es, dass du dich früher mal mis2com genannt hast?

    nein, wieso? hat der besonders viel dummes zeug geschrieben?
    🙂



  • Stimmt nicht, ich habe mich nie so genannt und war in diesem Forum vorher nie registriert. Auch in keinem anderen Forum habe ich diesen Namen je benutzt. In den meisten von mir besuchten Forums ist mein Alias Sputnik (wenn er nicht schon vergeben war).

    Aber wiso interessiert das? Bin ich euch so auf die Nerven gefallen oder was hat den mis2com verbrochen?

    mfg

    Sputnik


Anmelden zum Antworten