Enum + Array von Strukturen -> 'foreach' ?



  • Grüß Gott!

    Habe nach reichlich Grübeln und einiger Recherche leider nicht die passende Antwort auf diese Frage gefunden:

    Ist es möglich, eine Art "foreach" Schleife zu basteln, indem man eine Liste in Form von

    struct arr 
    {
       unsigned char name[15];
       unsigned char ort[20];
    } ; 
    struct arr arrL[100]
    

    zugrunde legt und die beiden Elemente der Struktur "name" und "ort" sozusagen mit enum durchnumeriert und so irgendwie (pointer?) ansprechen kann?

    Gemeint wäre:

    for (int i = 0; i < 100; i++)
    {
       for (j = 0; j < 2 ; j++)
       {
          arrL[i][j] = FunktionDieEinenCharPointerZurueckGibt ();
       }
    }
    

    Wäre nett, wenn Ihr mir antworten würdet (womöglich, ohne mein Ego zu vernichten 🙂



  • typedef char *(*Dptr)[2];
    
    int main()
    {
      int i,j;
      char *achr[][2]={{"a","b"},{"a1","b2"},{"a3","b4"},{"a5","b6"},};
      Dptr p = achr;
      for(i=0;i<sizeof achr/sizeof*achr;++i)
        for(j=0;j<2;++j)
          puts(p[i][j]);
      return 0;
    }
    


  • Es gibt keine direkte Möglichkeit, dem Compiler zu sagen "gib mir den zweiten Member der Struktur". Zur Laufzeit sind Informationen über das Typsystem generell nur noch implizit vorhanden.

    Man kann sich so etwas aber durchaus selbst basteln, indem man sich die notwendigen Informationen merkt - es erfordert nur eine gewisse Vorsicht und etwas Extra-Code für jede Struktur, die so adressierbar sein soll. Im konkreten Fall könnte man das beispielsweise so aufziehen:

    #include <stddef.h>
    #include <stdio.h>
    
    struct A {
      char name[15];
      char ort[20];
      char beruf[50];
    };
    
    size_t const A_member_offsets[] = {
      offsetof(struct A, name),
      offsetof(struct A, ort),
      offsetof(struct A, beruf)
    };
    
    #define ARRAY_SIZE(arr) (sizeof(arr) / sizeof(*arr))
    #define MEMBER_NO(type, mem_type, object, index) ((mem_type)((char *) &(object) + type ## _member_offsets[index]))
    #define MEMBER_COUNT(type) ARRAY_SIZE(type ## _member_offsets)
    
    int main(void) {
      struct A a = { "Meier", "Berlin", "Zimmermann" };
      size_t i;
    
      for(i = 0; i < MEMBER_COUNT(A); ++i) {
        puts(MEMBER_NO(A, char*, a, i));
      }
    
      return 0;
    }
    

    Die Funktionsweise ist wie folgt: offsetof gibt die Entfernung in Byte des betreffenden Members vom Anfang der Struktur zurück. Im Makro MEMBER_NO wird diese Anzahl auf die Adresse der übergebenen Variable aufgeschlagen und das Ergebnis in den angegebenen Typen gecastet (hier char*).

    Es muss gesagt werden, dass diese Art von Konstruktion sehr fragil ist. Wenn nicht alle Argumente zu MEMBER_NO genau zusammenpassen, kann der Compiler dich darüber nicht informieren - die notwendigen Typinformationen haben wir ihm gerade weggecastet. Dementsprechend hast du selbst dafür Sorge zu tragen, dass zur Laufzeit alles da ist, wo es sein soll (insbesondere der Index nur solche Member abfragt, die zum angegebenen Typ passen), und wenn du dabei einen Fehler machst, darfst du dich auf langwierige und sehr frustrierende Debugging-Sessions mit merkwürdigen Heisenbugs einstellen. Sofern du nicht sehr gute Gründe hast (und "es ist weniger Tipparbeit" zählt nicht), kann ich dir nur davon abraten.



  • Was ist denn das eigentliche Problem? Ich könnte mir vorstellen, dass man da auch etwas findet ohne sich durch Knie, Brust und Enddarm ins Auge zu schiessen.



  • erstmal vielen Dank an Seldon und Wutz für die Mühe - sobald ich mir einen GROSSEN Kaffee geholt habe, werde ich Eure Tipps zu verstehen versuchen 🙂

    Problem gibt es an sich keines, mein Programm tut auch so, was es tun soll.
    Im Endeffekt lese ich aus einer CSV-Datei Daten aus (mit einer eigenen, verdammt unperformant programmierten strtok, da ich anscheinend zu blöd bin, mit sscanf an alle values ranzukommen ( sscanf (zeile, "%[;];%[;];......", struktur[0].member1, struktur[0].member2); -> irgendwo bei ~ member14 will sscanf einfach nicht mehr, obwohl syntaktisch soweit plausibel).

    Dieses Datenauslesen wollte ich etwas beschleunigen und besser lesbar machen, da ich im Moment jedes member einzeln raus"splitte". Eine schöne for-Schleife hätte mir am besten gefallen, aber mir fehlt eben das nötige Wissen bzw. der Ansatz.



  • strtok ist nichts für Anfänger.
    Gehe davon aus, dass sscanf richtig arbeitet, der Fehler wird in deinem Code liegen.
    Wenn du durchnummerieren willst, geht das nur, falls alle Strukturelemente gleichen Typs sind und dann ist deine Struktur quasi ein Array.
    Du kannst z.B. erstmal alles als String einlesen und die Interpretation in andere Formate anschließend (ausserhalb der Dateiaktion) machen.



  • Nunja, mein strtok entspricht ja auch nicht dem ANSI C-eigenen strtok...
    Ich erwarte einen String, der aus mehreren Teilen besteht, die von einem bekannten char getrennt werden (das ist auch der letzte char vor einem etwaigen Newline bzw. string delimiter) und einen Integer, der für das "n-te" Element dieser Auflistung steht (1. Element = alles von &string[0] bis ein ';' auftaucht).

    Ist also nicht großartig verwandt mit dem, was man als strtok kennt.
    Ansonsten habe ich mich jetzt entschieden, das ganze Zerlegen einfach auszulagern und habe die Funktion an die bekannte Struktur angepasst.

    Mein Fehler beim sscanf liegt wahrscheinlich in der Vorverarbeitung des Strings. Besonders intelligenterweise hatte ich z.B. etwaiges "Plenken" bei der Dateneingabe (' ' vor oder nach ';') mit '\0' ersetzt. Meinem eigenen strtok ist das Wurst, sscanf nicht unbedingt 😉



  • Das seh ich genau andersrum. strtok ist ziemlich simpel, mit sscanf muss man höllisch aufpassen, sich keine Buffer-Overflows einzuhandeln. Das einzige, was man bei strtok halt wissen muss, ist, dass man keine eigenständigen Strings zurückkriegt sondern Zeiger in den beim ersten Aufruf übergebenen Buffer - damit man nicht auf die Idee kommt, über den Rückgabewert von strtok Zeiger in einen lokalen Buffer aus einer Funktion zurückzugeben oder so.

    Da im konkreten Fall aber sowieso eine Längenbeschränkung vorliegt, ist die Verwendung von sscanf wohl vertretbar, sofern man die Länge der Eingabeargumente entsprechend beschränkt. Beispiel:

    #include <stdio.h>
    
    struct adresse {
      char name[15];
      char vorname[15];
      char strasse[30];
      char plz[10];
      char ort[20];
    };
    
    int main(void) {
      char zeile[] = { "Mustermann;Max;Musterstraße 12;12345;Musterhausen" };
      struct adresse adr;
    
      if(5 == sscanf(zeile,
                     "%14[^;];%14[^;];%29[^;];%9[^;];%19[^;]", /* <-- Längenbeschränkungen! */
                     adr.name,
                     adr.vorname,
                     adr.strasse,
                     adr.plz,
                     adr.ort)) {
        printf("%s %s\n%s\n%s %s\n", adr.vorname, adr.name, adr.strasse, adr.plz, adr.ort);
      }
    
      return 0;
    }
    

    Ein Nachteil dabei ist natürlich, dass jedes mal, wenn du die Bufferlängen im Struct änderst, auch der einlesende Code verändert werden muss. Man kann sich da prinzipiell mit Makrokonstruktionen behelfen, aber besonders hübsch ist das dann auch nicht.


Anmelden zum Antworten