如何停止创建 linked 列表的循环以再创建一个空列表 link?

how to stop cycle that creates a linked list to create one more empty link?

struct data {
  int date;
  int temperature;
  char day[11];
  struct data *next;
}; 
typedef struct data Data;

int from_file_to_list(File *fp, Data **list) {
  Data *p;
  int cnt = 0;
  *list = p;
  p = malloc(sizeof(Data));
  while (fscanf(fp, "%d" "%s" "%d", &(p->date), p->day, &(p->temperature)) == 3) {
    p->next =  malloc(sizeof(Data));
    p = p->next;
    cnt++;
  }
  return cnt;
}

我有这个函数,它用文本文件的数据创建一个 linked 列表,它 returns 列表中 link 的数量,它工作正常,但它在最后为额外的空 link 分配内存,有什么办法可以阻止它吗?因为如果我有一个在最后添加 link 的函数,可能会有问题吧?

初学者似乎有错字

int from_file_to_list(File *fp, Data **list) {
                      ^^^^

应该有

int from_file_to_list(FILE *fp, Data **list) {
                      ^^^^

I have this function that creates a linked list with the data of a text file and it returns the number of links in the list and it works fine,

你错了。该函数具有未定义的行为。在这个声明中

int from_file_to_list(File *fp, Data **list) {
  Data *p;
  int cnt = 0;
  *list = p;
  //.. 

您将未初始化的指针 p 分配给了指针 *list。也就是说,在退出函数后,main 中的指针 list 将具有这个不确定的值。

例如至少可以通过以下方式定义函数

#include <stdlib.h>
#include <string.h>

//... 

size_t from_file_to_list( FILE *fp, Data **list ) 
{
    size_t cnt = 0;

    int date;
    int temperature;
    char day[11];
    
    while ( fscanf( fp, "%d" "%s" "%d", &date, day, &temperature ) == 3 && 
            ( *list = malloc( sizeof( Data ) ) ) != NULL ) 
    {
        ( *list )->date = date;
        ( *list )->temperature = temperature;
        strcpy( ( *list )->day, day );
        ( *list )->next = NULL;
        
        list = &( *list )->next;
        ++cnt;
    }
    
    return cnt;
}

或者甚至在从文件创建新列表之前释放列表会更好

size_t from_file_to_list( FILE *fp, Data **list ) 
{
    while ( *list )
    {
        Data *tmp = *list;
        *list = ( *list )->next;
        free( tmp );
    }
    
    size_t cnt = 0;

    int date;
    int temperature;
    char day[11];
    
    while ( fscanf( fp, "%d" "%s" "%d", &date, day, &temperature ) == 3 && 
            ( *list = malloc( sizeof( Data ) ) ) != NULL ) 
    {
        ( *list )->date = date;
        ( *list )->temperature = temperature;
        strcpy( ( *list )->day, day );
        ( *list )->next = NULL;
        
        list = &( *list )->next;
        ++cnt;
    }
    
    return cnt;
}

如果您将编写一个单独的函数来清除列表,例如

void clear( Data **list )
{
    while ( *list )
    {
        Data *tmp = *list;
        *list = ( *list )->next;
        free( tmp );
    }
}    

那么上面的函数可以这样写

size_t from_file_to_list( FILE *fp, Data **list ) 
{
    clear ( list );
    
    size_t cnt = 0;

    int date;
    int temperature;
    char day[11];
    
    while ( fscanf( fp, "%d" "%s" "%d", &date, day, &temperature ) == 3 && 
            ( *list = malloc( sizeof( Data ) ) ) != NULL ) 
    {
        ( *list )->date = date;
        ( *list )->temperature = temperature;
        strcpy( ( *list )->day, day );
        ( *list )->next = NULL;
        
        list = &( *list )->next;
        ++cnt;
    }
    
    return cnt;
}

你设置 *list = p; before p 设置,所以你有 UB(未定义的行为)。

修复该问题后,您的代码将无法正确处理空列表。

最好将值读入 temp 变量并仅在存在有效数据行时执行 malloc

不要对 下一个 记录执行 malloc。在文件的最后一行,你有一个 extra struct 分配。

这是一些重构代码。是有注释的。我已经编译但没有测试它:

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

struct data {
    int date;
    int temperature;
    char day[11];
    struct data *next;
};
typedef struct data Data;

int
from_file_to_list(FILE *fp, Data **list)
{
    Data temp;
    Data *prev;
    Data *cur;
    int cnt = 0;

    // make the list empty
    *list = NULL;

    prev = NULL;

    // loop on all lines/records in the file
    while (fscanf(fp, "%d" "%s" "%d",
        &temp.date, temp.day, &temp.temperature) == 3) {

        // allocate new record
        cur = malloc(sizeof(*cur));

        // copy in the data we just read
        *cur = temp;

        // append to non-empty list
        if (prev != NULL)
            prev->next = cur;

        // the first record
        else
            *list = cur;

        // remember the previous record
        prev = cur;

        // advance the count of number of records
        cnt++;
    }

    return cnt;
}