0

I tried to write and read from a file with pointers in structures. But when I read from file I see some garbage value. I am using GCC 4.7.2 on Linux. Need some help.

Read:

//read from a file

#include<stdio.h>

typedef struct 
{
    char* name;
    char* phone;
}LISTING;

int main(void)
{
    LISTING phoneList[14];  
    FILE * fp = NULL;

    fp = fopen("/media/Study/PhoneDirectory.dat","rb"); 

    if(fp == NULL)
        printf("Error opening file!!!");    

    fseek(fp,0,SEEK_SET);

    if(fread(&phoneList[1],sizeof(LISTING),1,fp)==1)
        printf("%s %s",phoneList[1].name,phoneList[1].phone);

    fclose(fp);

    return 0;
}

And write:

//Write to file

#include<stdio.h>

typedef struct 
{
    char* name;
    char* phone;
}LISTING;

int main(void)
{
    LISTING phoneList[2];   
    FILE * fp = NULL;

    fp = fopen("/media/Study/PhoneDirectory.dat","wb");

    phoneList[1].name = "Santosh";
    phoneList[1].phone = "9657681798";

    if(fwrite(&phoneList[1],sizeof(LISTING),1,fp)==1)
        printf("inserted");

    fclose(fp);

    return 0;
}
user101285
  • 111
  • 3
  • 11
  • 1
    the number 1 don't of writing to a file: don't write pointers... – ratchet freak Dec 30 '13 at 17:56
  • 1
    @user101285 This will be the second of your questions to get migrated to Stack Overflow. Please read the [help/on-topic] on each site to familiarize yourself with what the differences between the two are - it will help you get better answers, faster. –  Dec 30 '13 at 18:01
  • What are you actually trying to write here ? – Josselin Poiret Dec 30 '13 at 18:51

3 Answers3

1

Pointers are only meaningful in the application process that they originate from. If you write them to a file, as you're doing here, the values you read back will be meaningless — they will most likely point to uninitialized memory, or to memory which is being used for something else entirely.

You will need to come up with another way of writing this data to a file.

0

The problem you have is equivocating between char* and char[]. You can certainly assign a string literal to a char*, but you need to understand what the contents of a LISTING structure contain, and how you want to serialize and deserialize data to a file.

It does not make sense to save pointers from one process and read them into another process, so you probably want to save the contents (what a pointer points at). You want to store two values, (name, phone) to the file. Since you likely want to store the literal name and literal phone, let us consider what the file might look like:

roast duck|212-333-4444
peking duck|411-511-61111
duck soup|314-222-3333
free duck|800-111-2222
...

You need functions to serialize and deserialize your data. Since your LISTING type is pointers, you will need to allocate appropriate space for those values, as you read them, and you need functions (methods) to read serialized data from a file and write serialized data to a file.

Reading (you will need to allocate enough space),

int
listing_read(FILE*fp, LISTING* listing)
{
    char name_buffer[100];
    char phone_buffer[100];

    if(!fp) return(-1);
    if(!listing) return(-2);

    int res = fscanf(fp,"%s|%s\n",name_buffer,phone_buffer);
    if( !res ) {
        //handle error here
    }
    //careful here, you cannot free if you didn't malloc/strdup
    if(listing->name) free(listing->name);
    if(listing->phone) free(listing->phone);

    listing->name = strdup(name_buffer);
    listing->phone = strdup(phone_buffer);
    return(0);
}

Writing (you will need to provide proper formatting),

int
listing_write(FILE*fp, LISTING* listing)
{
    if(!fp) return(-1);
    if(!listing) return(-2);

    fprintf(fp,"%s|%s\n",listing->name,listing->phone);
    return(0);
}

Here is how you need to modify your code,

//read from a file
#include<stdio.h>

typedef struct 
{
char* name;
char* phone;
}LISTING;

int main(void)
{
    LISTING phoneList[14];  
    FILE* fp = NULL;
    if( !(fp = fopen("/media/Study/PhoneDirectory.dat","rb")) ) {
        printf("Error opening file!!!");
        exit(1);
    }

    fseek(fp,0,SEEK_SET);
    if( listing_read(fp,&phoneList[0]) >= 0 ) {
        printf("%s %s",phoneList[0].name,phoneList[0].phone);
    }
    fclose(fp);

    return 0;
}

And here is how writing the file would change,

//Write to file
#include<stdio.h>

typedef struct 
{
char* name;
char* phone;
}LISTING;

int main(void)
{
    LISTING phoneList[14];   
    FILE* fp = NULL;

    if( !(fp = fopen("/media/Study/PhoneDirectory.dat","wb")) ) {
        printf("error, cannot write file\n");
        exit(1);
    }

    phoneList[0].name = "Santosh";
    phoneList[0].phone = "9657681798";

    if( listing_write(fp,&phoneList[0])>=0) {
        printf("inserted");
    }
    fclose(fp);

    return 0;
}

Note that in you writing program you assign the string literals "Santosh" and "9657681798" to the LISTING members name and phone. Though legal to do, you need a better understanding of what C does here. C takes the address of these C-string constants and assigns those addresses to the phonelist[1].name and phonelist[1].phone member pointers.

Consider that if you did this assignment,

    phoneList[0].name = "Santosh";
    phoneList[0].phone = "9657681798";

You have assigned the pointers to constant strings to your structure members.

But if you were to allocate space (for example, using strdup()),

    phoneList[0].name = strdup("Santosh");
    phoneList[0].phone = strdup("9657681798");

You have allocated space for the strings, assigning independent locations for these member elements. Which is is more likely what you want to do.


Note that I used phonelist[0] since C has zero-based arrays.

ChuckCottrill
  • 4,360
  • 2
  • 24
  • 42
-1
printf("%s %s",phoneList[1].name,phoneList[1].phone);

The above statement invokes undefined behaviour.

Since the pointers name & phone of struct object phoneList[1] are not initialized dereferencing them invokes UB. In your case they are throwing out garbage values but it could have lead to a crash also.

To fit your case of reading the contents of file and storing it in the struct objects use getline function to read them row-wise(assuming that all the details are stored line-wise) and then dynamically allocate the memory for char pointers then assign them to the read value. But, this approach leads to lot of memory management which is error prone.

Uchia Itachi
  • 5,287
  • 2
  • 23
  • 26
  • This answer is correct, as the pointers read from the file are invalid in the context of the reading processes. More detail would be helpful. – ChuckCottrill Dec 31 '13 at 00:09