在 C 中迭代链接列表时,Null-check 行为不正确

Null-check behaves incorrectly when iterating linked-list in C

提问人:Andy Nguyen 提问时间:2/17/2023 最后编辑:chqrlieAndy Nguyen 更新时间:2/19/2023 访问量:54

问:

我正在尝试通过编写一个程序来学习 C 基础知识,该程序提示用户输入整数,然后将值存储在链表中。如果输入等于或低于,则节点设置为 并且提示结束。然后,程序遍历列表并计算输入值的平均值。但是,列表的计数器最终总是比预期多一个元素,因此会导致不正确的平均值,尽管我在添加每个值和增量计数器之前显式指定了检查。-128currentNULLNULL

代码如下:

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

int main()
{
    struct record {
        int temperature;
        struct record *next;
    };
    
    // Keep hold of the first record for iterating --v
    struct record *first = NULL;
    first = (struct record *)malloc(sizeof(struct record));
    
    // Keep hold of the current record used in the while loop --v
    struct record *current = first;
    
    while (true) {
        
        // Get user input --v
        int temp;
        printf("Enter measurement: ");
        scanf("%d", &temp);
        
        // If input is less than -128, end the loop --v
        if (temp <= -128) {
            current = NULL;
            break;
        }
        
        // Record the temperature --v
        current->temperature = temp;
        
        // Allocate memory for the next record --v
        current->next = (struct record *)malloc(sizeof(struct record));
        
        // The next now becomes the current because we are done with the current --v
        current = current->next;
        printf("Next record created!\n");
    }
    
    // Calculate average --v
    current = first;
    double sum = 0.;
    int count = 0;
    // As long as the record is not NULL --v
    while (current != NULL) {
        
        sum += current->temperature;
        count++;
        
        printf("%d %d\n", count, current->temperature);
        
        // Move to the next record --v
        current = current->next;
    }
    
    printf("\nAverage of the list values: %.1f", sum / count);
}

这是正确的行为吗?C 语言中有一些我不知道的机制吗?

我放置了一些调试行,以便我可以跟踪计数器以及相应的记录,并发现该属性似乎不是 NULL,尽管我将其显式设置为 NULL。这是代码的输出:current

Enter measurement: 22
Next record created!
Enter measurement: 22
Next record created!
Enter measurement: 22
Next record created!
Enter measurement: -128
1 22
2 22
3 22
4 0

Average of the list values: 16.5

我确实尝试通过使用来释放指针的内存,但结果并不好,因为当时只保留一个随机数。currentfree(current)temperature

c 指针 linked-list malloc null-check

评论

0赞 Barmar 2/17/2023
您永远不会设置列表的最后一个元素。current->next = NULL

答:

2赞 Vlad from Moscow 2/17/2023 #1

问题在于指针 和 是两个不同的指针,它们占用不同的内存范围。 是局部变量,而是动态分配节点的数据成员。currentcurrent->nextcurrentcurrent->next

在此声明中

    current = current->next;

设置指向指针的指针的值。因此,这两个指针具有相同的值。current->nextcurrent

但是在这个if语句中

    if (temp <= -128) {
        current = NULL;
        break;
    }
    

只有指针发生了变化。指针保持不变。也就是说,局部变量已更改,但动态分配节点的数据成员未更改,并指向具有未初始化数据成员的动态分配节点。current"previous"current->nextcurrentnext

分配内存时的方法如下

    // Allocate memory for the next record --v
    current->next = (struct record*) malloc(sizeof(struct record));
    
    // The next now becomes the current because we are done with the current --v
    current = current->next;

并将其地址分配给指针,然后尝试将指针设置为循环内,在任何情况下都会导致内存泄漏。您应该重新设计代码的逻辑。current->nextNULL

最简单的方法是使用指针到指针,例如

struct record *first = NULL;
struct record **current = &first;

while (true) {
    
    // Get user input --v
    int temp;
    printf("Enter measurement: ");
    scanf("%d", &temp);
    
    // If input is less than -128, end the loop --v
    if (temp <= -128) {
        break;
    }
    
    *current = malloc( sizeof( struct record ) );        

    ( *current )->temperature = temp;
    ( *current )->next = NULL; 

    current = &( *current )->next;  
  
    puts("Next record created!");
}

在 while 循环之外,您需要为辅助指针使用另一个名称。例如

// Calculate average --v
struct record *tmp = first;
// and so on...

请注意这一点 -- 当不再需要列表时,您将需要释放所有分配的内存。

评论

0赞 Andy Nguyen 2/18/2023
在什么情况下,我应该考虑使用指针到指针而不是单个指针?
0赞 Vlad from Moscow 2/18/2023
@AndyNguyen 指针到指针是一种通过引用处理对象的机制,即取消引用指针到指针,您可以访问原始指针并更改它。