【问题标题】:Trying to read a file into a linked list but program crashes while reading试图将文件读入链表但程序在读取时崩溃
【发布时间】:2016-06-28 03:34:12
【问题描述】:

我编写了一个程序来将文件中的一组数据读入一个链表。程序从文件中读取一行,标记该行,将其存储到一个节点中,然后移动到下一行。每次执行此操作时,我都会打印节点的内容。我遇到的问题是,当程序运行时,它会在文件中的两行之一停止并崩溃。它比另一条更频繁地停在一条线上。

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

typedef struct prefectNode
{
    char *name; // prefect name
    char *type; // prefect type
    char *used;
    struct free// structure storing the prefect's free sessions on particular days
    {
        char M[9];
        char T[9];
        char W[9];
        char S[9];
        char F[9];
    } fses; // free sessions available to prefect;
    struct prefectNode *nextPtr;// a pointer to the next node
} pNode;// Self-Referential structure used to store information about a prefect
typedef pNode *ptrNode;// Synonym for a pointer to a self-referential structure

void induct(ptrNode *list, char l[]);// induct is called when information is being retrieved from file.

void induct(ptrNode *list, char l[])
{
    ptrNode curPtr = NULL;// pointer to the current node

    ptrNode newPtr = malloc(sizeof(pNode));// creating new node

    if (newPtr != NULL)
    {
        newPtr->name = strtok(l, ",");

        newPtr->type = strtok(NULL, ",");

        char *sesh = strtok(NULL, " ");
        char c = sesh[0];
        printf("%s\n%s\n", newPtr->name, newPtr->type);

        int i = 0, j = 0;
        for (i = 0; i < strlen(sesh); i++)
        {
            char c = sesh[i];
            if (c == 'M')
            {
                newPtr->fses.M[j] = sesh[i + 1];
                printf("M%c", newPtr->fses.M[j]);
            }
            if (c == 'T')
            {
                newPtr->fses.T[j] = sesh[i + 1];
                printf("T%c", newPtr->fses.T[j]);
            }
            if (c == 'W')
            {
                newPtr->fses.W[j] = sesh[i + 1];
                printf("W%c", newPtr->fses.W[j]);
            }
            if (c == 'S')
            {
                newPtr->fses.S[j] = sesh[i + 1];
                printf("S%c", newPtr->fses.S[j]);
            }
            if (c == 'F')
            {
                newPtr->fses.F[j] = sesh[i + 1];
                printf("F%c", newPtr->fses.F[j]);
            }

            i = i + 2;
            j++;
        }

        newPtr->nextPtr = NULL;
        curPtr = *list;

        if (curPtr == NULL)
        {
            newPtr->nextPtr = *list;
            *list = newPtr;
        }
        else
        {
            while (curPtr->nextPtr != NULL)
            {
                curPtr = curPtr->nextPtr;
            }
            curPtr->nextPtr = newPtr;
        }
    }
    else
    {
        printf("no memory");
    }
}

char ln[200];
FILE *fp;
int main()
{
    ptrNode start = NULL;

    fp = fopen("thefile.txt", "r");

    if (fp == NULL)
    {
        printf("File not found.");
    }
    else
    {
        while (!feof(fp))
        {
            fgets(ln, sizeof(ln), fp);
            induct(&start, ln);
            printf("\n\n");
        }
    }
    fclose(fp);
    getch();
    return 0;
}

这是输入文件的样例:

Denelia Alvaranga,R,M7_T4_T5_T6
Sainna Christian,R,M4_M5_M6_T1_T2_T3_F5_F6
Kashielle Clarke,R,F1_T4_W4_F4_T5_W5_S5_W6_S6_M7_W7
Candice Gordon,R,M1_M2_M3_S3_T7
Alphene Groves,R,M2_T5_S7_W3
T-Anna Johnson,R,M7
Vanessa Lewis,R,M1_M2_M3_S3_T4_W4_S4_T5_S5_T6_S6

当我到达第 9 行时,程序崩溃(更频繁):

Rhoni-Ann Parkins,R,W1_S1_W2_S2_W3_S3_M4_T4_S4_F4_M5_T5_F5_M6_T6_F6_T7_F7

或第 37 行:

Ashleigh Graham,N6A 

【问题讨论】:

  • 文件长约 85 行。
  • 那么您的输入文件是什么样的,哪些是问题行??
  • 那么,您是否在调试器中单步执行了代码?
  • 我试过了,但是我的编译器的调试器不能工作,所以我试着不用它
  • 这真是个坏主意。学习如何调试程序与学习语言一样重要。我建议您花一些时间来获得一个正常工作的开发环境。它会让生活更简单。

标签: c list malloc


【解决方案1】:

主要问题

您的一个主要问题是 j 失控 - 它在首次初始化后永远不会归零。 Rhoni-Ann 惹麻烦是因为她有很多空闲时间; Ashleigh 造成麻烦,因为她没有空闲时间。使用 Rhoni-Ann,j 达到 18,但您要索引的数组最多只能达到 9。您需要重新访问整个代码部分。您需要阅读日期字母后面的数字并使用它来代替 j 值。您还应该验证该值是否足够小(9 的值会再次导致溢出;只有 0..8 是可以的),并报告日期字母是否无效。还要注意,分配内存时空间不会归零。不清楚你之后要做什么,但MT 等数组中没有有效的字符串。

修改代码

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

typedef struct prefectNode
{
    char *name; // prefect name
    char *type; // prefect type
    char *used;
    struct free// structure storing the prefect's free sessions on particular days
    {
        char M[9];
        char T[9];
        char W[9];
        char S[9];
        char F[9];
    } fses; // free sessions available to prefect;
    struct prefectNode *nextPtr;// a pointer to the next node
} pNode;// Self-Referential structure used to store information about a prefect
typedef pNode *ptrNode;// Synonym for a pointer to a self-referential structure

void induct(ptrNode *list, char l[]);// induct is called when information is being retrieved from file.

void induct(ptrNode *list, char l[])
{
    ptrNode curPtr = *list; // pointer to the current node
    ptrNode newPtr = calloc(sizeof(*newPtr), 1);// creating new node

    if (newPtr != NULL)
    {
        newPtr->name = strdup(strtok(l, ","));
        newPtr->type = strdup(strtok(NULL, ","));

        printf("Prefect: %s, type %s\n", newPtr->name, newPtr->type);

        char *sesh = strtok(NULL, " ");
        if (sesh == NULL)
        {
            fprintf(stderr, "No free sessions for %s\n", newPtr->name);
            free(newPtr->name);
            free(newPtr->type);
            free(newPtr);
            return;
        }

        int i = 0, j = 0;
        int len = strlen(sesh);
        for (i = 0; i < len; i++)
        {
            char c = sesh[i];
            if (c == 'M')
            {
                newPtr->fses.M[j] = sesh[i + 1];
                printf("M%c", newPtr->fses.M[j]);
            }
            if (c == 'T')
            {
                newPtr->fses.T[j] = sesh[i + 1];
                printf("T%c", newPtr->fses.T[j]);
            }
            if (c == 'W')
            {
                newPtr->fses.W[j] = sesh[i + 1];
                printf("W%c", newPtr->fses.W[j]);
            }
            if (c == 'S')
            {
                newPtr->fses.S[j] = sesh[i + 1];
                printf("S%c", newPtr->fses.S[j]);
            }
            if (c == 'F')
            {
                newPtr->fses.F[j] = sesh[i + 1];
                printf("F%c", newPtr->fses.F[j]);
            }
            printf("(j=%d)\n", j);

            i = i + 2;
            j++;
        }

        newPtr->nextPtr = NULL;
        curPtr = *list;

        if (curPtr == NULL)
        {
            newPtr->nextPtr = *list;
            *list = newPtr;
        }
        else
        {
            while (curPtr->nextPtr != NULL)
            {
                curPtr = curPtr->nextPtr;
            }
            curPtr->nextPtr = newPtr;
        }
    }
    else
    {
        printf("no memory\n");
    }
}

static void free_node(pNode *node)
{
    if (node != 0)
    {
        free(node->name);
        free(node->type);
        free(node);
    }
}

static void free_list(pNode *node)
{
    while (node != 0)
    {
        pNode *next = node->nextPtr;
        free_node(node);
        node = next;
    }
}

static void print_sessions(const char *dow, const char *ses)
{
    printf(" %s:", dow);
    for (int i = 0; i < 9; i++)
    {
        if (ses[i] != '\0')
            printf(" %c", ses[i]);
    }
}

static void print_node(const pNode *node)
{
    printf("%s [%s]", node->name, node->type);
    print_sessions("Mon", node->fses.M);
    print_sessions("Tue", node->fses.T);
    print_sessions("Wed", node->fses.W);
    print_sessions("Thu", node->fses.S);
    print_sessions("Fri", node->fses.F);
    putchar('\n');
}

static void print_list(const char *tag, pNode *list)
{
    printf("%s:\n", tag);
    while (list != NULL)
    {
        print_node(list);
        list = list->nextPtr;
    }
}

int main(void)
{
    FILE *fp;
    char ln[200];
    ptrNode start = NULL;
    const char filename[] = "thefile.txt";

    fp = fopen(filename, "r");

    if (fp == NULL)
    {
        fprintf(stderr, "File %s not found.\n", filename);
    }
    else
    {
        while (fgets(ln, sizeof(ln), fp) != 0)
        {
            size_t len = strlen(ln);
            if (len > 0)
                ln[len-1] = '\0';
            printf("[%s]\n", ln);
            induct(&start, ln);
        }
        fclose(fp);
        print_list("After data read", start);
        free_list(start);
    }

    return 0;
}

valgrind 下运行的示例

==54054== Memcheck, a memory error detector
==54054== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==54054== Using Valgrind-3.12.0.SVN and LibVEX; rerun with -h for copyright info
==54054== Command: xyz
==54054== 
[Denelia Alvaranga,R,M7_T4_T5_T6]
Prefect: Denelia Alvaranga, type R
M7(j=0)
T4(j=1)
T5(j=2)
T6(j=3)
[Sainna Christian,R,M4_M5_M6_T1_T2_T3_F5_F6]
Prefect: Sainna Christian, type R
M4(j=0)
M5(j=1)
M6(j=2)
T1(j=3)
T2(j=4)
T3(j=5)
F5(j=6)
F6(j=7)
[Kashielle Clarke,R,F1_T4_W4_F4_T5_W5_S5_W6_S6_M7_W7]
Prefect: Kashielle Clarke, type R
F1(j=0)
T4(j=1)
W4(j=2)
F4(j=3)
T5(j=4)
W5(j=5)
S5(j=6)
W6(j=7)
S6(j=8)
M7(j=9)
W7(j=10)
[Candice Gordon,R,M1_M2_M3_S3_T7]
Prefect: Candice Gordon, type R
M1(j=0)
M2(j=1)
M3(j=2)
S3(j=3)
T7(j=4)
[Alphene Groves,R,M2_T5_S7_W3]
Prefect: Alphene Groves, type R
M2(j=0)
T5(j=1)
S7(j=2)
W3(j=3)
[T-Anna Johnson,R,M7]
Prefect: T-Anna Johnson, type R
M7(j=0)
[Vanessa Lewis,R,M1_M2_M3_S3_T4_W4_S4_T5_S5_T6_S6]
Prefect: Vanessa Lewis, type R
M1(j=0)
M2(j=1)
M3(j=2)
S3(j=3)
T4(j=4)
W4(j=5)
S4(j=6)
T5(j=7)
S5(j=8)
T6(j=9)
S6(j=10)
[Rhoni-Ann Parkins,R,W1_S1_W2_S2_W3_S3_M4_T4_S4_F4_M5_T5_F5_M6_T6_F6_T7_F7]
Prefect: Rhoni-Ann Parkins, type R
W1(j=0)
S1(j=1)
W2(j=2)
S2(j=3)
W3(j=4)
S3(j=5)
M4(j=6)
T4(j=7)
S4(j=8)
F4(j=9)
M5(j=10)
T5(j=11)
F5(j=12)
M6(j=13)
T6(j=14)
F6(j=15)
T7(j=16)
F7(j=17)
[Ashleigh Graham,N6A ]
Prefect: Ashleigh Graham, type N6A 
No free sessions for Ashleigh Graham
After data read:
Denelia Alvaranga [R] Mon: 7 Tue: 4 5 6 Wed: Thu: Fri:
Sainna Christian [R] Mon: 4 5 6 Tue: 1 2 3 Wed: Thu: Fri: 5 6
Kashielle Clarke [R] Mon: Tue: 7 4 5 Wed: 4 5 6 Thu: 7 5 6 Fri: 1 4
Candice Gordon [R] Mon: 1 2 3 Tue: 7 Wed: Thu: 3 Fri:
Alphene Groves [R] Mon: 2 Tue: 5 Wed: 3 Thu: 7 Fri:
T-Anna Johnson [R] Mon: 7 Tue: Wed: Thu: Fri:
Vanessa Lewis [R] Mon: 1 2 3 Tue: 4 5 Wed: 6 4 Thu: 3 4 5 Fri: 6
Rhoni-Ann Parkins [R] Mon: 4 Tue: 5 6 4 Wed: 1 5 3 6 7 Thu: 1 2 3 4 Fri:
==54054== 
==54054== HEAP SUMMARY:
==54054==     in use at exit: 26,297 bytes in 186 blocks
==54054==   total heap usage: 297 allocs, 111 frees, 37,503 bytes allocated
==54054== 
==54054== LEAK SUMMARY:
==54054==    definitely lost: 0 bytes in 0 blocks
==54054==    indirectly lost: 0 bytes in 0 blocks
==54054==      possibly lost: 0 bytes in 0 blocks
==54054==    still reachable: 0 bytes in 0 blocks
==54054==         suppressed: 26,297 bytes in 186 blocks
==54054== 
==54054== For counts of detected and suppressed errors, rerun with: -v
==54054== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)

“抑制”内存是在 Mac OS X (10.11.3) 上运行的产物。我正在使用 GCC 5.3.0。代码是干净的——它释放了它分配的所有内存。 (这就是“肯定丢失”、“间接丢失”、“可能丢失”和“仍可到达”的零的含义。)

请注意,尽管使用了valgrind,它并没有抱怨“越界”内存访问,但是您可以看到j 被打印出来并达到了 Rhoni-Ann 的“j=17”。你肯定没有写到你想要的地方——每个级长的输出与你的输入不匹配,因为j 是失控的。碰巧的是,当您在 64 位系统上将 j 设置为 17 写入 newPtr-&gt;fses.F[j] 时,您仍在分配的内存范围内,因为 struct free 占用 45 个字节,但被填充为 48字节对齐 8 字节指针 nextPtr,因此 F[17] 在指针内(您随后会覆盖)。如果您使用 32 位编译器进行编译,情况可能会有所不同。 (我得到了一些“条件跳转或移动取决于未初始化的值”错误,但不是我预期的“超出范围”写入。)

其他问题:

  • 请参阅 [while (!feof(file)) 总是错误],了解您的输入循环有缺陷的原因。
  • 如果您无法打开文件,则不应尝试关闭它。
  • 您应该将错误报告给stderr,而不是stdout
  • 您应该用换行符结束消息,以便它们被看到。
  • fpln 都应该是main 中的局部变量。
  • 您会覆盖您存储的名称,因为您没有复制字符串以包含在结构中 - 它们都指向数组 ln
  • 您没有将used 元素设置为已知值。

【讨论】:

  • 感谢您的回复,特定日期的免费时段数组为 9,因为一天分为 9 个会话。没有人一天有 18 个免费时段,所以我不是这样的。
  • 认为这就是问题所在*
  • 你的变量j 肯定是失控了;请参阅上面的演示。它可能会也可能不会导致您的程序崩溃;在我测试的系统中,写入仍然在数据结构的范围内——只是——因此没有触发来自valgrind 的错误。但是通过在struct free 之前移动nextPtr(并删除used)会得到关于Invalid write of size 1Address 0x100aa8db8 is 0 bytes after a block of size 72 alloc'd 的错误,这是一个越界写入问题。
  • 如果你在处理完数据之后(在将新节点挂钩到列表之前)在induct()函数中使用print_node(),你可以很容易看到为给定prefect生成的数据不一定匹配输入的内容。 Denelia 和 Sainna 的输出恰好看起来“正确”(尽管我认为它只是巧合地正确——修改后的 print_session() 会证明这一点),但 Kashielle 的输出是错误的:[Kashielle Clarke,R,F1_T4_W4_F4_T5_W5_S5_W6_S6_M7_W7] Prefect: Kashielle Clarke, type R … Kashielle Clarke [R] Mon: Tue: 7 4 5 Wed: 4 5 6 Thu: 7 5 6 Fri: 1 4
  • 非常感谢乔纳森。我的代码现在可以工作了,你的回答教会了我很多东西!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-07-01
  • 2020-12-07
  • 2012-10-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多