Reputation: 197
I'm trying to save a board (which is a 2D array that has been dynamically allocated) into a file such that it can be later "loaded" by the user and they can use the board again. To save the board the user must input s filename
. To get the user input, I have an else if statement for the save command:
else if (playerMove.command == 's') {
char* fileName = NULL;
scanf(" %s", fileName);
implementSave(boardState, fileName);
Since we've only learned how to read/open/write to binary files in C, I tried to do the same for this in my implementSave function:
void implementSave(BoardState* boardState, char* fileName) {
FILE* file = fopen(fileName, "wb");
fwrite(fileName, sizeof(char), sizeof(boardState->board.theBoard), file);
fclose(file);
}
Note: I use some structs in my program; the struct board
contains char** theBoard, int numRows, int numCols, char blankSpace
.
However, this gives me an error when I try to run the save command. Can anyone point me in the right direction?
Upvotes: 1
Views: 2165
Reputation: 132909
This code
fwrite(fileName, sizeof(char), sizeof(boardState->board.theBoard), file);
means:
Write data to the file
file
. The data is found at the memory locationfileName
points to, the data consists out ofsizeof(boardState->board.theBoard)
items and every single item issizeof(char)
bytes big.
Does that sound right to you? I don't think so; it's already wrong that the data can be found at the position fileName
points to as you want to write the board data to the file and no the file name, right?
Also be careful with sizeof()
; sizeof()
cannot dynamically determine the content of some memory, it only knows the size of static memory. E.g.
char test[20];
size_t s = sizeof(test);
s
will be 20. But now consider this:
char test[20];
char * ptr = test;
size_t s = sizeof(ptr);
Now s
will be 4 or 8 as pointers are usually 4 bytes in size or 8 bytes in size. sizeof()
gives you the size of the pointer here, not the size of the memory the pointer points to. There is no way in C to obtain the size of a memory block a pointer points to, this size must always be known.
You say your board is char ** theBoard
, so theBoard is a pointer to an array of pointers to memory or characters.
theBoard -> [0] -> ['a1', 'b1', 'c1', 'd1', ... ]
[1] -> ['a2', 'b2', 'c2', 'd2', ... ]
[2] -> ['a3', 'b3', 'c3', 'd3', ... ]
:
In that case the creation code would need to look like that (assuming that [0], [1], ... are the rows):
theBoard = calloc(numberOfRows, sizeof(char *));
for (size_t i = 0; i < numberOfRows; i++) {
theBoard[i] = calloc(numberOfCols, sizeof(char));
}
If that is the case, you need to write your data row by row:
for (size_t row = 0; row < numberOfRows; row++) {
fwrite(boardState->board.theBoard[row], sizeof(char), numberOfCols, file);
}
Of course, assuming that all rows have have an equal number of cols.
If I can give you a tip, don't make the board char **
, just make it char *
, as it makes everything so much easier. See, if your board is 20x30 (20 rows, 30 cols), then you can define your board like this:
char * theBoard = calloc(numberOfRows * numberOfCols, sizeof(char))
Now you just have a single array, like this:
theBoard -> ['a1', 'b1', 'c1', 'd1', ... ,
'a2', 'b2', 'c2', 'd2', ... ,
'a3', 'b3', 'c3', 'd3', ... ,
:
]
How would you access a specific field? Very simple:
int row = 5;
int col = 8;
char field = theBoard[(row * numberOfCols) + col];
And then you can write the entire board in one call:
fwrite(theBoard, sizeof(char), numberOfRows * numberOfCols, file);
See, much easier. Also you can just free the entire board by calling free(theBoard);
whereas when using the char **
approach, you have to do this instead:
for (size_t i = 0; i < numberOfRows; i++) {
free(theBoard[i]);
}
free(theBoard);
Upvotes: 1
Reputation: 1071
You can not just write the double-pointer to file. You have to index each element in the first pointer and write the data out in chunks of rows (or columns, but I've demonstrated rows). To do this use a for loop. Note that (assuming the board is all you are saving to the binary file) the resulting file size should be numRow * numCol
bytes.
int i;
for (i = 0; i < BoardState->board.numRow; ++i) {
fwrite(BoardState->board.theBoard[i], sizeof(char), BoardState->board.numCol, file)
}
Reading ("loading") the file is done in reverse of the code above - note that it is unwise to only save the board to file - I suggest at the start of the file you save the number of rows and columns to make allocation of memory easy while reading (loading) the file.
So assuming you store the number of rows/columns in an int the above code then becomes
fwrite(&BoardState->board.numRow, sizeof(int), 1, file);
fwrite(&BoardState->board.numCol, sizeof(int), 1, file);
int i;
for (i = 0; i < BoardState->board.numRow; ++i) {
fwrite(BoardState->board.theBoard, sizeof(char), BoardState->board.numCol, file)
}
NOTE this has the possibility of introducing endianness problems. You can Google that if your interested.
Upvotes: 0