I had previously written some code (as practice) to write create a struct type ‘Data’ made up of an integer field and a string field (char*). I created 3 instances of the Data struct and wrote them to a data file.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
int main(void)
{
typedef struct{
int num;
char* str;
}Data;
// Create data file
FILE* fptr = fopen("stuff.dat", "wb");
// Declare 3 Data structs and an array of Data structs
Data data1, data2, data3, dataf[3];
// Initialize the values of fields of each of the 3 Data structs
data1.num = 5;
data1.str = malloc(100);
strcpy(data1.str, "Scott Pilgrim");
data1.str = (char*) realloc(data1.str, strlen(data1.str) + 1);
data2.num = 7;
data2.str = malloc(100);
strcpy(data2.str, "Dave Patel");
data2.str = (char*) realloc(data2.str, strlen(data2.str) + 1);
data3.num = 67;
data3.str = malloc(100);
strcpy(data3.str, "Matthew Skinner red riding hood");
data3.str = (char*) realloc(data3.str, strlen(data3.str) + 1);
// Write the data structs to the data file "stuff.dat" if the file pointer is not NULL
// and close the file
if(fptr != NULL){
fwrite(&data1, sizeof(data1), 1, fptr);
fwrite(&data2, sizeof(data2), 1, fptr);
fwrite(&data3, sizeof(data3), 1, fptr);
printf("%ld\n", ftell(fptr));
fclose(fptr);
}
// Open data file for reading
fptr = fopen("stuff.dat", "rb");
// Read the data in the data file into the array of Data structs if the file pointer is
// not NULL and print the fields of each struct to the screen.
if (fptr != NULL){
for (int i = 0; i < 3; i++){
fread((dataf+i), sizeof(data1), 1, fptr);
printf("%d: %s", dataf[i].num, dataf[i].str);
if (i != 2){
printf(", ");
}
// printf("%d\n", feof(fptr)); -please ignore. I was trying to confirm something
}
// printf("\n");
// printf("%ld\n", ftell(fptr));
// fseek(fptr, 1*sizeof(data1), SEEK_SET);
// printf("%ld\n", ftell(fptr)); Please ignore. I was testing something
fclose(fptr);
}
// Free up any memory allocated for the string fields of the Data structs
free(dataf[0].str);
free(dataf[1].str);
free(dataf[2].str);
free(data1.str);
free(data2.str);
free(data3.str);
return 0;
}
Lets call the above code program.c. It ran as expected. Now begins my problem in another code which I wrote to read the data only from the data file. Below is program2.c:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
typedef struct{
int num;
char* str;
}Data;
int main(void)
{
// Open data file for reading
FILE* fp = fopen("stuff.dat", "rb");
// Allocate memory for storing a Data type struct
Data* dataf = malloc(sizeof(Data));
// dataf.str = malloc(100);
// char s[100];
if (fp != NULL){
// printf("%ld\n", ftell(fp)); this was to check the position of the pointer
// move pointer to the position of the last data written into the file
fseek(fp, 2*sizeof(dataf), SEEK_SET);
// print out position of pointer
printf("%ld\n", ftell(fp));
// Read data into the dataf Data struct
fread(dataf, sizeof(Data), 1, fp);
// Print position of pointer
printf("%ld\n", ftell(fp));
// (*dataf).str = (char*) malloc((strlen((*dataf).str) + 1));ignore please
// print fields of dataf to screen
printf("%d: %s\n", (*dataf).num, (*dataf).str);
fclose(fp);
}
return 0;
}
Code in program2.c does not run. I mean it compiles but only prints out the pointer position but not the fields. I did switch up the code a bit where I read first and foremost into the address of the struct field num and then read into an array of characters and tried to store that array of characters into the str field of the struct. In that case I got only the num field printed out but null for the str field. How can solve the problem?
>Solution :
Your struct contains a pointer as one of its members. So when you write the struct to disk you’re writing the pointer value to disk, not the string that’s stored at that address. This works out alright when the same process that wrote the values is also reading, however when another process reads those pointer values they’re not valid, and attempting to dereference those value leads to undefined behavior.
You also have a double-free error in the first code as a result of this, as data1.str and dataf[0].str contain the same pointer value. The same holds for the other two.
You need to change the str member to have an array type, i.e. char str[100]. Then the actual strings will be written to disk, and you don’t need to worry about allocating space for that field.
So in both programs, change the struct definition to use an array instead of a pointer:
typedef struct{
int num;
char str[100];
}Data;
In the first program, remove the allocations for the str field:
data1.num = 5;
strcpy(data1.str, "Scott Pilgrim");
data2.num = 7;
strcpy(data2.str, "Dave Patel");
data3.num = 67;
strcpy(data3.str, "Matthew Skinner red riding hood");
And remove the calls to free at the end.