0
typedef struct node {
    int x;
    struct node *next;
    struct node **head;
} node;

Considering this struct, I've implemented a push function:

node *push(node *nodo, node *top) {
    nodo->next = top;
    top = nodo;
    nodo->head = ⊤
    return top;
}

so that every node is aware of who is the current head list. But I have some problems when I have to delete the head of the list:

node *delete(node *top, int x) {
    if (top->x == x)
        return pop(top);
    else
        return *other function*;
}

node *pop(node *top) {
    node *tmp = top;
    top = tmp->next;
    free(tmp);
    return top;
}

When printing head content, this will give me segmentation fault:

void print(node *top) {
    node *tmp = top;
    printf("List:\n");
    while (tmp != NULL) {
        printf("%d\n", (*((tmp)->head))->x);
        tmp = tmp->next;
    }
    printf("\nEnd\n");
}
chqrlie
  • 131,814
  • 10
  • 121
  • 189
  • 2
    `nodo->head = ⊤` is wrong. it sets the current node's `head` pointer to the address of a temporary that will expire the moment the function exits, leaving a dangling pointer to nowhere. – WhozCraig Jun 28 '22 at 16:27
  • It just works fine when i print my list after calling push functions. @WhozCraig – FueledByPizza Jun 28 '22 at 16:31
  • Why does `pop` (and sometimes `delete`) return a pointer to a freed node? – Ian Abbott Jun 28 '22 at 16:37
  • 1
    Please [edit] your question and create a [mre]. Please explain in your question why you need a reference to the head in all nodes. `struct node** head;` seems to be the address of a pointer to the head node, so you could simply modify the head pointer. (This is based on guessing because you didn't show how you call your functions. – Bodo Jun 28 '22 at 16:40
  • *"It just works fine"* - So long as you don't actually *use* that `head` member by you're stuffing in every node in any fashion involving a dereference, it certainly can otherwise "work fine" and have no issues. But that `head` member is nonetheless set to what is ultimately a dangling address. If you *ever* dereference it you *will* invoke UB. – WhozCraig Jun 28 '22 at 16:52
  • Right, that was the point, thanks, @WhozCraig! – FueledByPizza Jun 28 '22 at 20:16

1 Answers1

1

But I have some problems when I have to delete the head of the list

You have much worse and more pervasive problems.

Here ...

node* push(node* nodo, node* top){
  nodo->next=top;
  top = nodo;
  nodo->head = ⊤
  return top;
}

You set nodo->head to point to a parameter of the function. The lifetime of that parameter ends when the function returns, at which time the node's head pointer becomes invalid. Undefined behavior results from any subsequent attempt to use the node's head pointer in any way.

I suppose that you have defined node.head as a double pointer so as to be able to change the head node in one place for all nodes. In that case, you need to choose a "one place" that is in fact the same for every node and whose lifetime does not end before that of the overall list does. Having chosen such a location, you would pass it itself to push:

node* push(node* nodo, node** top){
  nodo->next = *top;
  nodo->head = top;
  *top = nodo;
  return *top;
}

Of course, all callers would need to be updated appropriately.

However, if you want to have this kind of association between nodes and the list to which they belong, then you should consider creating a separate data structure for the list itself, and giving the nodes a poiunter to that. That would solve several problems for you.

Example:

typedef struct node{
  int x;
  struct node* next;
  struct list* list;
}node;

struct list {
    node *head;
    // maybe other whole-list information, too, such as the tail node
    // or an element count
};

The struct list objects then provide the place for head node pointers to be recorded, and nodes can access their lists' head node via the list.

John Bollinger
  • 160,171
  • 8
  • 81
  • 157