Read/write 来自二进制文件的链接节点

Read/write linked nodes from binary file

我在读取包含链接节点的二进制文件时遇到问题。

这是代码:

lib1.c

struct my_stack_node {
void *data;
struct my_stack_node *next;
};

struct my_stack {
int size;
struct my_stack_node *first;
};

int my_stack_write(struct my_stack *stack, char *filename){
int count = 0;
struct my_stack_node *aux;
FILE *file = fopen(filename, "wb");
if(stack->first != NULL){
    aux = stack->first;
    count++;
    while(aux->next != NULL){
        fwrite(&aux ,sizeof(aux), 1, file);
        aux = aux->next;
        count++;
    }
}
fwrite(&stack, sizeof(stack), 1, file); //Escriure stack
fclose(file);
return count;
}    

struct my_stack *my_stack_read(char *filename){
struct my_stack *stackRead;
struct my_stack_node *stackNode;
FILE *file = fopen(filename, "rb");

if(!file){
    puts("Impossible obrir el fitxer");
    return NULL;
}else{
    int primerInici = 0;

    while(!feof(file)){
        if(primerInici == 0){
            stackRead = (struct my_stack*) malloc(sizeof(struct my_stack));
            fread(stackRead, sizeof(stackRead), 1, file);
            primerInici = 1;
        }else{
            //Crear nou node i llegir-lo del fitxer
            stackNode = (struct my_stack_node*) malloc(sizeof(struct my_stack_node));
            fread(stackNode, sizeof(stackNode), 1, file);
            //Afegir node a la pila
            stackNode->next = stackRead->first;
            stackRead->first = stackNode;
        }
    }
    fclose(file);
    return stackRead;
}

}

main.c

 struct my_data {
   int val;
   char name[60];
 };


int main() {
struct my_stack *s, *t, *u;
struct my_data *data, *data1, *data2;
//...more code
    u = my_stack_read("/tmp/my_stack.data");
if (! u) {
    puts("Error in my_stack_read (u)");
    exit(1);
}

if (my_stack_len(s) != my_stack_len(u)) {
    puts("Stacks s and u don't have the same len");
    exit(1);
}

// Test we can free the data and compare stacks s and u
while((data1 = my_stack_pop(s))) {
    data2 = my_stack_pop(u);
    if (! data2 || data1->val != data2->val || my_strcmp(data1->name, data2->name)) {
        printf("Data in s and u are not the same: %d <> %d\n", data1->val, data2->val);
        exit(1);
    }
    free(data1);
    free(data2);
}
//...more code 
puts("All tests passed");
return 0;

}

执行结果为:

堆栈长度:100

s和u中的数据不一样:22145808 <> 22134800

正确的结果应该是:

所有测试都通过了

问题就出在这里(在my_stack_write内):

aux = stack->first;
count++;
while(aux->next != NULL){
    fwrite(&aux ,sizeof(aux), 1, file);
    aux = aux->next;
    count++;
}

您正在写入指针aux。不是 aux 指向的结构。 data指向的数据都不是,这是重要的部分。

所以。想象一下你有这样的东西:

my_stack  { first=0x100 }
at memoryPosition 0x100 we have : my_stack_node { data=0x200; next=0x300 }
at memoryPosition 0x300 we have : my_stack_node { data=0x500; next=0x600 }
at memoryPosition 0x600 we have : my_stack_node { data=0x700; next=NULL }

对于您的程序正在编写的结构:0x100、0x300
您正在编写构成链表的节点的内存地址。并且您遗漏了最后一个节点,这是另一种错误。
但那是没有用的。下次您 运行 您的程序时,您的节点可能位于不同的内存地址中,因此没有必要保存它们。它是动态内存,每次你 运行 你的程序时它可能驻留在不同的地方。

你应该写的是你的链接列表列出的数据。

几乎整个程序都重复了同样的错误。

如何正确写入链表中包含的数据:

void writeStack(struct my_stack *stack, const char *filename)
{
  struct my_stack_node *aux;
  FILE *file = fopen(filename, "wb");
  if ( file==NULL )
  {
    fprintf( stderr, "Could not open %s for writting.\n", filename );
    exit(1);
  }
  if (stack != NULL)
  {
    aux = stack->first;
    while(aux != NULL)
    {
      //   aux->data is of type void*
      //   Assuming that aux->data contains a struct my_data
      //   Most likely it would be better to redefine data as having
      // type struct my_data*
      fwrite(aux->data ,sizeof(struct my_data), 1, file);
      aux = aux->next;
    }
  }
  fclose(file);
}    

这里我们遍历链表中的所有节点
对于每一个,我们都将数据写入其中。
注意 fwrite( aux->data, 是如何写入 aux->data 指向的数据的,这是正确的。
fwrite( &aux, 会写入包含在 aux 的内存地址,这不太可能是正确的。
fwrite( &aux->data, 会写入包含在 aux->data 的内存地址,这也不太可能是正确的。

计数代码自己写,读函数自己写

您只能读写堆栈本身,而不是其节点的有效负载,它存储在 void * 指针中。

节点本身不携带任何有意义的信息。或者跨会话有意义的信息,而是: datanext 指针仅在写入数据的会话中有效。

你的栈本质上是一个线性数据结构。不是存储节点,而是将堆栈数据存储为 data 成员的数组。当您读入它们时,构建一个包含新分配的节点和读取的 data 字段的列表。

您的堆栈使用 void * 指针来支持各种数据类型。因此,您必须找到一种方法来告诉读取和写入方法应该如何写入或读取数据。

您可以在传递打开的文件时提供回调函数。如果需要,此类回调可以处理复杂的数据结构作为有效负载。

Edit: 下面的代码显示了一个示例,说明如何使用自定义函数序列化堆栈以进行读取和写入。对称回调应该将数据写入文件并读取数据。读取函数可以分配内存,该内存由堆栈拥有。用户必须确保释放它。

回调可以 return 一个负数来指示错误。要读取的堆栈不必为空。读取数据只是压入堆栈。

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

#define die(...) exit((printf(__VA_ARGS__), putchar('\n'), 1));

typedef struct Stack Stack;
typedef struct SNode SNode;

struct SNode {
    void *data;
    SNode *next;
};

struct Stack {
    SNode *head;
};

/*
 *      Core stack functions
 */
void stack_push(Stack *st, void *data)
{
    SNode *sn = malloc(sizeof(*sn));

    sn->data = data;
    sn->next = st->head;
    st->head = sn;
}

void *stack_pop(Stack *st)
{
    void *data;
    SNode *sn;

    if (st->head == NULL) die("Undeflow");

    sn = st->head;
    data = sn->data;
    st->head = sn->next;

    free(sn);
    return data;    
}

int stack_empty(const Stack *st)
{
    return (st->head == NULL);
}

/*
 *      Stack write function with custom callback
 */
int stack_write(const Stack *st, const char *filename,
    int (*func)(FILE *f, const void *data))
{
    const SNode *sn = st->head;
    size_t count = 0;

    FILE *f = fopen(filename, "wb");
    if (f == NULL) return -1;
    fwrite(&count, 1, sizeof(count), f);

    while (sn) {
        if (func(f, sn->data) < 0) {
            fclose(f);
            return -1;
        }
        count++;
        sn = sn->next;
    }

    fseek(f, SEEK_SET, 0);
    fwrite(&count, 1, sizeof(count), f);    
    fclose(f);

    return count;
}

/*
 *      Stack read function with custom callback
 */
int stack_read(Stack *st, const char *filename,
    int (*func)(FILE *f, void **data))
{
    size_t count = 0;
    size_t i;

    FILE *f = fopen(filename, "rb");
    if (f == NULL) return -1;
    fread(&count, 1, sizeof(count), f);

    for (i = 0; i < count; i++) {
        void *p;

        if (func(f, &p) < 0) {
            fclose(f);
            return -1;
        }

        stack_push(st, p);
    }

    fclose(f);
    return count;
}


/*
 *      Custom data struct with read/write functions
 */
struct my_data {
    int val;
    char name[60];
};

int my_data_write(FILE *f, const void *data)
{
    if (fwrite(data, sizeof(struct my_data), 1, f) < 1) return -1;
    return 0;
}

int my_data_read(FILE *f, void **data)
{
    *data = malloc(sizeof(struct my_data));

    if (*data == NULL) return -1;

    if (fread(*data, sizeof(struct my_data), 1, f) < 1) {
        free(data);
        return -1;
    }

    return 0;
}

/*
 *      Example client code
 */
int main()
{
    Stack s = {NULL};
    Stack t = {NULL};

    struct my_data aa = {23, "Alice Atkinson"};
    struct my_data bb = {37, "Bob Bates"};
    struct my_data cc = {28, "Carol Clark"};

    stack_push(&s, &aa);
    stack_push(&s, &bb);
    stack_push(&s, &cc);

    stack_write(&s, "kk", my_data_write);

    while (s.head) stack_pop(&s);

    stack_read(&t, "kk", my_data_read);

    while (t.head) {
        struct my_data *p = stack_pop(&t);

        printf("%4d '%s'\n", p->val, p->name);
        free(p);
    }

    return 0;    
}