Assign a pointer to a struct that is a member of a struct of the same type to another pointer to a struct of the same type

后端 未结 2 455
死守一世寂寞
死守一世寂寞 2021-01-24 11:01

This question sounds super confusing even for me, and it may seem obvious or already answered, but I have searched a lot and although I found interesting things, I didn\'t find

相关标签:
2条回答
  • 2021-01-24 11:06

    What should be written

    Suppose the code in the question were written like this:

    typedef struct Node {     // Not Node_struct as in the question!
       char keyLine[100];
       int occurrences;
       struct Node* leftChild;
       struct Node* rightChild;
       struct Node* parent;
    } Node;
    

    Then the name Node would be a synonym (alias) for struct Node. (For any typedef X Y;, Y becomes a synonym for type X — where in your case, X would be struct Node and Y would be Node.)

    The cast in:

    currentNode = (Node *)currentNode->leftChild;
    

    would be unnecessary (but mostly harmless) because it would be a no-op — the types struct Node * and Node * would be two names for the same pointer type. Similarly for:

    coverNode->leftChild = (struct Node *)newNode;
    

    The cast would be unnecessary but (mostly) harmless. There would be a small risk of confusing people with the cast. It is better to avoid casts when possible, and these would be better written without the casts:

    currentNode = currentNode->leftChild;
    coverNode->leftChild = newNode;
    

    What is written

    typedef struct Node_struct {
       char keyLine[100];
       int occurrences;
       struct Node* leftChild;
       struct Node* rightChild;
       struct Node* parent;
    } Node;
    

    Now we have three type names in play: struct Node_struct, struct Node, and Node. In this case, struct Node_struct and Node are synonyms, and struct Node is an incomplete structure type (or, at least, it is not completed by any code in the question). It is wholly unrelated to either struct Node_struct or Node except by the coincidence that it is referenced inside the structure.

    With this notation, the casts are 'necessary' because you're converting between pointers to unrelated types (struct Node * and struct Node_struct *). Fortunately, there are rules that say all structure type pointers are inter-convertible and must have the same size and alignment requirements (C11 §6.2.5 Types ¶28 and §6.3.2.3 Pointers ¶7).

    But you should drop the _struct part of Node_struct to make the rules of the first part of this answer apply. In C, it is (IMO) sensible to use:

    typedef struct SomeTag SomeTag;
    

    so that you can subsequently use SomeTag * etc. The first SomeTag is in the tags name space and does not conflict with the second SomeTag, which is in the ordinary identifiers name space. See C11 §6.2.3 Name spaces of identifiers.

    See also:

    • Which part of the C standard allows this code to compile?
    • Does the C standard consider that there are one or two struct uperms_entry types in this code?
    • C style/C++ correctness — is struct, union, enum tag same as type name bad in any way?
    0 讨论(0)
  • 2021-01-24 11:22

    In c++, when you say struct Node, Node [immediately] becomes a type. So, you could say:

    struct Node {
        char keyLine[100];
        int occurrences;
        Node *leftChild;
        Node *rightChild;
        Node *parent;
    };
    
    struct Tree {
        Node *root;
    };
    
    int
    insertNode(Tree *myTree, Node *newNode)
    {
        Node *currentNode = myTree->root;
        if (caseSenCmpString(newNode->keyLine, currentNode->keyLine) == -1) {
            currentNode = currentNode->leftChild;
        }
    }
    

    But, in c, it is merely in the "tag" namespace and does not define a type. Thus, you want:

    typedef struct Node {
        char keyLine[100];
        int occurrences;
        struct Node *leftChild;
        struct Node *rightChild;
        struct Node *parent;
    } Node;
    
    typedef struct Tree_struct {
        Node *root;
    } Tree;
    
    int
    insertNode(Tree *myTree, Node *newNode)
    {
        Node *currentNode = myTree->root;
        if (caseSenCmpString(newNode->keyLine, currentNode->keyLine) == -1) {
            currentNode = currentNode->leftChild;
        }
    }
    

    As an alternative, you can use a forward declaration:

    // forward declaration
    struct Node;
    typedef struct Node Node;
    
    struct Node {
        char keyLine[100];
        int occurrences;
        Node *leftChild;
        Node *rightChild;
        Node *parent;
    };
    
    typedef struct Tree_struct {
        Node *root;
    } Tree;
    
    int
    insertNode(Tree *myTree, Node *newNode)
    {
        Node *currentNode = myTree->root;
        if (caseSenCmpString(newNode->keyLine, currentNode->keyLine) == -1) {
            currentNode = currentNode->leftChild;
        }
    }
    

    Note that the struct name does not have to match the type name:

    // forward declaration
    struct Node_struct;
    typedef struct Node_struct Node;
    
    struct Node_struct {
        char keyLine[100];
        int occurrences;
        Node *leftChild;
        Node *rightChild;
        Node *parent;
    };
    
    typedef struct Tree_struct {
        Node *root;
    } Tree;
    
    int
    insertNode(Tree *myTree, Node *newNode)
    {
        Node *currentNode = myTree->root;
        if (caseSenCmpString(newNode->keyLine, currentNode->keyLine) == -1) {
            currentNode = currentNode->leftChild;
        }
    }
    

    To allow cross linking of your two structs, we could do:

    // forward declaration
    struct Node_struct;
    typedef struct Node_struct Node;
    
    struct Tree_struct;
    typedef struct Tree_struct Tree;
    
    struct Node_struct {
        char keyLine[100];
        int occurrences;
        Node *leftChild;
        Node *rightChild;
        Node *parent;
        Tree *tree;
    };
    
    struct Tree_struct {
        Node *root;
    };
    
    int
    insertNode(Tree *myTree, Node *newNode)
    {
        Node *currentNode = myTree->root;
        if (caseSenCmpString(newNode->keyLine, currentNode->keyLine) == -1) {
            currentNode = currentNode->leftChild;
        }
    }
    
    0 讨论(0)
提交回复
热议问题