简体   繁体   中英

Define a struct with a member pointing to another member

I'm trying to program a network in C. I have nodes which are linked to each other and I 'd like to do that by making the struct member point to another member (not to another node, because I want to preserve the identity of the links). The code I made to do that is something like:

struct node{
    int k; //number of links
    struct node.link **link; //<- wrong
};

but this is not right as node is not a variable but a type of variable (this is already discussed as an error in another QA: first you have to define a variable of node type and then apply the .link, but this doesn't help here). There's also a QA called "Struct member point at another struct member" but they don't do it from definition and it is not so clear how to generalize it (at least for me).

Is it a correct way to do this?

The problem is that the C language doesn't let you create the type you want. You need a type T with the property *T has the same type as T . You can't do that. (Well, function pointers have that property, but that's an irrelevant technicality.)

You have to introduce a new name. C only lets you do this with structs or similar constructions.

struct link {
    struct link *ptr;
};
struct node {
    int k;
    struct link *link;
};

This will get you what you want. Now, in order to go from a struct link * to a struct node * , you'll have to do some pointer math:

struct node *node_from_link(struct link *link) {
    return (struct node *) ((char *) link - offsetof(struct node, link));
}

This is also provided by the container_of macro, which is not part of the C standard, but you can find a definition for it online.

Or, you could just go the traditional route.

// Usually easier to do it this way...
struct node {
    int k;
    struct node *link;
};

Is this what you are after?

struct Node
{
    int k; //number of links
    void* link; 
};

struct Node* create()
{
    struct Node* node = malloc(sizeof(struct Node));
    node->k = 0;
    node->link = 0;
    return node;
}

void link(struct Node* from, struct Node* to)
{
    from->link = &(to->link);
}

int main()
{
    struct Node* child = create();
    struct Node* parent = create();
    link(parent, child);
    return 0;
}

I've used void* for the link for the reason expressed by Dietrich: you want a pointer to the link to be the same type as the link. This effectively means a cast, so why not just use a generic pointer?

Membership in a structure, generalized or specific, is not an attribute of C data types. There is therefore no way to declare a pointer that can only point to a structure member, and not to any other variable of compatible type.

On the other hand, you don't need to do anything special to declare a pointer that can point to a member of another structure. You just need a pointer to that member's data type, and structure membership is irrelevant to that data type.

For example, you can have

struct node {
    int k; /* number of links */
    struct node **links; /* points to a dynamic array of node pointers */
    struct node **one_link; /* points to a node pointer from another node */
};

In that case, it might make sense to do something like this:

struct node *n1 = /* ... */;
struct node *n2 = /* ... */;

n2->one_link = &(n1->links[3]);

Overall, though, I think this is kind of convoluted. There is probably a better way to structure your data.

Update:

Based on your description of what you're after:

[...] links are bidirectional, if I destroy one link (say the one that links node 1 to node 3) I'll need to destroy the node 1 link AND the corresponding link from node 3. Then I need to know more than just who is link to who. I need to know which link they are using.

there are at least two possible solutions, depending on details of how your nodes are structured. If they are structured like I show above, with an array (dynamic or not) of pointers to other nodes, then your general idea simply won't work. That's because the position of each link within an array of links will change as you delete other links (supposing that you close the gaps). Instead, you can just scan:

struct node {
    int k; /* number of links */
    struct node **links; /* points to a dynamic array of node pointers */
    struct node *parent; /* points to a node that links to this one */
};

void delete_node(struct node *n) {
    if (n->parent) {
        int i;

        for (i = 0; i < n->parent->k; i += 1) {
            if (n->parent->links[i] == n) {
                /* ... delete the ith element of n->parent->links ... */
                break;
            }
        }
    }

    /* ... clean up node n ... */
}

If one node's links to others are stored in separate members, on the other hand, then you could indeed provide a double-pointer by which to remove links from the parent, but the presence of member k in your original structure tells me that's not your situation.

Ok, this is how I finally solved it in my program:

typedef struct node{
int k; //connectivity
struct link **enlace; //vector of LINKs
}NODE;

typedef struct link{
NODE *node1;
NODE *node2;
}LINK;

Basicly, I defined two structures: one is the NODE type, which contains the information of how connected is the node and a vector of LINKs, and the other is the structure LINK which contains the information of the link itself, I mean which nodes the link connects.

With these two I'm able to create the network of nodes with a connectivity following a Poisson distribution, and then destroy each link one by one, choosing one link at random from a list and then redirecting the pointers of each node to NULL.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM