无法从 c 中的函数获得响应

can't get response from a function in c

我正在编写一个程序来创建一个双向链表并从中删除一个具有负值的元素。一切都很好,除了我调用修改函数时的部分以及当我尝试删除它时,程序崩溃了。有什么建议吗?

/*
*Given a doubly linked lists with +ve and -ve key values.
*Write a function to delete all the nodes with negative key values.
*/

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

struct list {
    int data;
    struct list *next;
    struct list *prev;
};
struct list *head = NULL;

struct list* create(int);
void modify(struct list*);

int main(void) {

int n, i, value;
struct list *temp;

printf("Enter the count of node :");
scanf("%d",&n);
for (i = 0; i < n; i ++) {
    printf("Enter the value of node: ");
    scanf("%d",&value);
    create(value);
}
temp = head;
printf("\nDoubly linked list is created and the list is as follows : \n");
while (temp != NULL) {
    printf("%d ",temp -> data);
    temp = temp -> next;
}
    modify(head);
}

struct list* create(int value) {
    struct list *new_node, *temp;
    temp = head;
    new_node = (struct list*)malloc(sizeof(struct list));
    new_node -> data = value;
    new_node -> next = NULL;
    new_node -> prev = NULL;
    if (head == NULL) {
        head = new_node;
    }
    else {
        while (temp -> next != NULL) {
            temp = temp -> next;
        }

        temp -> next = new_node;
        new_node -> prev = temp;
    }
    return head;
}

void modify(struct list *head) {
    struct list *current_node, *prev_node, *next_node, *temp;
    temp = head;
    while (temp -> next != NULL) {
        if (temp -> data < 0) {
            current_node = temp;
            prev_node = temp -> prev;
            next_node = temp -> next;
            prev_node -> next = next_node;
            next_node -> prev = prev_node;
            free(current_node);

        }
    }
    printf("\nThe modified doubly linked list is : \n ");
    temp = head;
    while (temp -> next != NULL) {
        printf("%d",temp -> data);
        temp = temp -> next;
    }
}

create()函数return是一个链表项。所以你必须将 return 值分配给一个项目。另外结构体内部指针的定义也是完全错误的。

struct list {
    int data;
    struct list *next;
    struct list *prev;
};
struct list *head = NULL;
struct list* create(int); //function prototype
void modify(struct list*);//function prototype

int main(void) {

int n, i, value;
struct list *temp;

printf("Enter the number of nodes :");
scanf("%d",&n);
for (i = 0; i < n; i ++) {
    printf("Enter the value of node: ");
    scanf("%d",&value);
    create(value);
}
temp = head;
printf("\nDoubly linked list is created and the list is as follows : \n");
while (temp != NULL) {
    printf("%d ",temp -> data);
    temp = temp -> next;
}
    modify(head);
}

void create(int value) {
    struct list* point = head;
    while(point->next){
      if(point->data != value)
          point = point->next;
      else{
         printf("Data exists\n");
         return NULL;
      }
    }
    struct list* item = (struct list*)malloc(sizeof(struct list));
    item->data = value;
    item->next = NULL;
    item->prev = point;
}

void modify(struct list *head) {
    struct list *current_node, *prev_node, *next_node, *temp;
    temp = head;
    while (temp -> next != NULL) {
        if (temp -> data < 0) {
            temp->prev->next = temp->next;
            temp->next->prev = temp->prev;
            free(temp);
        }
        temp = temp->next;
    }
    printf("\nThe modified doubly linked list is : \n ");
    temp = head;
    while (temp -> next != NULL) {
        printf("%d",temp -> data);
        temp = temp -> next;
    }
}

我希望这对你有用。

你对双向链表的定义没有多大意义。

列表应包含两个指针:指向列表的头节点和尾节点。

所以你需要定义两个结构体。第一个定义节点,第二个定义列表本身。

在这种情况下,您无需遍历整个列表即可将新节点追加到列表的尾部。

名称混乱的函数create基于全局变量head,而函数modify则通过参数获取变量。

这很令人困惑。例如,您不能在一个程序中创建两个列表。

因此,当函数 modify 通过值获取指向头节点的指针时,这意味着它处理指向头节点的指针的副本。因此,函数中指向头节点的指针的任何更改都不会影响指向头节点的原始指针。

函数中的这个循环modify

temp = head;
while (temp -> next != NULL) {

一般可以调用未定义的行为,因为不排除指向头节点的指针可以等于NULL

并且在任何情况下,循环的条件都没有意义,因为在循环中您考虑的不是下一个节点,而是当前节点

while (temp -> next != NULL) {
    if (temp -> data < 0) {

那么问题来了如果temp->next等于NULL但是指针temp指向的当前节点的值为负数是不是说明这个节点不会被移除?

请注意,如果您将正确编写循环条件,则删除节点的数据成员 prev 或删除节点的数据成员 next 甚至两者都可以等于 NULL。在这种情况下,这些语句

        prev_node = temp -> prev;
        next_node = temp -> next;
        prev_node -> next = next_node;
        ^^^^^^^^^^^^^^^^^
        next_node -> prev = prev_node;
        ^^^^^^^^^^^^^^^^^ 

再次可以调用未定义的行为。

这是一个演示程序,展示了如何定义列表及其函数。调查一下。

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

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

struct List
{
    struct Node *head;
    struct Node *tail;
};

int push_back( struct List *list, int data )
{
    struct Node *new_node = malloc( sizeof( struct Node ) );
    int success = new_node != NULL;

    if ( success )
    {
        new_node->data = data;
        new_node->next = NULL;

        if ( list->head == NULL )
        {
            new_node->prev = NULL;
            list->head = list->tail = new_node;
        }
        else
        {
            new_node->prev = list->tail;
            list->tail = list->tail->next = new_node;
        }
    }

    return success;
}

void remove_if( struct List *list, int predicate( int ) )
{
    struct Node *prev = NULL;

    for ( struct Node **current = &list->head; *current != NULL; )
    {
        if ( predicate( ( *current )->data ) )
        {
            struct Node *tmp = *current;

            if ( ( *current )->next != NULL )
            {
                ( *current )->next->prev = ( *current )->prev;
            }

            *current = ( *current )->next;

            free( tmp );
        }
        else
        {
            prev = *current;
            current = &( *current )->next;
        }
    }

    list->tail = prev;
}

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

    puts( "null" );
}

void display_reverse( const struct List *list )
{
    for ( const struct Node *current = list->tail; current != NULL; current = current->prev )
    {
        printf( "%d -> ", current->data );
    }

    puts( "null" );
}

int is_negative( int data )
{
    return data < 0;
}

int main(void) 
{
    struct List list = { .head = NULL, .tail = NULL };
    const size_t N = 10;

    srand( ( unsigned int )time( NULL ) );

    for ( size_t i = 0; i < N; i++ )
    {
        push_back( &list, rand() % N - N / 2 );
    }

    display( &list );
    display_reverse( &list );
    putchar( '\n' );

    remove_if( &list, is_negative );

    display( &list );
    display_reverse( &list );
    putchar( '\n' );

    return 0;
}

程序输出可能看起来像

2 -> 4 -> 3 -> -5 -> 3 -> -3 -> -3 -> -2 -> 0 -> 2 -> null
2 -> 0 -> -2 -> -3 -> -3 -> 3 -> -5 -> 3 -> 4 -> 2 -> null

2 -> 4 -> 3 -> 3 -> 0 -> 2 -> null
2 -> 0 -> 3 -> 3 -> 4 -> 2 -> null

查看来自莫斯科的 Vlad 的示例,以更好地了解您在做什么。

我将检查您的代码并告诉您我要更改的内容。

/*
*Given a doubly linked lists with +ve and -ve key values.
*Write a function to delete all the nodes with negative key values.
*/
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>

首先:您正在制作一个(双向链接的)节点列表,而不是列表列表。称之为节点。另外,你可以做一个typedef来防止你一直写struct Node

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

void append(struct Node** head, int value); // variable names aren't needed here
struct Node* findLastNode(struct Node** head);
void removeNegativeNodes(struct Node** head);
void removeNode(struct Node** head, struct Node* currNode);

int main(void)
{

尽量不要使用全局变量。不使用它们的原因有很多,但在这里也可以不使用它们。想象一下有几千行代码,你将无法对代码有一个像样的看法。

    struct Node* head = NULL;
    struct Node* p; // temp-<p>ointer

    int n, value;
    printf("Enter the count of node :");
    scanf("%d", &n);

您只需要在 for 循环中使用 i,所以请保留它。

    for (int i = 0; i < n; ++i) {
        printf("Enter the value of node: ");
        scanf("%d", &value);

确保您的函数名称清楚,并告诉您它们的作用。 create() 会告诉我它创建了一个节点,但并不是说它也 append 是节点。

        append(&head, value);
    }

    // this can be in a function! (A) printData
    p = head; // temp-<p>ointer
    printf("\nDoubly linked list is created and the list is as follows : \n");
    while (p != NULL) {
        printf("%d <=> ", p->data);
        p = p->next;
    }
    printf("NULL\n");

看看你在做什么:也许你想做一个通用的函数来拆分代码?在这里,您将再次浏览列表并打印出它的数据成员。

    // this can be in a function! (B) printData
    removeNegativeNodes(&head);
    printf("\nThe modified doubly linked list is : \n");
    p = head;
    while (p != NULL) {
        printf("%d <=> ", p->data);
        p = p->next;
    }
    printf("NULL\n");
}

struct Node* findLastNode(struct Node** head)
{
    struct Node* p = *head;
    if (p != NULL)
        while (p->next != NULL)
            p = p->next;
    return p;
}

因为你的头部要改变,所以你也必须传递头部的地址。另外,稍微拆分一下代码,这样您就可以更轻松地了解代码的结构。如果你的函数有 40 条规则,那么(准确)找出 bug 的原因将需要更长的时间。

void append(struct Node** head, int value)
{
    struct Node* lastNode = findLastNode(head);
    struct Node* nextNode = (struct Node*)malloc(sizeof(struct Node));
    if (lastNode != NULL) {
        lastNode->next = nextNode;
        nextNode->prev = lastNode;
    }
    else {
        *head = nextNode;
        nextNode->prev = NULL;
    }
    nextNode->next = NULL;
    nextNode->data = value;
}

这里也是:第一个数字可以是负数,所以确保你可以通过它的地址访问 head 变量。此外,再次保持简单并将代码拆分为函数 removeNegativeNodes > removeNode.

void removeNegativeNodes(struct Node** head)
{
    struct Node* p = *head;
    struct Node* temp;
    while (p != NULL) {
        temp = p->next;
        if (p->data < 0)
            removeNode(head, p);
        p = temp;
    }
}

void removeNode(struct Node** head, struct Node* currNode)
{
    if (currNode->next != NULL)
        currNode->next->prev = currNode->prev;
    if (currNode->prev != NULL)
        currNode->prev->next = currNode->next;
    else
        *head = currNode->next;
    free(currNode);
}

我已经测试了代码,它应该可以工作。让它正常工作并不重要,重要的是了解会发生什么。我建议你仔细看看它。祝你好运!