Anfängerfragen zu `typedef struct` (ist Fork aus "Gemeinsame nette double linked list...")


  • Mod

    @Leon0402 sagte in Sollen wir gemeinsam eine nette Double-linked- oder vielleicht sogar Skip List in C schreiben?:

    Ah gut zu wissen 🙂

    Und bei C++ ist das nicht nötig? Da kann ich einfach das Ding direkt so bennen wir ich möchte und muss kein struct dazu schreiben?

    Ich bin mir nicht sicher, ob du das richtige meinst.

    C:

    struct MeinCStruct { };
    
    int main()
    {
        struct MeinCStruct mcs1;  // korrekt
        MeinCStruct mcs2;  // falsch
    }
    

    Weil das nervt, macht man

    typedef struct MeinCStruct { } MeinCStruct;
    
    int main()
    {
        MeinCStruct mcs1;  // Geht jetzt korrekt
        struct MeinCStruct mcs2;  // geht auch noch, aber warum sollte man?
    }
    

    In C++ geht das direkt ohne typedef:

    struct MeinCppStruct { };
    
    int main()
    {
        MeinCppStruct mcs1;  // korrekt
        struct MeinCppStruct mcs2;  // Geht aus Kompatibilitätsgünden auch, ist aber sehr unüblich
    }
    

    Da das typedef einen Alias einführt (in meinem Beispiel den Namen MeinCStruct anstelle von struct MeinCStruct) braucht der Aliasname auch nicht zwingend etwas mit dem Structnamen zu tun haben, siehe bei Swordfish:

    typedef struct list_tag {
        node_t  *head;
        node_t  *tail;
        size_t   length;
    } list_t;
    

    Es ist nur recht unüblich, unterschiedliche Namen zu wählen, weil es dann einfacher nachvollziehbar ist.

    Es ist auch nicht ganz unüblich (und erlaubt) den Namen des structs gleich ganz weg zu lassen:

    typedef struct {  // Erlaubt!
        node_t  *head;
        node_t  *tail;
        size_t   length;
    } list_t;
    


  • Ja ich denke das meinte ich 🙂

    Und wie sieht es aus, wenn man das struct selbst wieder im struct nutzt wie im Code von Fish

    typedef struct node_tag node_t;
    
    struct node_tag {
        void    *data;
        node_t  *next;
        node_t  *prev;
    };
    

    darf ich hier das ganze auch einfach so machen:

    typedef struct node_tag {
        void    *data;
        node_tag  *next;
        node_tag  *prev;
    } node_t;
    

    Oder müsste ich hier vor das node_tag (in C) ebenfalls dann ein struct schreiben?

    Vom Namen her finde ich es interessant, dass man "tag" nutzt, ich hätte einfach "node" und "node_t" genuzt.


  • Mod

    Nein, die Idee ist doppelt falsch, und ich denke anhand der frage, du hast das mit dem typedef immer noch nicht verstanden.

    Daher nochmal:
    In C:

    struct Irgendwas { };
    

    Es gibt jetzt keinen Typ mit Namen Irgendwas. Es gibt nur den Typ struct Irgendwas und das "struct" ist essentieller Bestandteil des Namens, der nicht weg gelassen werden darf. Der Typ hat halt einfach einen Namen, der aus zwei Worten besteht. Denk irgendwie analog zu unsigned long int. Typen dürfen halt auch aus mehreren Worten bestehen und machmal tun sie das auch.

    Aber genauso wie du schreiben dürftest

    typedef unsigned long int my_int;
    

    darfst du auch schreiben

    typedef struct Irgendwas IrgendwasAnderes;
    

    Da, wie gesagt, der Name Irgendwas hier noch nicht belegt ist (es gibt nur struct Irgendwas) ist dies hier auch völlig regulär:

    typedef struct Irgendwas Irgendwas;
    

    Jetzt gibt es einen Typen mit Namen Irgendwas und das ist ein Alias für den eigentlichen Typen struct Irgendwas.

    Da C viel verschachtelte Syntax erlaubt, darf man typedef und struct-Definition auch alles in einem machen:

    struct Irgendwas
    {
       int i;
    };
    
    typedef struct Irgendwas Irgendwas;
    

    kann man zusammenfassen als

    typedef struct Irgendwas
    {
       int i;
    } Irgendwas;
    

    Und wichtig: Die ganzen Typennamen existieren erst nach dem Semikolon, das ihre Definition abschließt:

    struct Irgendwas
    {
       int i;
    };
    // Name `struct Irgendwas` existiert ab hier
    
    typedef struct Irgendwas Irgendwas;
    // Name `Irgendwas` existiert ab hier
    

    oder mit der Kurzfassung

    typedef struct Irgendwas
    {
       int i;
    } Irgendwas;
    // Die Namen `struct Irgendwas` und `Irgendwas` existieren ab hier
    

    Daher geht deine Idee mehrfach nicht:

    typedef struct node_tag {
        void    *data;
        node_tag  *next;
        node_tag  *prev;
    } node_t;
    
    1. Müsste das in Zeile 3/4 entweder node_t oder struct node_tag heißen. Es gibt nirgendwo einen Typen mit Namen node_tag.
    2. Der Typ struct node_tag bzw. node_t ist erst nach dem Semikolon in Zeile 5 bekannt, und kann vorher nicht benutzt werden. Daher geht das prinzipiell überhaupt gar nicht, und man muss das so machen wie Swordfish. also erst den Namen bekannt machen, und ihn erst dann in einer Definition benutzen.

  • Mod

    Ich habe die Fragen zu typedef struct abgetrennt. Der Zweck des anderen Threads war eine Diskussion darüber, wie man das dortige Problem in C auf höchstem Designniveau anpacken würde, ohne Ablenkung durch Grundsatzfragen.



  • Stimmt, wenn ich drüber nachdenke muss man das ja auch in C++ so machen (also der Punkt 2).

    Meine Denkweise war, dass das struct vlt. eher so wie typename funktioniert. Man braucht es in gewissen Kontexten, weil sonst der Compiler verwirrt ist. Daher dachte ich im struct selbst, weiß der Compiler vlt. das es um ein struct geht 😃
    Aber mal abgesehen davon, dass das Szenario überhaupt keinen Sinn ergibt wegen Punkt 2, scheint das struct ja hier tatsächlich wirklich teil des Namens zu sein.

    Gibt es einen Grund, warum das in C so realisiert wurde?

    Edit: Nochmal nachgedacht, in C++ ist das doch nicht so, oder? Solange ich einen Pointer nutze, sollte das gehen?
    Edit 2: Ich weiß, dass ich hier laufend C und C++ vermische, aber grade um die Unterschiede geht es mir eben 🙂 Lücke im Kategorisierungssytem vom Forum ^^


  • Mod

    Stimmt, das war nicht ganz richtig. Du kannst sehr wohl

    typedef struct node_tag {
        void    *data;
        struct node_tag  *next;
        struct node_tag  *prev;
    } node_t;
    

    schreiben, aber nicht

    typedef struct node_tag {
        void    *data;
        node_t  *next;
       node_t *prev;
    } node_t;
    


  • Ah perfekt, das ergibt Sinn, vielen Dank 🙂



  • @SeppJ sagte in Anfängerfragen zu typedef struct (ist Fork aus "Gemeinsame nette double linked list..."):

    Ich habe die Fragen zu typedef struct abgetrennt. Der Zweck des anderen Threads war eine Diskussion darüber, wie man das dortige Problem in C auf höchstem Designniveau anpacken würde, ohne Ablenkung durch Grundsatzfragen.

    Danke! Und auch danke für die Erklärbärarbeit.

    @Leon0402 Vielleicht hilft auch https://en.cppreference.com/w/c/language/name_space



  • @Swordfish In der Tat sehr hilfreich! Jetzt ist mir auch klar, woher der Zusatz "Tag" kommt.

    Ich finde vor dem Hintergrund meine Annahme, dass:

    typedef struct node_tag {
        void    *data;
        node_t  *next;
        node_t *prev;
    } node_t;
    

    auch gar nicht mal so weit hergeholt. Nach dem Motto: Im Tag Name Space kann ich alles aus diesem Namespace ohne den Zusatz verwenden.

    Aber so wie ich das verstehe, ist der C Namespace nicht das selbe wie ein C++ Namespace, wo quasi alles in dem namespace landet, sondern eben nur der identifier landet in dem Namespace.



  • @Leon0402 sagte in Anfängerfragen zu `typedef struct` (ist Fork aus "Gemeinsame nette double linked list..."):

    Aber so wie ich das verstehe, ist der C Namespace nicht das selbe wie ein C++ Namespace

    Das sind einfach "Kategorien" von Namen. Hat mit namespaces in C++ überhaupt nichts zu tun.


Log in to reply