【问题标题】:Pushing item to a linked list (C)将项目推送到链表 (C)
【发布时间】:2018-02-07 19:11:46
【问题描述】:

我目前正在尝试将一个结构推送到列表,事情是我必须在不使用标准库的情况下执行此操作(因此没有 malloc 或 calloc 等)。因此我有点挣扎,这就是我到目前为止所得到的。

typedef struct node node_t;
struct node{
    int val;
    node_t *next;
    node_t *prev;
};

node_t nodes[10];
node_t blocked[10];



void init(){
    for(int i = 0; i < 10; i++){
        nodes[i].val = i;
        if(i == 0){
            nodes[i].next = &nodes[i+1];
            nodes[i].prev = NULL;
        }
        else if(i == 9){
            nodes[i].next = NULL;
            nodes[i].prev = &nodes[i-1];
        }else{
            nodes[i].next = &nodes[i+1];
            nodes[i].prev = &nodes[i-1];
        }
    }
}


node_t *push(node_t **arr, node_t node){

node_t *newhead = &node;
printf("%d\n", newhead->val);
newhead->next = (*arr);
printf("%d\n", (*arr)->val);

return newhead; 
}

int main(){


    init();
    node_t *head = &nodes[0];


    node_t *blockhead = &blocked[0];
    blockhead = push(&blockhead,nodes[3]);
    blockhead = push(&blockhead,nodes[4]);


    return 0;
}

我遇到的问题是列表只跟踪我添加到列表中的最后一个元素,如果尝试访问列表中的下一个值,这可能意味着我正在尝试访问 NULL 指针但据我所知,一切似乎都是正确的,但也许我遗漏了一些非常明显的东西?整个“push”函数的要点是简单地将节点添加到列表的前面,然后放在其他所有内容之前。

【问题讨论】:

  • 哦,这只是为了测试。
  • node_t *newhead = &amp;node; 中声明的node 在哪里以及它的存储位置在哪里?

标签: c linked-list double-pointer


【解决方案1】:

您的直接问题是之前没有node 的声明:

node_t *newhead = &node;

您正在获取不存在的地址 - 这绝不是一个好主意(并且会调用 Undefined Behavior 并且通常是 SegFault)。

没有必要将nodes 声明为全局,并且根本不需要blocked(基于您当前的代码)。避免使用全局变量。在main() 中声明您的变量,并根据需要将变量(或指针)传递给任何函数。

如果您使用node_t 类型的数组作为没有分配的链表,然后寻找"push"(例如,在开头添加一个新的node_t 作为新的列表地址),那么您基本上只需要让你newnode.next 指向你的nodes 数组(因为数组的地址只是指向它的第一个成员的指针),你需要array[0].prev 指向newnode 的地址。

根据我从您的问题中收集到的信息,编写代码的目的是进行指针理解练习。无论您是处理存储在堆栈上的列表还是全局分配的列表,如果您更改列表中的第一个节点,则您正在更改列表地址(因为列表的开头是第一个节点的地址)为了在函数中更改列表地址,您必须将列表的 地址 作为参数传递给函数 -- 或者 -- 您返回一个指向新的第一个节点的指针并将地址分配给你的列表指针回到调用者。 (两者都在下面)

因为你想"push"一个本地声明的节点到列表的头部,以同样的方式,你必须将该节点的地址传递给你的推送函数,以便它可以使用如果节点本身作为参数传递给推送函数,则该节点的实际地址而不是创建的节点副本的地址。您可以使用类似于以下内容的方法来执行此操作,以通过推送到列表头部的新节点将您的节点数组作为列表处理:

/* push takes address of array and address of node as parameters */
node_t *push (node_t **arr, node_t *node)
{
    node->next = *arr;              /* next for new node is list address */
    (*arr)[0].prev = node;          /* prev for list is new node address */
    *arr = node;                    /* new node address is new list address */

    return node;                    /* return pointer to new 1st node */
}

上面你只是将newnode-&gt;next设置为当前列表地址,然后将list-&gt;prev设置为指向新节点,然后将列表地址设置为newnode的地址(指向该地址的指针也会返回,可以根据需要在调用者中重新分配)。

其余的更改实际上只是对您所拥有的进行清理,根据需要向您的函数添加参数,以便可以在 main() 中正确声明 nodes 并添加一个 prn 函数来打印列表.节点也没有在代码中使用幻数。如果您需要一个常量 #define 一个(或多个),或者使用全局 enum 来定义它们(这是对全局的有效使用)。

总而言之,您可以执行以下操作:

#include <stdio.h>

#define LMAX 10         /* if you need a constant, define one */

typedef struct node {
    int val;
    struct node *prev, *next;
} node_t;

void init (node_t *nodes)
{
    for (int i = 0; i < LMAX; i++){
        nodes[i].val = i;
        if (i == 0)
            nodes[i].prev = NULL;
        else {
            nodes[i-1].next = &nodes[i];
            nodes[i].prev = &nodes[i-1];
        }
    }
}

/* push takes address of array and address of node as parameters */
node_t *push (node_t **arr, node_t *node)
{
    node->next = *arr;              /* next for new node is list address */
    (*arr)[0].prev = node;          /* prev for list is new node address */
    *arr = node;                    /* new node address is new list address */

    return node;                    /* return pointer to new 1st node */
}

/* simple print list */
void prn (node_t *list)
{
    for (; list; list = list->next)
        printf (" %d", list->val);
    putchar ('\n');
}

int main (void) {

    node_t nodes[LMAX],                 /* array of nodes */
        *head = nodes,                  /* list pointer */
        singlenode = { .val = LMAX };   /* new node to push */

    init(head);                 /* init list  */
    prn (head);                 /* print list */

    push (&head, &singlenode);  /* push new node to list */
    prn (head);                 /* print new list */

    return 0;
}

使用/输出示例

$ ./bin/lldblstatic
 0 1 2 3 4 5 6 7 8 9
 10 0 1 2 3 4 5 6 7 8 9

看东西,注意地址和指针的使用。如果您还有其他问题,请随时提问。

【讨论】:

  • 不错的答案。对于我大学的一个项目,我必须从一开始就编写一个微内核,因为时间限制我无法实现malloc,所以我不得不做类似的事情。但是我使用了双链表和两个指针,一个指向空闲内存,一个指向已使用的内存。这让我想起了那个实现。遗憾的是,时间太长了,我已经忘记了细节,但我知道这让我很头疼,而且很多个不眠之夜。
  • @Pable 是的,这些很有趣。如果我没记错的话,chux 曾经发布过一个静态链表,它使用递归来填充它——它有点像指针诗。我看看能不能找出链接。
  • 是的,虽然很难把它弄对,但它很有趣。我想看看那个 chux 的版本。
  • 好吧,虽然我已经查看了数千个文件,但似乎找不到我要找的那个。可能是因为问题不直接与用作列表的结构数组有关。也许如果@chux 路过他可以回忆起上下文。
猜你喜欢
  • 2013-09-19
  • 1970-01-01
  • 1970-01-01
  • 2019-10-01
  • 2016-06-09
  • 1970-01-01
  • 2013-06-21
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多