That's, my struct:
typedef struct
{
char* name;
int played;
int win;
} Player;
And this is my code, basically I'm passing an array of struct, which includes player name, number of wins and number of played games. What the later code is doing, is basically asking for input, the problem is that once I put (*(players (arraySize - 1))) to file, later it reads (from the same file) and reads not the same values as putted earlier (currentPlayer.name is a malloc which size is MAX) :
#define N 9
#define MAX 1000
#define filename5 "tempName.bin"
#define filename6 "data.bin"
void putData(Player* players, Player currentPlayer, int arraySize)
{
FILE* fo;
FILE* fp;
fo = fopen(filename6, "ab");
fp = fopen(filename5, "wb");
if (!fp || !fo)
{
printf("error");
exit(0);
}
gets(currentPlayer.name);
(*(players (arraySize - 1))).name = currentPlayer.name;
(*(players (arraySize - 1))).played = 0;
(*(players (arraySize - 1))).win = 0;
currentPlayer = (*(players (arraySize - 1)));
fwrite(&(*(players (arraySize - 1))), sizeof(Player), 1, fo);
fclose(fo);
FILE* fl; //and here I'm trying to read what I putted in file earlier
fl = fopen("data.bin", "rb");
fseek(stdin, 0, SEEK_CUR);//clears buffer
Player a;
a.name = malloc(MAX);
fread(&players, sizeof(Player), 1, fl);
printf("%d %d %s", a.played, a.win, a.name);
fclose(fl);
break;
fclose(fp);
}
CodePudding user response:
Pointers cannot menaingfully be saved.
You have to save what they point to and somehow identify it so that the pointer can be replaced by a suitable "this one" in the file.
Then when you find a "this one" in the file you first need to find within the file what it refers to, load that and write its address into the pointer variable you want to restore.
Note that at this point, the address/number stored inside the pointer is almost guaranteed to be different than before saving. But what it points to has the same content, which is what you actually want when "storing a pointer".
The general idea I described is a concept called "persistence" for the special case of pointers. The special aspect of pointers is that you do not actually want to store the (basically) number inside, it is necessary to represent the semantic of "this one".
The details of which (proprietary file syntax, database, persistence libraries...) require some choices for your special case, which you have to decide; instead of letting me put any example here in detail which would in some way or other most likely not match what you need.
CodePudding user response:
You cannot do that, because a pointer's contents has no meaning per se.
Usually, this problem can be tackled by replacing each pointer with either a fixed or variable-sized field, or adding "internal pointers" if the filesize and memory pointer sizes are compatible.
typedef struct
{
char* name;
int played;
int win;
} Player;
could become, in the file:
0000 4 bytes (played)
0004 4 bytes (win)
0008 2 bytes (length of name)
000A .. bytes (name)
And you would read it by fread()ing the first 10 bytes, converting them into a temporary structure similar to your Player:
typedef struct
{
int32_t played;
int32_t win;
int16_t playerLength;
} FilePlayer;
at that point you would allocate tmp.playerLength 1 bytes and read tmp.playerLength' worth of bytes from the file, adding a zero since C strings are zero-terminated, and assign this chunk of memory to newPlayer->name.
Or you could gather all fixed-size records at the beginning of your save-file, so they are easily readable in a loop, and add the variable-length strings at the end. Then, each fixed-record item that points to a variable-length object would have to become an offset into the file itself.
Or you could go for a human-parsable file, where each line of text is a field. This also allows easy inspection and manipulation of the file with third-party editors. In that case you could use ancillary functions to read int, char*, and so on, and:
record = malloc(sizeof Player);
if (NULL == record) {
fclose(fp);
return failure("memory error");
}
if (NULL == (record->name = readString(fp))) {
fclose(fp);
return failure("error reading name");
}
if (-1 == (record->played = readInt(fp))) {
fclose(fp);
return failure("error reading record");
}
if (-1 == (record->win = readInt(fp))) {
fclose(fp);
return failure("error reading record");
}
...
