0
votes

I have a problem reading from binary file and casting it to a unknown type of struct...

  • Should I really avoid having the save/load functions inside my Linked List code?
  • Is it because the GCC compiler I'm using stacks the struct with more data to conveniently store in memory, and since when loading the function doesn't know about this "offsets"?

I'm writing a generic linked list in C, and it is intended to be a header file, so I can just use it anywhere. Since it's going to be a generic type list, the header will not know about the type of data on the list (I'm looking at mixed types, so structs). For saving data, I just pass the address of the data, and the length of it, extracted from sizeof(struct). The reading is the same concept, using fread(container, sizeof(struct), 1, FILE), which is passed by the calling program, again extracting se size using sizeof(struct). But in practice, it does not work...

#ifndef LINKEDLIST_H_INCLUDED
#define LINKEDLIST_H_INCLUDED
#include <string.h>

typedef struct tagNode{
    void            *data;
    struct tagNode  *next_Node;
} Node;

typedef struct tagLinkedList{
    Node *Head;
    int   Size;
} LinkedList;

int LinkedList_New(LinkedList *llist){
    llist->Head = NULL;
    llist->Size = 0;
    return 1;
}

int LinkedList_Insert(LinkedList *llist, int index, void *Data, size_t s_Data){
    int cur_index = 0;
    if(index > llist->Size || index < 0)
        index = 0;

    Node *newNode = malloc(sizeof(Node));
    newNode->data = malloc(s_Data);
    if(newNode       == NULL){return 0;}
    if(newNode->data == NULL){return 0;}
    newNode->data = Data;

    Node *currentNode = llist->Head;
    Node *lastNode    = llist->Head;

    if(index == 0){
        newNode->next_Node = llist->Head;
        llist->Head = newNode;
    }else{
        while(llist->Head->next_Node != NULL && cur_index != index){
            if(cur_index == index){
                newNode->next_Node  = currentNode;
                lastNode->next_Node = newNode;
            }else{
                lastNode    = currentNode;
                currentNode = currentNode->next_Node;
                cur_index++;
            }
        }
    }
    llist->Size += 1;
}

int LinkedList_Save(char *Path, LinkedList *llist, size_t sData){
    FILE *fp;
    fp = fopen(Path, "w");
    if(fp == NULL){return -1;}

    Node *currentNode;
    currentNode = llist->Head;

    while(currentNode != NULL){
        fwrite(currentNode->data, sData, 1, fp);
        currentNode = currentNode->next_Node;
    }
    fclose(fp);
    return 1;
}

int LinkedList_Load(char *Path, LinkedList *llist, size_t sData){
    FILE *fp;
    fp = fopen(Path, "r");
    if(fp == NULL){fclose(fp);return -1;}

    while(!feof(fp)){
        void *Data = malloc(sData);
        if(Data == NULL){fclose(fp);return -1;}
        fread(Data, sData, 1, fp);
        LinkedList_Insert(llist, 0, Data, sData);
    }
    fclose(fp);
    return 1;
}
#endif // LINKEDLIST_H_INCLUDED

And my currently testing subject:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "../LinkedList.h"
typedef struct{
    int a;
    char b[5];
} tempo;

int main(){
    tempo teste = {5, "oito"};

    LinkedList lista;
    LinkedList_New(&lista);
    LinkedList_Insert(&lista, 0, &teste, sizeof(tempo));
    LinkedList_Save("data.txt", &lista, sizeof(tempo));
    printf("%s", ((tempo*)lista.Head->data)->b);
    LinkedList ls2;
    LinkedList_New(&ls2);
    LinkedList_Load("data.txt", &ls2, sizeof(tempo));

    printf("%s", ((tempo*)ls2.Head->data)->b);

    return 1;
}

The first printf shows me the b variable in the struct, which means the list is working like its supposed to.

But the second printf, if used to show the a variable(int), I get a random number(something like 8712382), and if used to show the b variable, I get just "L"

1
Is the binary data being read produced on the same type of environment to avoid endianness issues? Also, are you considering struct padding? (from reading it into the binary file, to reading it back out) - ryyker
Well, since im passing de sData as de size of the struct, im reading 1 block of sData size into the Data variable, from the fp FILE, or am I wrong? I mean, if the sData is 12, I read 12 byte's 1 times into the Data storage, right? Also, yes, it is all generated on the fly, same environment, as seeing in the second code block posted - Mathias Hemmer
Not a problem, Im taking a look at struct packing, but since its all happening in the same environment, the program should know how to align the data back correctly, or so I think... - Mathias Hemmer
Have you looked at the file that it produces? Is it the right size for 1 record? The char array should be obvious too - Chris Turner
Again, if used for generic purposes, suggest being careful with things such as the lenght of it, extracted from sizeof(struct). Make sure you understand packing, padding and alignment boundaries. Stuff about that HERE. - ryyker

1 Answers

1
votes

you have a problem in your LinkedList_load function. update it to the following

int LinkedList_Load(char *Path, LinkedList *llist, size_t sData){
        FILE *fp;
        fp = fopen(Path, "rb");
        if(fp == NULL){fclose(fp);return -1;}
        fseek(fp,0L,SEEK_SET);
        while(!feof(fp)){
                void *Data = malloc(sData);
                if(Data == NULL){fclose(fp);return -1;}
                int readed=fread(Data, sData, 1, fp);
                if(readed==0){return -1;}
                /*you were displaying the last reading that contains 
                *nothing, the previous check solves the problem.
                */
                printf("readed %d items:  \n",readed);
                LinkedList_Insert(llist, 0, Data, sData);       
        }
       fclose(fp);

}