Writing char pointer as struct member to file issue - c

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.

Related

How to make a void pointer to read a given part of a binary file

I have a binary file which contains 3 differents structs and a christmas text. On the first line of the binaryfile have they provided me with a int which represents the size of a package inside the file. A package contains 3 structs ,the chistmastext and the size.
The structs lies in a file called framehdr.h and the binary file I'm reading is called TCPdump.
Now am I trying to create a program att will read each package at a time and then withdraw the text.
I have started with something like this:
#pragma warning(disable: 4996)
#include <stdio.h>
#include <stdlib.h>
#include "framehdr.h"
#include <crtdbg.h>
int main()
{
_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
FILE *fileOpen;
char *buffer;
size_t dataInFile;
long filesize;
// The three structs
struct ethernet_hdr ethHdr;
struct ip_hdr ipHdr;
struct tcp_hdr tcpHDr;
fileOpen = fopen("C:\\Users\\Viktor\\source\\repos\\Laboration_3\\Laboration_3\\TCPdump", "rb");
if (fileOpen == NULL)
{
printf("Error\n");
}
else
{
printf("Success\n");
}
char lenOf[10];
size_t nr;
// Reads until \n comes
fgets(lenOf, sizeof(lenOf), fileOpen);
sscanf(lenOf, "%d", &nr);
// Withdraw the size of a package and check if it's correct
printf("Value: %d\n", nr);
printf("Adress: %d\n", &nr);
void *ptr;
fread(&ptr, nr, 1, fileOpen);
int resEth = 14;
printf("resEth: %d\n", resEth);
int resIP = IP_HL((struct ip_hdr*)ptr);
printf("ResIP: %d\n", resIP);
int resTcp = TH_OFF((struct tcp_hdr*)ptr);
printf("tcpIP: %d\n", resTcp);
int res = resEth + resIP + resTcp;
printf("Total: %d", res);
fclose(fileOpen);
//free(buffer);
system("pause");
return 0;
}
I know that the first struct ethernet will always have the size of 14 but I need to get the size of the other 2 and I'm suppose to use IP_HL and TH_OFF for that.
But my problems lies in that I can't seem to read the entire package to one
void * with the fread. I get noting in my *ptr.
Which in turn makes the code break when I try to convert the void * to one of the structs ones.
What I'm doing wrong with the void *?
Two problems:
First you should not really use text functions when reading binary files. Binary files doesn't really have "lines" in the sense that text file have it.
Secondly, with
void *ptr;
fread(&ptr, nr, 1, fileOpen);
you are passing a pointer to the pointer variable, you don't actually read anything into memory and then make ptr point to that memory. What happens now is that the fread function will read nr bytes from the file, and then write it to the memory pointed to by &ptr, which will lead to undefined behavior if nr > sizeof ptr (as then the data will be written out of bounds).
You have to allocate nr bytes of memory, and then pass a pointer to the first element of that:
char data[nr];
fread(data, nr, 1, fileOpen);
You should also get into the habit of checking for errors. What if the fread function fails? Or the file is truncated and there isn't nr bytes left to read?
You can check for these conditions by checking what fread returns.
And not only check for fread, there are more functions than fopen that can fail.

Repeating strings but not ints when reading from files

I've written a function that uses fscanf("%s &d &d &d") to read from a file. For some reason, when I display the output, all the integer values are correct, but the strings are all the same. food_stuff is a struct held in an array.
*EDIT
I've tried using a for to add each element from one array to the other, and I've tried strcpy.
Here's the function.
int readFromFile()
{
char*name;
int type;
int cals;
int price;
FILE * food_file;
food_file = fopen ("food_file.txt", "r");
while(fscanf(food_file, "%s %d %d %d", name, &type, &cals, &price)!=EOF)
{
food_stuff fs;
fs.name = name;
fs.type = type;
fs.calories = cals;
fs.price = price;
fflush(stdin);
addItem(fs);
}
}
As a commenter already has pointed out, you need to allocate memory for your variable name. Because the variable is temporary, you could just declare a local char array on the stack instead of allocationg memory on the heap:
char name[40];
Your problem is that the name field in your structure is probably also only a pointer to char. That pointer points to name for all footstuff structs you generate, which is the same for all instances, only with different contents. (What's worse: That buffer won't exist any more if you leave readFromFile, invalidating the name field for every foodstuff.)
One possible solution is to make the name field also a buffer, e.g:
typedef struct food_stuff food_stuff;
struct food_stuff {
char name[40];
int type;
int cals;
int price;
};
When you assign the read data to the struct, don't copy the pointer, but copy the contents with strcpy. (You need to include <string.h> for that):
strcpy(fs.name, name);
This solution assumes that all names are less than 39 characters long, which is not a safe assumption for real data. You should specify a width on the %s format in your call to fscanf.
Another solution is leave the struct definition as I assume it is now with char *name and to allocate memory for each new struct. You still have to copy the contents, though:
fs.name = malloc(strlen(name) + 1);
// error check omitted
strcpy(fs.name, name);
This solution requires that you free the extra memory allocated when you free the foodstuff structs.

C fread() magically reading dynamically allocated struct members, how?

This is a test program that I have written for a larger project that I am working on. It has to do with writing struct data to disk with fwrite() and then reading that data back with fread(). One member of the struct is dynamically allocated.
First, here is my code
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define STRING_LEN 128
struct Person {
int age;
char *name;
};
int main(int argc, const char *argv[])
{
struct Person *person = calloc(1, sizeof(struct Person));
person->age = 22;
person->name = calloc(STRING_LEN, sizeof(char));
char *name = "Name that is really, really, really, really, really, really, long.";
strncpy(person->name, name, STRING_LEN);
FILE *out_file = fopen("rw.out", "w");
fwrite(person, sizeof(struct Person), 1, out_file);
fclose(out_file);
FILE *in_file = fopen("rw.out", "r");
struct Person *person_read = calloc(1, sizeof(struct Person));
fread(person_read, sizeof(struct Person), 1, in_file);
fclose(in_file);
printf("%d %s\n", person_read->age, person_read->name);
free(person->name);
free(person);
free(person_read);
return 0;
}
And the outpout
22 Name that is really, really, really, really, really, really, long.
My question is, why is this working? Shouldn't fwrite() only write the address that 'name' contains (i.e., the address of the beginning of the string)? That is, I am passing in sizeof(struct Person) to fwrite() and yet it is writing the string the 'name' is pointing to.
Even more confusing to me is the behavior of fread(). Again, if I am passing sizeof(struct Person), how is the actual value of 'name' being read? How is the memory for it being allocated?
My previous understanding of how to use fwrite() + fread() was that I would have to "manually" write the data that 'name' was pointing to, "manually" read that data, and then copy that string after allocating memory for both the structure and the 'name' member. In other words, I would have to traverse any pointers myself, write the data, and then read that data back in the same order.
EDIT: Dan and the others are correct. I have looked at the output file with xxd:
0000000: 1600 0000 0000 0000 30a0 d900 0000 0000 ........0.......
If I print out the address that 'name' contains before writing and after reading it is the same (0xd9a030), which matches the output from xxd.
You are writing the data in the struct, which is an int followed by a pointer to a string. It's just data like anything else, and you know how long it is because the struct is fixed length - an int plus a pointer. You read the same pointer to the same name string as the original. The name itself is neither written nor read.
Both person->name and person_read->name wind up pointing to the same memory location. Since you didn't deallocate person->name before reading the file back in, the pointer value in person_read->name is still valid.
If you had deallocated person->name or read the file from a different program, the pointer value would no longer be valid, and attempting to reference it would invoke undefined behavior - you would either have printed out gibberish or gotten a segfault.
The *name pointer remains valid throughout the fwrite and fread calls, which is seemingly a fluke to you. If you free(person->name) before printf you would get the result or error your were expecting.

Write pointer to file in 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);
...

fread/fwrite string in C

I have a binary file which contains records. The structure of the file is as such:
Structure (see below)
Name String
Address String
The structure in question:
typedef struct{
char * name;
char * address;
short addressLength, nameLength;
int phoneNumber;
}employeeRecord;
employeeRecord record;
I get the name as such:
char name[50];
printf("\nName:");
fgets(name,50,stdin);
record.nameLength = strlen(name)-1;
record.name = malloc(sizeof(char)*record.nameLength);
strcpy(record.name,name);
I write the structure, the the name, then the address (as mentioned above).
fwrite(&record.name,sizeof(char),record.nameLength,fp);
where fp is a file pointer.
Now i close the file.
However, if i then want to read from the file to get this data back, I believe I need to read in the structure, read the nameLength variable, malloc enough memory for the name to sit in, then fread the name into the variable.
Like so:
char *nameString = malloc(sizeof(char)*record.nameLength);
fread(nameString,sizeof(char),record.nameLength,fp);
printf("\nName: %s",nameString);
However, when i attempt this, i do not get valid data.
Example:
Input name is: Joseph (6 characters)
Output data:
Name length is 6 (correct),
Name is �A � (aka garbage)
So obviously im doing something wrong. Could someone give me some help?
I see two problems with the write, you are setting record.nameLength to be too small, and you are passing the wrong pointer to fwrite for the name. record.name is already a pointer.
Change this
record.nameLength = strlen(name)-1;
...
fwrite(&record.name,sizeof(char),record.nameLength,fp);
to this
record.nameLength = strlen(name);
...
fwrite(record.name,sizeof(char),record.nameLength,fp);
You also have a problem on the read, since you aren't writing the terminating \0 from the strings into your file, when you read back, you need to add that terminator explicitly.
char *nameString = malloc(sizeof(char)* (record.nameLength + 1));
fread(nameString,sizeof(char),record.nameLength,fp);
nameString[record.NameLength] = '\0';
The problem is that you pass the pointer to the char* in your fwrite:
fwrite(&record.name,sizeof(char),record.nameLength,fp);
This means that instead of writing the name, you're writing the memory address of the name. Fwrite expects a pointer to the data to write—in your case, that's the pointer to the char data, not the pointer to the pointer of the char data.
Pass it record.name instead of &record.name and you should be set:
fwrite(record.name, sizeof(char), record.nameLength, fp);

Resources