我在读取包含链接节点的二进制文件时遇到问题。
这是代码:
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;
}
}
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 <> %dn", data1->val, data2->val);
exit(1);
}
free(data1);
free(data2);
}
//...more code
puts("All tests passed");
return 0;
}
执行的结果是:
Stack len: 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 *
指针中。
节点本身不携带有意义的信息。或者跨会话有意义的信息:data
和next
指针只在写数据的会话中有效。
你的堆栈本质上是一个线性数据结构。不存储节点,而是将堆栈数据存储为data
成员数组。当您读取它们时,用新分配的节点和读取的data
字段构建一个列表。
你的堆栈使用void *
指针来允许各种数据类型。因此,您必须找到一种方法来告诉读和写方法应该如何写入或读取数据。
您可以提供一个回调函数来传递打开的文件。如果需要,这样的回调可以处理复杂的数据结构作为有效负载。
Edit:下面的代码显示了如何使用自定义函数序列化堆栈以进行读写的示例。对称回调函数应该将数据写入文件并读取数据。read函数可以分配内存,这些内存属于堆栈。用户必须确保已释放。
回调函数可以返回一个负数来表示错误。要读取的堆栈不必为空。
#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;
}