Write pointer to file in C - c

I have a structure:
typedef struct student {
char *name;
char *surname;
int age;
} Student;
I need to write the structure into a binary file.
Here is my attempt :
Student *s = malloc(sizeof(*s));
I fill my structure with data and then I write the struct into the file with :
fwrite(s, sizeof(*s), 1, fp);
In my file, name and surname don't exist. Instead they have their respective address of their char * pointer.
How can I write a char * into a file instead of the address of the pointer ?

You need to dereference your pointer and write the parts of the struct. (You should not fwrite a struct directly, but rather encode its parts and write those:
Student *s = malloc(sizeof(*s));
s->name = "Jon";
s->surname = "Skeet";
s->age = 34;
// ....
fwrite(s->name, sizeof(char), strlen(s->name) + 1, fp);
fwrite(s->surname, sizeof(char), strlen(s->surname) + 1, fp);
//This one is a bit dangerous, but you get the idea.
fwrite(&(s->age), sizeof(s->age), 1, fp);

You will have to do some extra work to write out the data pointed to by the structure elements - probably just write it out element by element.
Alternatively change your structure to something like this:
typedef struct
{
char name[MAX_NAME_LEN];
char surname[MAX_SURNAME_LEN];
int age;
} Student;

if you want to directly write the structure as it is to a file you would want to define the sizes of name and surname rather than dynamically allocating it.
typedef structure student { char name[100]; char surname[100]; int age; }
else you will need to write each info in the structure one by one.

In C, you have to serialize structures (convert them to a series of bytes) manually. If you want the data you output to be readable by another machine, you have to take endianness account endianness.
Here's a simple example of serializing your structure (writing it to a file), disregarding endianness/differing word size and making the code unportable:
size_t length;
length = strlen(s->name) + 1;
fwrite(&length, sizeof(length), 1, fp);
fwrite(s->name, 1, length, fp);
length = strlen(s->surname) + 1;
fwrite(&length, sizeof(length), 1, fp);
fwrite(s->surname, 1, length, fp);
fwrite(&s->age, sizeof(s->age), 1, fp);
And to unserialize:
size_t length;
fread(&length, sizeof(length), 1, fp);
s->name = malloc(length);
fread(s->name, 1, length, fp);
fread(&length, sizeof(length), 1, fp);
s->surname = malloc(length);
fread(s->surname, 1, length, fp);
fread(&s->age, sizeof(s->age), 1, fp);
In a real application, you should check the validity of your input rather than blindly assuming the input is valid and trustworthy. Also, you need to decide what byte order you'll use to store your ints, size_ts, etc, and make sure you read/write them in the correct byte order by using bit shifting.
By the way, you may want to look at tpl, a simple binary serialization library for C.

You need to dereference s and individually write out each element of the structure to actually write thecontents to the file:
size_t nameCount = strlen(s->name) + 1;
fwrite(s->name, sizeof(char), nameCount, fp);
size_t surnameCount = strlen(s->surname) + 1;
fwrite(s->surname, sizeof(char), surnameCount, fp);

Note that anyway you won't be able to see the fields in a 'normal' form because you write it to a binary file. Try to open the file without the "b" in the mode argument of fopen (r+\ w\ a etc. instead of rb+\ wb\ ab).
You can see that when using fread (from the binary file, after opening it in "rb" mode you should get the structure fields as expected.

You'll have to write out the fields individually, as others have said. One thing which may help reading back the data is to write out the string lengths before each entry, eg.
size_t len;
len = strlen(s->name)+1;
fwrite(&len,sizeof(len),1,stream);
fwrite(s->name,sizeof(*(s->name)),len,stream);
...
Reading back is then a matter of
Student * s = malloc(sizeof(*s));
size_t len;
fread(&len,sizeof(len),1,stream);
s->name = malloc(len);
fread(s->name,sizeof(*(s->name)),len,stream);
...

Related

Writing char pointer as struct member to file issue

I have a struct member as a char * and assign it to a string literal "John" on the struct initialisation as shown below. The string prints fine with printf.
However, if I write this string to a file using fwrite, in the file I read back garbage.
If I use a char array instead of a char * (commented out in the struct as shown), and write it to the file, I can read back the string in the file as expected. I can't understand this. Shouldn't fwrite in both cases take the pointer and write the correct string to the file?
Furthermore , if I declare a separate char * and point it to a string literal and write it to the file, I can read it back as expected. Many thanks in advance for any help with explanation. (I am using codeblocks IDE with Mingw compiler on Windows).
UPDATED : To include actual code used
int main()
{
struct person
{
//char name[20];
char* name;
int age;
};
struct person p1 = {"John", 25};
printf("%s\n", p1.name);
FILE* fp = fopen("test.txt", "w");
fwrite(&p1, 1, 10, fp);
fclose(fp);
return 0;
}
With this structure definition:
struct person
{
char* name;
int age;
};
following code won't do what you expect:
struct person p1 = {"John", 25};
printf("%s\n", p1.name);
FILE* fp = fopen("test.txt", "w");
fwrite(&p1, 1, 10, fp);
The reason is that bytes of the variable p1: doesn't actually contain the string "John", but it contains a pointer to the string "John", that is a the memory address of the string "John". Now fwrite(&p1, 1, 10, fp); will write 10 bytes starting from the memory address of p1, that is (assuming a 32 bit system): 4 bytes representing the pointer which is just some memory address, 4 bytes representing 1 (age) and 2 bytes that follow in memory that don't belong to the p1variable.
Now if the structure is define like this:
struct person
{
char name[20];
int age;
};
The situation is entirely different. This time the variable p1 actually does contain the string "John". the first 4 bytes contain the characters J, o, h, n then follows a NUL character which is the string terminator and then follow 15 bytes of undetermined value. Then follow 4 bytes representing age.
In that case the fwrite(&p1, 1, 10, fp); will again write 10 bytes starting from the memory address of p1, but as explained in the paragraph above, the first 4 bytes contain "John", the 5th byte contains the NUL terminator and the 5 remaining bytes are of indeterminate value.
The commenters are saying that the code you provided does not give enough information to solve your problem. It looks like the error lies elsewhere in your code. I can convert your code to a MCVE like this:
#include <stdio.h>
struct person
{
//char name[20];
char* name;
int age;
};
int main(void)
{
struct person p1 = {"John", 25};
printf("%s\n", p1.name);
FILE *fp = fopen("test.txt", "w");
fwrite(p1.name, 1, 10, fp);
fclose(fp);
char buffer[20];
fp = fopen("test.txt", "r");
fread(buffer, 1, 10, fp);
fclose(fp);
printf("The stored name is: %s\n", buffer);
return 0;
}
But I am sure that what you have differs, because this code works:
John
The stored name is: John
Update
From the new code that you provided, I can see that your problem is that you are writing the contents of a struct to a file instead of writing a string to a file, as in your original code example.
The original code used:
fwrite(p1.name, 1, 10, fp);
and wrote 10 bytes of the string p1.name to the file "test.txt" (blowing right past the NUL terminator of the string "John" and saving garbage values that would not be seen on printing the string).
The new code uses:
fwrite(&p1, 1, 10, fp);
saving the first 10 bytes of the struct p1 to the file "test.txt".
When the struct contains char name[20];, the first 10 bytes are chars stored in the character array name, and this is why your code appeared to work in this case.
When the struct instead contains char *name;, the first few bytes saved belong to the pointer name(8 bytes on my system), not to the string literal "John". The next few bytes belong to the int age(4 bytes on my system). It is these values and not the chars of the string literal "John" that are being saved in this case.
Note that the sizes of pointers and ints may be different on different systems, and it is entirely possible to have 4 bytes for the pointer and 4 bytes for the int, leaving 2 bytes which are saved that are not part of the struct. Also, though this does not affect you here, there may be padding after the first member of the struct.

How does one print a struct member after reading from binary file (using ab+) in C

Hello I am trying to write code to save a struct to a binary file then print it after reading from file. When I run my code I get a segmentation fault (11).
this is my code (sorry for all the printfs, I was trying to find where the code went wrong)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main() {
struct contact {
int unqID;
char *name;
char *relationship;
char *phone_number;
char *email;
};
FILE *file;
file = fopen("test.bin", "ab+");
struct contact conwrite;
conwrite.unqID = 0;
conwrite.name = "president";
conwrite.relationship = "";
conwrite.phone_number = "";
conwrite.email = "";
struct contact conwrite2;
conwrite2.unqID = 1;
conwrite2.name = "vice president";
conwrite2.relationship = "";
conwrite2.phone_number = "";
conwrite2.email = "";
printf("%s\n", conwrite.name);
printf("%s\n", conwrite2.name);
fwrite(&conwrite, sizeof(conwrite), 1, file);
fwrite(&conwrite2, sizeof(conwrite2), 1, file);
fclose(file);
struct contact phonebook[100];
file = fopen("test.bin", "rb");
printf("size of file: %lu\n", sizeof(file));
printf("size of contact: %lu\n", sizeof(struct contact));
int size = (sizeof(file)) / (sizeof(struct contact));
printf("size of file: %d\n", size);
int read = fread(phonebook, sizeof(conwrite), 2, file);
printf("read: %d\n", read);
if (phonebook[0].name == NULL)
printf("phonebook is empty\n");
else
printf("phonebook is filled\n");
printf("%s\n", phonebook[0].name);
fclose(file);
return 0;
}
You cannot compute the length of the file with the sizeof operator:
printf("size of file: %lu\n", sizeof(file));
Will print the size of a FILE *, the number of byte in the pointer itself. Note that sizeof evaluates to a value of type size_t for which the proper printf format is %zu. If for some reason you cannot use the z option, cast the sizeof as (unsigned long) to use %lu but be aware that size_t is actually larger than unsigned long on some platforms such a Windows 64 bits.
To compute the length of the file on disk, you can use a system call such as stat or possibly ftell() after seeking to the end of the stream with fseek(file, 0L, SEEK_END);. Note that this method only works for tiles open in binary mode. In your program, you do open it in binary mode, but also in update (the +), which seems unnecessary.
Finally, you cannot store the information pointed to by these structures with your approach: the strings pointed to by the struct members are in memory, you are just writing the values of the pointers, which will not be meaningful when read back by a different program or even a different instance of the same program. You need to make these strings arrays of char, as for example:
struct contact {
int unqID;
char name[64];
char relationship[32];
char phone_number[16];
char email[64];
};
You will need to update your code to account for the difference in types. Note that this approach introduces a limit on the size of all textual member. Storing the information in disk files is usually not done this way. Either use specialized libraries that implement databases, or use an interchange format such as csv, json, xml... by increasing order of complexity and versatility, or a custom text based format.

Prepend to unsigned char pointer in C?

In C I am reading binary data from a file into a var data like this:
unsigned char *data;
data = malloc(size);
int read_size = fread(data, 1, size, fp);
I want to prepend the var data with <filename><size> of the file. How can I achieve this?
It's not a legal C string because it's binary data with null bytes potentially all over the place.
I know to make sure I allocate it with enough memory, I just can't figure out how to actually prepend it.
Allocate enough memory to data.
Copy the prefix into it.
Get a reference to just behind what had been copied in 2..
Pass this reference to fread().
Define your own data format for storage:
<uint64_t datalength><string name><char[datalength] contents>
Or for easier in-app use:
struct named_file {
char* contents;
uint64_t datasize;
char name[]; // contents begin directly after the name.
}
Allocate the struct with enough space: sizeof(named_file)+strlen(_name)+1+_datasize
strcpy(name, _name)
contents = name+strlen(name)+1
save data to contents-pointer. memcpy(), direct reading, whatever.

Strange value when reading from binary file with fread() in C

I have an issue that I can't solve...
I used fwrite to write an int value (among other values that come from a struct) into a file that I've opened using fopen(file, "wb");
This worked fine, and when I view the contents of the file, I can see the HEX value of the int that I've "written" (and all the other values as well).
Next, I try to read the file. So, I open the file using fopen(file, "rb"); and start reading it using fread. The first int value is read correctly (e.g. 7) and all the other values after it - which belong to the first struct that I've written.
I then try to read the next struct and when I try to read the first int value, I receive a very strange number (e.g. 1140850688) but it should be 39 instead.
So the question is: Why is this happening? And how can I solve it?
My struct looks like this:
struct a {
int a1;
char* a2;
struct b* a3;
unsigned char a4;
int a5;
char* a6;
}
And the fread call looks like this: fread(&(tmpA->a1), sizeof(int), 1, file);
EDIT:
The fwrite part:
fwrite(&(tmpA->a1), sizeof(int), 1, file);
fwrite(tmpA->a2, sizeof(char), strlen(tmpA->a2), file);
fwrite(tmpA->a3, sizeof(struct b), 1, file);
fwrite(&(tmpA->a4), sizeof(unsigned char), 1, file);
fwrite(&(tmpA->a5), sizeof(int), 1, file);
fwrite(tmpA->a6, sizeof(char), strlen(tmpA->a6), file);
The fread is almost the same.
I suspect your problem is on this line
fwrite(tmpA->a3, sizeof(struct b), 1, file);
where you are writing a struct. This struct will have padding bytes, as per this wikipedia article which will offset the rest of your data. You will likely have to come up with a better way of writing that struct to memory. This page provides additional explanation.
You can check to see if this is indeed the problem by checking if a4 has the value you expect, or by comparing the size of the entire struct versus the sum of the sizes of its members.
Also, these lines look dangerous:
fwrite(tmpA->a2, sizeof(char), strlen(tmpA->a2), file);
fwrite(tmpA->a6, sizeof(char), strlen(tmpA->a6), file);
they say that the length of the string being printed may vary in your stored data. strlen will give you the length of your string without counting the terminating \0 so there is no good way to know where a string ends once you've written it. I can't imagine how you plan on reading the string back in.
If you wrote a struct from a file, you should read a struct back; trying to read an int is not going to work. However, it rarely, if ever, makes sense two write out as binary a struct that contains pointers. You should either make them arrays of fixed size, or serialize them separately from your struct.
EDIT : (in response to posted code) Most likely the problem has to do with writing an unknown number of characters in the second call of fwrite. When you serialize a C string, you should write out the number of characters before writing the characters themselves, so that when you read you'd know how much characters to allocate and to read back:
int len = strlen(tmpA->a2);
fwrite(&len, sizeof(int), 1, file);
fwrite(tmpA->a2, sizeof(char), len, file);
...
fread(&(tmpA->a1), sizeof(int), 1, file);
tmpA->a2 = malloc(tmpA->a1+1);
fread(tmpA->a2, sizeof(char), tmpA->a1, file);
tmpA->a2[tmpA->a1] = '\0';

fread into multiple variables

I have the following code:
// fp is a FILE opened in binary mode
unsigned char bit_depth;
unsigned char color_type;
unsigned char compression_method;
unsigned char filter_method;
unsigned char interlace_method;
fread(&bit_depth, 1, 1, fp);
fread(&color_type, 1, 1, fp);
fread(&compression_method, 1, 1, fp);
fread(&filter_method, 1, 1, fp);
fread(&interlace_method, 1, 1, fp);
I'd like to to all freads in one go, perhaps using something akin to a format string (but fscanf is only for text files, right?).
Any help is much appreciated, thanks!
Sounds like you just want a struct.
typedef struct info {
unsigned char bit_depth;
unsigned char color_type;
unsigned char compression_method;
unsigned char filter_method;
unsigned char interlace_method;
} info_t;
info_t my_info;
fread(&my_info, sizeof(info_t), 1, fp);
But, be careful about structure padding. If the file was written with just 5 bytes, you'll want to check in your debugger and make sure that this doesn't mess up your file pointer. Frequently, structs are aligned to 8-byte boundaries, so see what sizeof(info_t) evaluates to, and if necessary, put a pragma around the struct to affect its alignment.
Make an array with 5 elements and read it into the array, something like this:
unsigned char infos[5];
fread(infos, 1, 5, fp);
unsigned char bit_depth = infos[0];
/* etc. */
Or you could put it into a struct, but watch out for alignment. To remedy that you could prefix it with a #pragma pack directive, but that's less portable. As the elements are of the same type I recommend the array-approach.
Read the data into a buffer of sufficient size-:
#define READ_BUF_SZ 5
unsigned char readbuf[READ_BUF_SZ];
fread(readbuf, READ_BUF_SZ, 1, fp);
then assign the variables from the buffer
bit_depth = readbuf[0];
etc.
****** 30 chars needed *******
See
http://www.cplusplus.com/reference/cstdio/fread

Resources