将数据从二进制文件读取到 C 中的链表中(访问冲突读取位置)



编辑了代码,现在我在读取文件时遇到了问题。我的结构没有得到值。

感觉我快疯了,我试图找到一个解决方案,大约 8 个小时......所以这是我的结构

typedef struct _megye
{ 
    int megye;
    int hektar1_min;
    int hektar1_max;
    int hektar1_tam;
    int hektar2_min;
    int hektar2_max;
    int hektar2_tam;
    int hektar3_min;
    int hektar3_tam;
    struct _megye *next;
}megye;

好吧,它正试图成为一个链表。以下是我尝试从文件中读取数据并将其放入结构中的方式:

{
    FILE *fb;
    megye*p;
    megye *mhead;
    mhead=(megye*)malloc(sizeof(megye));
    p=mhead;
    fb=fopen("tamogatas.dat", "rb");
    if (fread(p, 3, 7, fb) != 7)
    {
        printf("there was an error");
    }
    else
    {
        p=p->next;
        p=(megye*)malloc(sizeof(megye));
        p->next=NULL;
    }
    fclose(fb);
    return 0;
}

顺便说一下,我的文件看起来像这样:

1 50 100 2 100 200 4 200 6

7行这种。

因此,如果我运行它,则会收到错误"访问违规读取位置"。请帮我做什么,在课堂上我们没有做过这样的事情,但他们仍然想要它。

这里似乎有几个问题。

一:

FILE *fb;
megye*p;
mhead=p=(megye*)malloc(sizeof(megye));
fb=fopen("tamogatas.dat", "rb");
if (fread(p, sizeof(megye), 7, fb) != 7)

我建议您避免使用 a = b = c ...语法。 它可能导致微妙的错误,并且在阅读时很容易错过。 然而,真正的问题是你到底期望面包在这里做什么。 您的数据文件是二进制的吗? 否则,您将无法得到您所期望的。 如果您希望它读取所有 7 个二进制结构体值并将它们存储在仅分配给一个此类结构的内存位置中。 这几乎肯定不是你想要做的。

{
    printf("there was an error");
}
else
{
    p=p->next=(megye*)malloc(sizeof(megye));
    p->next=NULL;
}
fclose(fb);
return 0;

在 else 子句中,您将分配另一个结构位置,并将 p 和 p->next 设置为等于该位置。 然后将 p->next 设置为空。 同样,这几乎肯定不是您想要做的。

要形成链表,您需要一个指向 NULL(空列表(或其他节点的头。 每个节点都有一个指向 NULL(列表末尾(或列表中下一个节点的下一个字段。 应单独分配每个节点,并且每个读取应仅读取该节点(格式正确(。 我猜你的输入文件是文本的,所以你需要将每个数字转换为二进制值。 一种方法是使用标准的 fscanf(...( 函数。

我看到所呈现的代码存在两类主要问题。

指针和列表

此条款

    p=p->next;
    p=(megye*)malloc(sizeof(megye));
    p->next=NULL;

要么导致您的访问冲突,要么将导致进一步的问题。

链表是一系列节点,其中每个节点都指向列表中的下一个节点。实现链表需要了解指针。我上面引用的三行表明了对指针的深刻误解。

指针只是某物的地址。在这里,p是某个节点的地址,p->next是该节点中的一个字段,其中包含NULL或列表中下一个节点的地址。

执行行p = p->next后,您忘记了前一个节点的地址,因此无法再修改其next字段。随后对 p 的赋值仅修改变量p本身,而不修改先前值 pnext字段。

表示添加一个新节点以遵循p当前指向的节点的正确方法是:

    p->next = (megye*)malloc(sizeof(megye));
    p = p->next;
    p->next = NULL;

如果您想对错误检测迂腐,则还要检查malloc()是否实际返回了非 NULL 值。你可以通过使用calloc()而不是malloc()来消除第三行,这也将具有将struct的所有字段初始化为所有0位的优点,这是C标准要求与数值0,0.0或NULL 指针相同的。

文件 IO

您定义了一个显然是链表节点结构的struct megye(它包含一个next字段,该字段是指向该结构的指针(,然后使用一个作为fread()的目标。

fread() 的调用将从文件中读取字节,并将它们放入您请求的缓冲区的内存中。它的参数是要填充的缓冲区的地址、数组元素的大小、元素计数以及要从中读取的FILE *

你写道:

    if (fread(p, 3, 7, fb) != 7)

它要求将 3 字节结构的七个副本读取到 p 指向的内存中,该内存先前已初始化为指向列表头节点的指针。列表节点的大小不是 3 个字节。它有 9 个int字段和一个指针,在常见的 32 位体系结构上,总共可能有 40 个字节。

因此,从文件中读取的 21 个字节(7 个 3 字节元素(不会溢出分配。但是,它也不会与结构的任何部分对齐。

如果你真的有一个带有 3 字节整数的二进制文件,那么你需要引入一些读取文件并转换格式的代码。

即使您打算保存自然大小的整数,将文件格式与内部表示形式分开也是一个好主意。一旦您尝试与其他体系结构系统交换此数据,您将看到好处。不同的机器可以有不同的sizeof(int)值,即使大小相同,不同的平台也以不同的顺序存储int的各个字节。

另一个问题是,由于您提到文件中有七行数据,因此您可能尝试在单个操作中读取整个列表。这不会像您预期的那样工作。即使内存中有一个有效的链表,也不能简单地将其写入文件,因为指针将是在这个特定的程序运行实例中有效的地址,并且无法写入文件并安全地读回。

有关这些文件 I/O 和数据表示问题的详细信息,请查看"序列化"这一广泛主题。这不是一个简单的问题。

出于学习目的,我强烈建议序列化为简单的文本文件,以便您可以在文本编辑器中看到您获得的内容,并手动构建简单的测试用例。

首先,在尝试读取其内容之前,您可能需要小心 fopen 是否通过检查 fb 的值来查看它是否成功打开了您的文件。

然后,据我所知,您只为节点的 1 个分配了大小(sizeof megye(,但通过您的 fread 调用,您尝试在p地址设置 7 * sizeof(megye( 字节。

另一个问题是结构本身,因为您将数据和指向列表下一个节点的指针保留在同一结构中,该结构是从文件中填充的。这意味着,您将从文件中将任意地址设置为下一个。您的文件是否也包含下一个字段的字节?

我建议拆分您的结构。一个结构用于数据,另一个用于列表。然后,仅填充结构的数据部分会容易得多。

最后,如果这有效,则无需强制转换 malloc 结果。隐式强制转换效果很好,可读性更强。

最新更新