c - 如何实现链表删除?

标签 c

#include<stdio.h>

struct node {
    int info;
    struct node *next;
};

typedef struct node *nodeptr;
nodeptr i;
nodeptr q;
nodeptr p;
nodeptr *plist;

nodeptr getnode(void)
{
    nodeptr p;
    p = (nodeptr) malloc(sizeof(struct node));
    return p;
}

void freenode(nodeptr p)
{
    free(p);
}


int main()
{
    int i;
    nodeptr *k;
    int a;
    int *px;
    int r;
    nodeptr end;
    nodeptr s;
    nodeptr start;

    p = getnode();
    q = getnode();
    q = start;
    p = end;

    for (i = 0; i < 6; i++) {
        printf("enter value");
        scanf("%d", &r);
        p = getnode();
        p->info = r;

        q->next = p;

        q = q->next;

    }

    q = start;

    while ((q->next) != NULL) {
        printf("n%d", (q->next)->info);
        q = q->next;
    }

    scanf("%d", &a);
    end = getnode();
    end->info = a;
    end->next = NULL;

    for (q = start; q->next != NULL; q = q->next)
        ;

    q->next = end;
    q = start;

    while ((q->next) != NULL) {
        printf("n%d", (q->next)->info);
        q = q->next;
    }

    for (q = start; q->next->next != NULL; q = q->next)
        ;

    freenode(q->next);
    q->next = NULL;
    q = start;

    while (q->next != NULL) {
        printf("n%d", (q->next)->info);
        q = q->next;
    }


    return 0;
}


在该程序中,将创建一个列表,并在最后插入一个元素
在此元素被删除,但列表未正确显示
仅显示最后两个元素
请提供帮助以显示删除了元素的整个列表

最佳答案

不幸的是,有很多错误。正如WhozCraig所说,关于此主题还有很多其他帖子,因此您可能应该在发布之前搜索更多内容。但是既然有了,让我们一起探讨一些问题。

nodeptr i;
nodeptr q;
nodeptr p;
nodeptr *plist;


在这里,您要声明大量的全局变量,其中大多数带有错误名称。什么是i?什么是p?什么是q?再往下,您重新声明具有相同名称的变量。一些具有相同类型,其他具有不同类型。这使您不清楚要引用的变量。

一般来说,请避免使用全局变量并选择描述性名称。在这种情况下,您可以摆脱ipq

另外,您永远不会将plist初始化为任何东西;您应该养成将变量初始化为合理的默认值的习惯。在这种情况下,NULL是合适的,但是由于您根本不使用该变量,因此可以将其删除。

nodeptr getnode(void)
{
    nodeptr p;
    p = (nodeptr) malloc(sizeof(struct node));
    return p;
}


这很好,但是在C语言中,您不应将malloc的结果强制转换为特定类型,因为这被认为是错误的形式,并且可能导致难以发现的错误。只需直接分配malloc的收益即可。

其次,您永远不要检查以确保malloc成功。当然,在简单的程序中它不太可能失败,但是您应该养成检查可能失败的函数的返回值的习惯。

您可能应该将分配的内存初始化为某个默认值,因为malloc返回给您的内存已满。在这种情况下,看起来像这样的事情是合适的:

if(p) /* only if we allocated memory. */
    memset(p, 0, sizeof(struct node));


有时您可以跳过此操作,但是清除内存是理智的默认做法。

void freenode(nodeptr p)
{
    free(p);
}


这也很好,但是在调用p之前,您应该考虑验证free不是NULL。再次,这归结为健壮性,这是一个养成的好习惯。

int main()
{
    int i;
    nodeptr *k;
    int a;
    int *px;
    int r;
    nodeptr end;
    nodeptr s;
    nodeptr start;


同样,这里有很多单位化变量,但至少其中一些名称更好。但是请注意会发生什么:

您声明类型为i的名为int的变量。但是您已经声明了一个名为i的全局变量,其类型为nodeptr。因此,现在,局部作用域(int)中的变量将隐藏(即隐藏)全局变量。因此,在main中,名称i指的是int。当有人正在阅读您的程序时,这只会增加混乱。

    p = getnode();
    q = getnode();


好的...所以,在这里您分配了两个新节点,并使pq指向这些节点。到目前为止,一切都很好。

    q = start;
    p = end;


糟糕...这是个问题。现在,我们使pq分别指向startend所指向的位置。

这些指向何处?谁知道。 startend都是统一的,因此它们可以指向任何内容。从现在开始,您的程序将显示undefined behavior:这意味着任何事情都可能发生。在这种情况下,很可能会崩溃。

不幸的是,从现在开始,事情变得更加混乱。我不会尝试解释所有内容,而只是给出一些一般性评论。

    for (i = 0; i < 6; i++) {
        printf("enter value");
        scanf("%d", &r);
        p = getnode();
        p->info = r;

        q->next = p;

        q = q->next;    
    }


该循环应读取6个整数并将它们放入我们的链表中。这似乎很简单,但是存在一些问题。

首先,您永远不会检查scanf的返回来知道输入操作是否成功。如前所述,您应该始终检查可能失败的函数的返回值并相应地处理失败。但是在这种情况下,让我们忽略该规则。

一个大问题是q指向内存中的随机位置。因此,我们处于未定义的行为领域。

另一个大问题是要考虑两种情况:当列表为空时(即第一次在i == 0时我们向列表中添加一个数字)和当列表不为空时(即每隔一次)。这两种情况下的行为是不同的。当i == 0时,我们不能盲目地设置q->next,因为即使q没有指向随机位置,从概念上讲,这里也没有q的用法。

这里我们需要一些额外的逻辑:如果这是我们正在创建的第一个节点,请将q设置为指向该节点。否则,在该节点上设置q->next,然后执行q = q->next

另外请注意,您永远不会在任何地方设置p->next,这将导致列表不以NULL终止(您在这里和其他循环中所依赖的东西)。 memset中的getnode修复程序可以解决此问题,但通常应确保如果代码期望特定的行为(“未链接节点的next指针指向NULL;列表以NULL终止”),则应具有代码以确保行为。

    q = start;


同样,在这里,我们将q重置为指向start,而该q->next仍未初始化并且指向垃圾。

    while ((q->next) != NULL) {
        printf("n%d", (q->next)->info);
        q = q->next;
    }


这是一个经典的打印循环。从本质上讲,这里没什么错,尽管我认为从风格上讲,scanf周围的括号是过大的,并且使阅读代码比必须的要困难一些。我的指导原则是仅在需要覆盖C的默认求值顺序时添加括号,或者当括号在视觉上解析代码时帮助读者直观地解释如何将表达式分组时,才添加括号。

    scanf("%d", &a);
    end = getnode();
    end->info = a;
    end->next = NULL;


这很好,除了end->next的错误检查问题外,尽管您不会提示用户输入数字。但是您正确,明确地使NULL指向q很好。

    for (q = start; q->next != NULL; q = q->next)
        ;


同样,这里的问题是start设置为q->next->next,不幸的是,它仍然指向垃圾。

    q->next = end;
    q = start;

    while ((q->next) != NULL) {
        printf("n%d", (q->next)->info);
        q = q->next;
    }


这是您第二次必须键入此代码才能打印列表。通常,您应该避免代码重复。如果发现在多个地方需要特定的代码块,则将其拆分为一个函数并使用该函数是有意义的。这使理解和维护代码更加容易。

    for (q = start; q->next->next != NULL; q = q->next)
        ;


由于q->next位,很难理解此循环。问问自己“如果我正在阅读本文,我是否立即确定q永远不能为NULL?”如果不是,那么您确实应该重写此循环。

    freenode(q->next);
    q->next = NULL;
    q = start;


同样,使start指向统一的。但是,嘿,如果我们还没有崩溃...;)

    while (q->next != NULL) {
        printf("n%d", (q->next)->info);
        q = q->next;
    }


再说一次……这确实应该是一个功能。

    return 0;
}


为了实现更好的实现,我请您参考这里提出的其他许多问题之一(只需搜索“链表删除”即可。KhalidWaseem在该问题中的实现也可能会有所帮助,但记录不多,因此您将获得仔细研究和分析代码以确保您理解它。

关于c - 如何实现链表删除?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/18578716/

相关文章:

c - 谁调用了 atexit()?

c++ - 解码字符数组

c - 使用 write()、read() 和 fifo 在 C 中的进程之间共享数据

c - 通过GDB加载模块

c - C语言删除文件中的一行

将双指针转换为单指针,反之亦然

javascript - : javascript vs C vs Python? 中 if block 中变量的范围是多少

c - popen 创建一个额外的 sh 进程

c - 以相反的顺序打印字符串中的单词 C

c - 可能不一致的类型转换行为