C 如何在将节点插入单链表时避免分段错误?

C 如何在将节点插入单链表时避免分段错误?,c,data-structures,struct,linked-list,singly-linked-list,C,Data Structures,Struct,Linked List,Singly Linked List,当我在控制台上按顺序插入节点时,就会插入节点 如何确保我考虑了所有边界条件?例如,如果用户输入的位置大于列表的大小,该怎么办?此外,当我尝试在节点后插入时,会出现分段错误,但在节点前插入时效果很好。这里有一张图片可以更好地解释我的问题 此外,当我尝试在节点后插入时,会出现分段错误,但在节点前插入时效果很好 产出2 How many elements? 3 enter the value and the position: 3 2 List is: 3 enter the value and

当我在控制台上按顺序插入节点时,就会插入节点
如何确保我考虑了所有边界条件?例如,如果用户输入的位置大于列表的大小,该怎么办?此外,当我尝试在节点后插入时,会出现分段错误,但在节点前插入时效果很好。这里有一张图片可以更好地解释我的问题

此外,当我尝试在节点后插入时,会出现分段错误,但在节点前插入时效果很好

产出2

How many elements?
3
enter the value and the position: 
3 2
List is: 3 
enter the value and the position: 
4 1
List is: 4 3 
enter the value and the position: 
5 3
List is: 4 3 5 
Linked list is: 
4 3 5 

您正在使插入变得比需要的困难得多。您只需使用两个条件进行迭代(1)
pos
次数或更少,以及(2)下一个指针不为
NULL
。通过使用的地址和指向下一个节点的指针进行迭代,可以大大简化插入过程。使用地址维护当前节点,指针始终指向下一个节点。当您迭代了
pos
次或下一个指针为
NULL
时,插入节点。看

此外,您没有使用来自
insert()
的任何返回,因此您的函数原型应该是
void insert(int x,int pos)
。虽然您应该避免使用指向列表的全局指针,但在这个有限的示例中,这很好。知道您的列表通常应该在需要的范围内声明,并且指向列表开头的指针(或指向指针的指针)应该作为参数传递,以使列表可供在其上操作的任何函数使用,而不是全局的

将这些部分放在一起,您的
insert()
函数将减少为:

void insert (int x, int pos)
{
    struct Node **ppn = &head,                  /* pointer to pointer to node */
                 *pn = head,                    /* pointer to node */
                 *node = malloc (sizeof *node); /* allocate for new node */

    if (!node) {                                /* validate allocation */
        perror ("malloc-node");
        exit (EXIT_FAILURE);
    }
    node->data = x;                             /* initialize members values */
    node->next = NULL;

    while (pos-- && pn) {   /* iterate pos times && while pn != NULL */
        ppn = &pn->next;
        pn = pn->next;
    }

    node->next = pn;    /* set next to pointer to node */
    *ppn = node;        /* set node at address to node */
}
将此添加到示例的其余部分,您的完整示例将是:

#include<stdio.h>
#include<stdlib.h>

struct Node {
    int data;
    struct Node *next;
};

struct Node *head = NULL;

void insert (int x, int pos)
{
    struct Node **ppn = &head,                  /* pointer to pointer to node */
                 *pn = head,                    /* pointer to node */
                 *node = malloc (sizeof *node); /* allocate for new node */

    if (!node) {                                /* validate allocation */
        perror ("malloc-node");
        exit (EXIT_FAILURE);
    }
    node->data = x;                             /* initialize members values */
    node->next = NULL;

    while (pos-- && pn) {   /* iterate pos times && while pn != NULL */
        ppn = &pn->next;
        pn = pn->next;
    }

    node->next = pn;    /* set next to pointer to node */
    *ppn = node;        /* set node at address to node */
}

/** print all nodes in list */
void print (void)
{
    if (!head) {
        puts ("list-empty");
        return;
    }
    for (struct Node *n = head; n; n = n->next)
        printf (" %d", n->data);
    putchar ('\n');
}

/** delete all nodes in list */
void del_list (void)
{
    struct Node *n = head;
    while (n) {
        struct Node *victim = n;
        n = n->next;
        free (victim);
    }
}

int main()
{
    int n,i,x,pos;

    printf ("How many elements?\n");
    if (scanf ("%d",&n) != 1) {
        fputs ("error: invalid integer input.\n", stderr);
        return 1;
    }

    for (i = 0; i < n; i++)
    {
        printf ("enter the value and the position: \n");
        if(scanf("%d %d",&x, &pos) == 2) {
            insert (x, pos);
            fputs ("list is: ", stdout);
            print();
        }
    }

    puts ("\nLinked list is:");
    print();
    del_list();    /* free all memory allocated to list */
}
内存使用/错误检查

在您编写的任何动态分配内存的代码中,对于所分配的任何内存块,您有两个责任:(1)始终保留指向内存块起始地址的指针,以便(2)在不再需要它时可以释放它

必须使用内存错误检查程序,以确保您不会试图访问内存或写入超出/超出分配的块的边界,尝试在未初始化的值上读取或基于条件跳转,最后确认释放所有已分配的内存

对于Linux,
valgrind
是正常的选择。每个平台都有类似的内存检查器。它们都很容易使用,只需运行程序即可

$ valgrind ./bin/llatpos
==16615== Memcheck, a memory error detector
==16615== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==16615== Using Valgrind-3.13.0 and LibVEX; rerun with -h for copyright info
==16615== Command: ./bin/llatpos
==16615==
How many elements?
3
enter the value and the position:
3 2
list is:  3
enter the value and the position:
4 3
list is:  3 4
enter the value and the position:
1 0
list is:  1 3 4

Linked list is:
 1 3 4
==16615==
==16615== HEAP SUMMARY:
==16615==     in use at exit: 0 bytes in 0 blocks
==16615==   total heap usage: 5 allocs, 5 frees, 2,096 bytes allocated
==16615==
==16615== All heap blocks were freed -- no leaks are possible
==16615==
==16615== For counts of detected and suppressed errors, rerun with: -v
==16615== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
始终确认已释放所有已分配的内存,并且没有内存错误

仔细检查一下,如果您还有其他问题,请告诉我。

对于初学者,C中的索引(例如数组)从0开始

因此,您还应该使用列表中从0开始的位置的值

从逻辑上讲,如果给定位置大于或等于列表中的节点数,则应将新值附加到列表中

在函数
insert
中,您没有使用列表的大小。所以这个代码片段

    int len = 0;
    struct Node *temp = head;
    while(temp!=NULL)
    {
        ++len;
        temp = temp->next;
    }
        for(int i = 2; i<pos; i++)
        {
            temp1 = temp1->next;
        }
没有道理

如果位置大于列表的大小,则此代码段

    int len = 0;
    struct Node *temp = head;
    while(temp!=NULL)
    {
        ++len;
        temp = temp->next;
    }
        for(int i = 2; i<pos; i++)
        {
            temp1 = temp1->next;
        }
程序输出为

Linked list is: 3 -> NULL
Linked list is: 1 -> 3 -> NULL
Linked list is: 1 -> 2 -> 3 -> NULL
Linked list is: 1 -> 2 -> 3 -> 4 -> NULL
Linked list is: 1 -> 2 -> 3 -> 4 -> 5 -> NULL

请不要发布文本图片。请尝试在所有位置添加空检查…@n.“代词m。”。编辑。@dan1st我不确定在哪里可以添加更多检查。未检查头指针是否为空或不足以执行插入操作?。我在这里的产出似乎自相矛盾。我可以插入第二、第一和第三个位置(按顺序),但不能插入第二、第三和第一个位置。请解释一下?您应该在每个malloc之后添加空支票,
pos
可能比列表中的要大。非常感谢。解释清楚。解决了问题这比我希望的要复杂一点,因为我是初学者。但是我研究了你的答案,发现它提供了很多信息。谢谢。是的,我花了一段时间才认识到,链接列表中指针的地址如何允许你在该地址更改节点,而上一个地址的链接保持不变(上一个->下一个仍然指向同一个地址,那里只存储了一些新的东西…)。这一切归根结底都取决于你是从一开始就正确地学习它,还是以后不得不与这个概念作斗争。理解指针的地址如何在其值更新时保持不变,是理解指针使用的关键。
#include <stdio.h>
#include <stdlib.h>

struct Node
{
    int data;
    struct Node *next;
};

struct Node *head = NULL;

int insert( int data, size_t pos )
{
    struct Node *temp = malloc( sizeof( struct Node ) );
    int success = temp != NULL;

    if ( success )
    {
        temp->data = data;

        struct Node **current = &head;
        while ( pos-- != 0 && *current != NULL )
        {
            current = &( *current )->next;
        }

        temp->next = *current;
        *current = temp;
    }

    return success;
}

void print()
{
    for ( const struct Node *current = head; current != NULL; current = current->next )
    {
        printf( "%d -> ", current->data );
    }

    puts( "NULL" );
}

int main(void) 
{
/*  
    insert( 3, 2 );
    printf( "Linked list is: " );
    print();

    insert( 4, 3 );
    printf( "Linked list is: " );
    print();
*/

    insert( 3, 3 );
    printf( "Linked list is: " );
    print();

    insert( 1, 0 );
    printf( "Linked list is: " );
    print();

    insert( 2, 1 );
    printf( "Linked list is: " );
    print();

    insert( 4, 3 );
    printf( "Linked list is: " );
    print();

    insert( 5, 10 );
    printf( "Linked list is: " );
    print();

    return 0;
}
Linked list is: 3 -> NULL
Linked list is: 1 -> 3 -> NULL
Linked list is: 1 -> 2 -> 3 -> NULL
Linked list is: 1 -> 2 -> 3 -> 4 -> NULL
Linked list is: 1 -> 2 -> 3 -> 4 -> 5 -> NULL