【问题标题】:Program crashes when printing a linked list on screen在屏幕上打印链接列表时程序崩溃
【发布时间】:2015-05-23 14:31:41
【问题描述】:

我不知道为什么我可以在 LABEL 中毫无问题地阅读链接列表:1;但程序只是在 LABEL : 0 中崩溃并打印抓取; 换句话说,为什么链表在讲座功能内部工作正常,但在其外部却不行? 这是我的代码:

/* including libraries */
#define V 20

typedef struct DATA{
    char* NomP;char* NomA;
    struct DATA *Next;
}DATA;

// Prototypes .

int main(void)
{
    char FileName[V];
    puts("Data file ? : ");gets(FileName);

    FILE* fs = fopen(FileName,"r"); // Check if fs is NULL

    DATA *HEAD = MALLOC(sizeof (DATA)); int len = lecture_data(fs,HEAD);
    print_data(HEAD,len); //LABEL : 0
    return 0;
}

int lecture_data(FILE *fs,DATA *ROOT)
{
    char cNom[V],cArticle[V];
    int eofs=0;int i=0;

while(!eofs)
{
    DATA *Data = MALLOC(sizeof (DATA));
    fscanf(fs,"%s %s",cNom,cArticle);
    Data->NomA = MALLOC(strlen(cArticle)+1);
    Data->NomP = MALLOC(strlen(cNom)+1);
    strcpy(Data->NomA,cArticle);
    strcpy(Data->NomP,cNom);
    if( i==0 )
    {
        Data -> Next = NULL ;
        ROOT = Data ;
    }
    else
    {
        DATA* Ptr = ROOT ;
        while( (Ptr->Next) != NULL )
        {
            Ptr = (Ptr -> Next);
        }
        Data -> Next = NULL ;
        Ptr -> Next = Data ;
    }
    i++;
    eofs = feof(fs) ;
    // check ferror(fs) here
}
    puts("Start of reading :");

    print_data(ROOT,len); // LABEL : 1

    puts("End Of Reading ");
    fclose(fs);
    return i;
}

这里是打印功能:

void print_data(DATA *L_ROOT,int len)
{
    int i = 0 ;
    DATA* LINK;
    LINK = L_ROOT;
    while( LINK != NULL )
    {
        printf("%d : DATA->NomA : %s\n",i,LINK->NomA);
        printf("%d : DATA->NomP : %s\n",i,LINK->NomP);
        LINK = LINK -> Next ;
        i++;
    }
}

【问题讨论】:

  • 该代码无法编译。您的 lecture_data 函数使用未定义的变量 (ken)。
  • 请问如何?什么是(ken)?
  • 我认为他试图写 len
  • 哦,我明白了,谢谢;但 len 不是问题..

标签: c linked-list crash


【解决方案1】:

您在主函数中为列表的根分配数据,并将其传递给函数,以便它可以填充列表,但第一次分配元素时,您会覆盖 ROOT 指针值。

这会让你失去函数和外部世界之间的唯一联系(因为返回值只是一个数字),所以 main() 中的 HEAD 值没有指向任何有意义的东西(因为你的函数从不使用它) ,而列表仍然分配在外部没有人指向的某个内存位置,这意味着它丢失了。运行 valgrind 将能够识别这一点。

您可以通过将 (i==0) 大小写从 - 更改来解决此问题

ROOT = Data ;

进入

ROOT->next = Data ;

但请确保稍后忽略根节点的数据。

附言- 使用大写的变量和类型不是一个好主意(它主要是为宏保留的)。它还使您的代码看起来像在大喊大叫:)

【讨论】:

  • 但 ROOT 应该保留 DATA 的地址,我认为 :o ;你能帮我解释一下吗^^'?我是 C 初学者
  • @MichaelHeidelberg,您已经在外部分配了头节点,以保留第一个元素的位置。问题是 - 你从来没有使用你分配的这个地址,你只是用你在函数内进行的第一次分配超出了它 - 跟踪你的分配,你会看到它。如果你想 head 直接指向第一个节点,在 (i==0) 的情况下不要分配任何东西,而是使用你得到的地址作为参数(只需以 if (i==0) Data = ROOT 开头,否则 malloc 它)
  • 我明白了,谢谢你,我以为 HEAD 将是第一个 DATA 的地址。我忘记了 HEAD 的地址仍然在外面。
  • 这在识别和解决OP中的问题方面是正确的,但这不是最好的解决方案。看看我的回答(无论如何+1 :-)
  • @Amit,我同意这是常用的方法,也是我自己会做的,但我不想与他的方法有太大的分歧。 +1 对你也是正确的 :)
【解决方案2】:

(主要)问题是lecture_data 不使用它的输入参数(ROOT)来存储链表,也不返回内部生成的链表。处理这个问题的正确方法是让ROOT 引用调用范围的参数,以便它可以根据需要更新它的引用。

int main(void)
{
    char FileName[V];
    puts("Data file ? : ");gets(FileName);

    FILE* fs = fopen(FileName,"r"); // Check if fs is NULL

    DATA *HEAD = NULL;
    int len = lecture_data(fs, &HEAD);
    print_data(HEAD); //LABEL : 0

    return 0;
}

int lecture_data(FILE *fs,DATA **ROOT)
{
    char cNom[V],cArticle[V];
    int i=0;
    DATA *current = *ROOT; // grab the passed in reference

    while(!feof(fs))
    {
        if(fscanf(fs,"%s %s",cNom,cArticle) <= 0) // This call is only successful if the return value is > 0
        {
            // check ferror(fs) here
            continue; // Can also "break;" here, essentially, it's eof already
        }

        DATA *Data = MALLOC(sizeof (DATA));
        Data->NomA = MALLOC(strlen(cArticle)+1);
        Data->NomP = MALLOC(strlen(cNom)+1);
        strcpy(Data->NomA,cArticle);
        strcpy(Data->NomP,cNom);

        if(NULL == current) // ROOT was uninitialized before the call
        {
            Data -> Next = NULL;
            *ROOT = Data;
        }
        else
        {   // We don't need to iterate the list in every step.
            Data->Next = current->Next; // This part allows the function to insert nodes in the middle / end of an existing list
            current->Next = Data;
            current = Data;
        }
        i++;
    }
    puts("Start of reading :");

    print_data(ROOT); // LABEL : 1

    puts("End Of Reading ");
    fclose(fs);
    return i;
}

注意:print_data 没有对 len 参数做任何事情,所以根本不需要传递它。

该解决方案在列表中的“空”节点方面并不浪费(与忽略空头相反),并且适用于从头开始初始化列表以及需要追加/插入的情况到现有列表中。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-05-04
    • 2015-05-21
    • 1970-01-01
    • 2021-11-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多