C - 链接列表中的节点以某种方式被修改,导致分段错误

时间:2015-07-12 17:16:29

标签: c pointers linked-list

我正在学习C中的链表和指针,并且我正在尝试实现一个简单的程序,允许用户在(单个)链接的整数列表中插入,删除或搜索整数。当我尝试插入一个数字时,我确信我的插入功能正常工作。

但是,我有一个函数将列表打印到终端,并且在执行该函数期间,插入的节点以某种方式被修改,以便它的整数(' n')变成一些垃圾价值,及其下一个'指针被改变,以便它指向自己!打印功能然后继续打印此节点的' n'价值因为它的下一个'指针始终指向自身,但是' n'值不断变化,在第三次迭代中,我得到一个段错误(参见底部的gdb会话)。

以下是我对节点的定义:

typedef struct node
{
    int n;
    struct node* next;
} node;

这是我的主要功能:

int main(void)
{
    // declare a linked list
    node* first = NULL;

    int command;
    int n;

    while (true)
    {
        printf("MENU\n\n");

        printf("1 - delete\n");
        printf("2 - insert\n");
        printf("3 - search\n");
        printf("0 - quit\n\n");

        printf("Enter a command: ");
        scanf("%i", &command);

        switch(command)
        {
            case 0:
                printf("Hope u had fun (:\n");
                return 0;
            case 1:
                printf("Number to delete: ");
                scanf("%i", &n);
                deleteNode(n, &first);
                break;
            case 2:
                printf("Number to insert: ");
                scanf("%i", &n);
                insertNode(n, &first);
                break;
            case 3:
                printf("Number to search for: ");
                scanf("%i", &n);
                if (searchList(n, first))
                {
                    printf("Found %i in list!\n", n);
                }
                else
                {
                    printf("Did not find %i in list :(\n", n);
                }
        }

        printList(first);
    }
}

这是我的insertNode()函数:

void insertNode(int n, node** first)
{
    // declare our new node
    node myNode;
    myNode.n = n;
    myNode.next = NULL;

    // initialize curNode to a pointer to the first node in the list
    node* curNode = *first;

    // initialize a pointer that will point to the previous node in the list if we need it
    node* prevNode = NULL;

    while (curNode != NULL)
    {
        if (n <= curNode->n)
        {
            // if prevNode is null, there's one element in the list
            // and we're inserting before it (i.e. at first position)
            if (prevNode == NULL)
            {
                *first      = &myNode;
                myNode.next = curNode;
                return;
            }
            // else, we're inserting between prevNode and curNode
            else
            {
                prevNode->next = &myNode;
                myNode.next    = curNode;
                return;
            }
        }
        // if n > curNode->n, move on to next node
        else
        {
            curNode  = curNode->next;
            prevNode = curNode;
        }
    }

    // curNode is null down here, so we're either at the end of the list, or the list is empty
    if (prevNode == NULL)
    {
        // empty list, only have to update first
        *first = &myNode;
    }
    else
    {
        // end of the list, only have to update previous node
        prevNode->next = &myNode;
    }
}

这是我的printList()函数:

void printList(node* ptr)
{
    printf("\nLIST IS NOW: ");

    while (ptr != NULL)
    {
        printf("%i ", ptr->n);
        ptr = ptr->next;
    }
    printf("\n\n");
}

这是一个说明错误的gdb会话:

35              printf("Enter a command: ");
(gdb)
Enter a command: 36             scanf("%i", &command);
(gdb)
2
38              switch(command)
(gdb) n
49                      printf("Number to insert: ");
(gdb)
Number to insert: 50                    scanf("%i", &n);
(gdb)
1
51                      insertNode(n, &first);
(gdb) s
insertNode (n=1, first=0x22fe48) at linked_list.c:78
78          myNode.n = n;
(gdb) n
79          myNode.next = NULL;
(gdb)
82          node* curNode = *first;
(gdb) p &myNode
$1 = (node *) 0x22fdf0
(gdb) n
85          node* prevNode = NULL;
(gdb)
87          while (curNode != NULL)
(gdb) p *first
$2 = (node *) 0x0
(gdb) p curNode
$3 = (node *) 0x0
(gdb) n
116         if (prevNode == NULL)
(gdb)
119             *first = &myNode;
(gdb)
126     }
(gdb) p *first
$4 = (node *) 0x22fdf0
(gdb) n
main () at linked_list.c:52
52                      break;
(gdb)
66              printList(first);
(gdb) p first
$5 = (node *) 0x22fdf0
(gdb) p *first
$6 = {n = 1, next = 0x0}
(gdb) s
printList (ptr=0x22fdf0) at linked_list.c:200
200         printf("\nLIST IS NOW: ");
(gdb) p ptr
$7 = (node *) 0x22fdf0
(gdb) p *ptr
$8 = {n = 1, next = 0x0}
(gdb) n

LIST IS NOW: 202            while (ptr != NULL)
(gdb) p ptr
$9 = (node *) 0x22fdf0
(gdb) p *ptr
$10 = {n = 4210908, next = 0x22fdf0}
(gdb) n
204             printf("%i ", ptr->n);
(gdb)
4210908 205             ptr = ptr->next;
(gdb)
202         while (ptr != NULL)
(gdb)
204             printf("%i ", ptr->n);
(gdb)
1397312522 205          ptr = ptr->next;
(gdb)
202         while (ptr != NULL)
(gdb)
204             printf("%i ", ptr->n);
(gdb)

Program received signal SIGSEGV, Segmentation fault.
0x0000000000401864 in printList (ptr=0x2500203a574f4e20) at linked_list.c:204
204             printf("%i ", ptr->n);

正如您在上面所看到的,节点在printList()函数的中间字面上发生了变化。怎么/为什么会发生这种情况?

4 个答案:

答案 0 :(得分:1)

如果不查看您编写的每一行,我注意到您没有为列表动态分配内存。 在insertNode函数中,您定义了一个元素,它将驻留在堆栈中:

node myNode;

如果保留该功能,则内存“消失”。这意味着您无权访问它。但是你用

将它传回主要的上下文
*first      = &myNode;

该函数必须自己分配内存(例如使用malloc)。 为了使函数更简单,不要将双指针传递给此函数。

相反,delteNode函数必须将内存返回给操作系统(例如,使用free)。同样在这里:不要传递指针的地址,而只传递应该删除元素的位置(指针)。

答案 1 :(得分:0)

节点myNode;在insertnode中可以反复指向相同的内存尝试使用malloc代替,以便插入节点的每个实例都保证是唯一的内存位置。

答案 2 :(得分:0)

函数insertNode至少是错误的,因为它使用指向局部变量myNode的指针将它包含在列表中,尽管在退出函数后该局部变量将被销毁并且指针无效。

这是函数有未定义的行为。

此外它太复杂了。

可以通过以下方式编写。考虑到第一个参数应该是&#34; list&#34;并且添加的数字应该是第二个参数。否则这是一种糟糕的编程风格。

void insertNode( node** first, int n )
{
    node *tmp = malloc( sizeof( node ) );
    tmp->n = n;

    if ( *first == NULL || n < ( *first )-> n )
    {
        tmp->next = *first;
        *first = tmp;
    }
    else
    {
        node *current = *first;
        while ( current->next != NULL && !( n < current->n ) ) current = current->next;

        tmp->next = current->next;
        current->next = tmp;
    }
}

答案 3 :(得分:0)

在insertNode()函数中,您正在使用局部变量的地址。

//声明我们的新节点

节点myNode;

if(prevNode == NULL)

{

    // empty list, only have to update first
    first = &myNode;  // Trying to assign address of a local variable 

}

记忆&#34; myNode&#34;一旦函数结束就会被释放。不要这样做。

相反,请尝试为myNode动态分配内存。

node * myNode = NULL;

myNode =(node *)malloc(sizeof(node));

myNode-&gt; n = n;

myNode-&gt; next = NULL;

分配myNode时

* first = myNode;

局部变量的内存在堆栈上分配,它的生命周期仅在函数执行之前。

当您尝试获取存储在那里的值时,您很可能会遇到分段错误。