c - 在 C 中迭代链表时空检查行为不正确

标签 c pointers linked-list malloc null-check

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

代码如下:

#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 语言中是否有一些我不知道的机制?

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

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

我确实尝试使用free(current)释放current指针的内存,但结果并没有更好,因为温度然后只保存一个随机数。

最佳答案

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

在此声明中

    current = current->next;

您将指针current->next 的值设置为指针current。所以两个指针具有相同的值。

但在这个 if 语句中

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

仅改变了指针当前。指针“前一个”当前->下一个保持不变。也就是说,局部变量current已更改,但动态分配节点的数据成员next未更改,并且指向具有未初始化数据成员的动态分配节点。

分配内存时的做法是这样的

    // 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->next,然后尝试在循环内将指针设置为NULL,无论如何都会导致内存泄露。您应该重新设计代码的逻辑。

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

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...

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

关于c - 在 C 中迭代链表时空检查行为不正确,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/75476068/

相关文章:

C - 使用 _int16 时出现异常

c - Makefile 无法构建内核模块 "recipe commences before first target"

c - 为什么以下代码中不需要 & 号?

c - 用于从函数返回字符串文字的程序的输出

c - 为什么 while 循环中 '*s-*t"的结果是 "67"?

c - 从头部删除结构(或可能重复?)打印时会产生奇怪的文本

c - 如何读取数组中的字节

c - 将数组称为指针

c - 单链表,如何返回指向新节点的指针

c - 在 C 中的单个链表中搜索