First things first - my header (.h) file looks like this:
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
typedef struct riff_list {
char id[4];
uint32_t cksize;
char subid[4];
uint8_t *buf;
} list_t;
void write_u32_le(uint32_t n, FILE *f)
{
uint8_t i, buf[4];
for (i = 0; i < 4; i++)
{
buf[i] = n & 0xFF;
n >>= 8;
}
fwrite(buf, 1, 4, f);
}
void init_list_chunk(list_t list, char *cktype)
{
memcpy(list.id, "LIST", 4);
list.cksize = 0;
memcpy(list.subid, cktype, 4);
}
void write_list_chunk(list_t list, FILE *f)
{
fwrite(list.id, 1, 4, f);
write_u32_le(4 + list.cksize, f);
fwrite(list.subid, 1, 4, f);
fwrite(list.buf, 1, list.cksize, f);
}
Now, the problem is with a basic test program trying to write something to a file. This comes out as expected:
int main()
{
FILE *f;
list_t list;
memcpy(list.id, "LIST", 4);
list.cksize = 0;
memcpy(list.subid, "TEST", 4);
f = fopen("C:\\listtest.bin", "wb");
fwrite(list.id, 1, 4, f);
write_u32_le(4+list.cksize, f);
fwrite(list.subid, 1, 4, f);
list.buf = malloc(4);
fwrite(list.buf, 1, 4, f);
free(list.buf);
fclose(f);
}
While this just produces a 4096-byte file with junk, with no recognizable parts of what is expected (not even "LIST"):
int main()
{
FILE *f;
list_t list;
init_list_chunk(list, "TEST");
list.buf = malloc(4);
list.cksize += 4;
f = fopen("C:\\listtest.bin", "wb");
write_list_chunk(list, f);
free(list.buf);
fclose(f);
}
Why is this? Why does the latter approach not work as expected?
The expected output should be something like:
LIST
ssss
TEST
xxxx
Where "ssss" is the size and "xxxx" is any random data (4 bytes).
I have seen approaches that instead passes structs as pointers to them (&my_struct_var) and accesses the members in a function by deferencing them as my_struct_var->member, but can I pass the struct as it is?
Calling a function with out = func(in); means that if the function modifies the contents of in those modifications will only be visible within the function. When the function returns the calling function will still have the same contents in the variable in. Doing a call by value could be described as the called function gets its own copy of the variable.
You have two options to solve your problem:
1) As you already suggested yourself, your function init_list_chunk could take a pointer to your structure. By then modifying the structure the pointer points to those modifications will be usable also for the calling function.
2) Instead of returning void your init_list_chunk could return the modified structure. Something like: list = init_list_chunk(list, "TEST"); or even list = init_list_chunk("TEST"); as there is no useful input information within list at the time of the function call.
Related
I've been wondering if there's a way to do a function that saves game state but in transparent way. It's not a question about actual saving (I know how to save to file etc), it's about writing it in a clear, simple and understandable way.
So far I've came to three ideas (remember, I wish it could be a function or some easy thing like savegame()), none is perfect:
Put every variable I want to save into a struct like:
struct state{
int hp;
pos position;
int x;
int y;
...
};
...
if(savegame==1) {
state game;
game.hp=hp;
game.position=position;
game.x=x;
game.y=y;
...
savegame(game);
}
as you can see, it's not looking any good, it takes so many lines to do just a saving: what if I would like to save like 40 variables? What if I want to do saving in like 5-6 places in a code?
Make my function long as snake (this gives a problem with loading it back, so for example I also put a loadgame func):
void savegame(int hp, pos position, int x, int y,...) // too long
{ ... }
void loadgame(int &hp, pos &position, int &x, int &y,...) // too long
{ ... }
...
int main()
{
...
savegame(hp, pos, x, y, ...) // too long
...
loadgame(hp, pos, x, y, ...) // too long
}
again, if it would have 40 variables inside(and i also would like to call savegame or loadgame like 5-6 times), it would take like 4 lines of code each(i just skip an idea to put it in 1 line)
Start my program with struct containing game state, and make all calculations, actually all game on that struct
struct gamestate{
int hp;
pos position;
int x;
int y;
}
...
int main()
{
struct gamestate s;
s.x++;
...
}
but this leads to a problem where everytime I make calculations I have to add this 's' at beggining.
Conclusion:
My idea was to maybe change second option just to reduce every call of savegame(...); to simple savegame();, is there a way to do it? Without define?
Here is a dynamic way to do something similar to what you describe in your 1st option, i.e. track a game state using pointer to struct but that also stores and recovers that data to and from a binary file...
The following .c and .h files illustrate the idea that you can use a compound struct, i.e. one containing several other structs, that at any one time contain all of the values representing states in your program. This is very easily passed via a function(s) with very few arguments (prototyped eg. as char *GetState(state *in, const char *filespec); and char *SetState(state *out, const char *filespec); ) which in turn would write or read the contents of the struct into/out of a binary buffer. I have used this method to store and retrieve multiple sets of state data within files.
Note, the structs have random fields that of course you will modify as needed, but the idea is that you can pass a single pointer value that points to all the state data, in every function where state date is updated, or needs to be stored.
so_SaveGameState.c
#include <so_SaveGameState.h>
unsigned char *pByteA;
GAME_STATE game = {{{"jim", "C:\\ico1.ico", {10, 120, 3}}, {"joe", "C:\\ico2.ico", {80, 10, -5}},{"larry", "C:\\ico3.ico", {15, -45, -45}},{"sue", "C:\\ico4.ico", {-100, -45, 45}}}, ENVR_3};
GAME_STATE *pGame = NULL;
int main(void)
{
pGame = &game;//point to populated memory
printf("Player 3 position\nx = %d\ny = %d\nz = %d\n", game.plyr[2].pos.x, game.plyr[2].pos.y, game.plyr[2].pos.z);
//example function that changes game state
UpdatePlayerPosition(&pGame, 2);
printf("Player 3 position\nx = %d\ny = %d\nz = %d\n", game.plyr[2].pos.x, game.plyr[2].pos.y, game.plyr[2].pos.z);
UpdatePlayerPosition(&pGame, 2);
printf("Player 3 position\nx = %d\ny = %d\nz = %d\n", game.plyr[2].pos.x, game.plyr[2].pos.y, game.plyr[2].pos.z);
UpdatePlayerPosition(&pGame, 2);
printf("Player 3 position\nx = %d\ny = %d\nz = %d\n", game.plyr[2].pos.x, game.plyr[2].pos.y, game.plyr[2].pos.z);
//prepare an instance of game state for storeing
(const GAME_STATE *)pByteA = &game;
int len1 = sizeof(game);
BOOL status = storeState("C:\\tempextract\\binFileStruct.bin", pByteA, len1);
//recover a stored state
unsigned char *buf = recoverState("C:\\tempextract\\binFileStruct.bin");
GAME_STATE *game_2 = (GAME_STATE *)buf;
free(game_2);
return 0;
}
unsigned char * recoverState(const char *filespec)
{
size_t sz = 0;
int n = 0;
unsigned char *binBuf = NULL;
FILE *fp = fopen(filespec, "rb");
if(fp)
{
fseek(fp, 0L, SEEK_END);
sz = ftell(fp);
fseek(fp, 0L, SEEK_SET);
rewind(fp);
binBuf = calloc(sz, sizeof(*binBuf));
n = fread(binBuf, sizeof(unsigned char), sz, fp);
fclose(fp);
}
if(n == sz)
{
return binBuf;
}
else
{
return NULL;
}
}
int storeState(const char *filespec, const unsigned char *buf, size_t sz)
{
int count = 0;
FILE *fp = fopen(filespec, "wb");
if(fp)
{
count = fwrite(buf, sizeof(unsigned char), sz, fp);
fclose(fp);
}
return (count == sz) ? 1 : 0;
}
void UpdatePlayerPosition(GAME_STATE **game, int player)
{
static int x=0, y=0, z=0;
static BOOL toggle = TRUE;
toggle = (toggle == 1) ? -1 : 1;
srand(clock());
//using fake assignment here
//i.e. you would have other criteria to set actual position
x += toggle * rand()%300;
y += toggle * rand()%300;
z += toggle * rand()%300;
(*game)->plyr[player].pos.x = x;
(*game)->plyr[player].pos.y = y;
(*game)->plyr[player].pos.y = z;
}
so_StoreGameState.h
typedef enum {//environment
ENVR_1, //bad weather
ENVR_2, //hill
ENVR_3, //pit
ENVR_4, //angry birds
ENVR_5, //enemy guard
MAX_OBST
}ENVR_TYPE;
typedef struct {
int x;
int y;
int z;
}POS;
typedef struct {
ENVR_TYPE envir;
//...
}ENVIR;
typedef struct {
char name[20];
char iconFile[260];
POS pos;
//...
}PLAYER;
typedef struct {
PLAYER plyr[4];
ENVIR env;
//...
}GAME_STATE;
extern GAME_STATE game;
unsigned char * recoverState(const char *filespec);
int storeState(const char *filespec, const unsigned char *buf, size_t sz);
I am new to C language somehow and I am trying here to call a function "func" in main function but it gives me compiling error. I tried to search for examples similar to this situation in Google but stil getting errors.
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
struct str {
int value;
uint32_t ptr_next;
};
void func(int arg, char* arr[]);
int main() {
int Arg;
char* Arr[1];
func(Arg, *Arr[1]);
return 0;
}
void func(int arg, char* arr[]) {
int list;
struct str r;
FILE* file = fopen(arr[1], "rb");
do {
list = fread(&r, sizeof(struct str), 1, file);
if (list > 0)
printf("%d ", r.value);
fseek(file, r.ptr_next, 0);
}
while ((r.ptr_next != 0) && (list > 0));
}
The question is how can I call functions by value in C language?
C only supports calling functions by value, calling by reference was added in C++ and uses the & symbol.
The value you are passing to the function is a location in memory, a pointer. If you want to hand to the function a copy of the data at that memory location you'll need to make a copy for it.
// Assuming that Arg and Arr are initialized.
char* Arr2[]; // Make a storage place for the char**.
Arr2 = malloc(Arg*sizeof(char*)); // Make the right number of char*s in it
for(int e=0; e<Arg; e++) { // For each elem in the main array:
Arr2[e]=malloc(sizeof(char)*strlen(Arr[e])); // Make space for the chars,
strcpy(Arr2[e],Arr[e]); // And copy them.
}
Side note
You haven't initialized Arg or Arr in main. I suspect that you might have meant the command line parameters.
int main(int Arg, char* Arr[])
As part of a college assignment, I'm trying to do a simple C app, using Win32 for GUI programming and writing my own dynamic linked list for storing data. Since i could use it for other things later, I'm trying to write a generic list, with "built in" functions for reading and writing data to a file. Now here's my problem
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct tagA{
int t_int;
float t_float;
char t_char;
char t_vchar[30];
} A;
typedef struct tagB{
void *data;
struct tagB *prox;
} B;
int main(){
A dA = {1, 1.5, 'a', "teste123"};
B dB = {&dA, NULL};
dB.data = &dA;
B dB2 = {0};
FILE *fp;
fp = fopen("Data.txt", "w");
size_t dSize = sizeof(dA);
printf("Struct size: %d", dSize);
if(fp == NULL){
printf("nope");
}else{
fwrite(&dB.data, dSize, 1, fp);
}
fclose(fp);
fp = fopen("Data.txt", "r");
dB2.data = malloc(dSize);
fread(&dB2.data, dSize, 1, fp);
printf("\n\n%s", ((A*)dB2.data)->t_vchar);
}
This is the code I'm trying to work out.
The writing works just fine, and I just have to make it to a separate function that receives the pointer to the data struct and its size.
But the reading is off... Once read and printed to the console, the t_int shows a really large number, the t_float has nothing, and so the char and string...
Also, I know its not the best written, most efficient and safe code, but it's just a prototype, something that came into my mind...
Your problem is you are writing the address of &dB.data with fwrite instead of dB.data itself (it is already a pointer). For example:
fwrite(dB.data, dSize, 1, fp);
will resolve your problem (with the like corresponding change to fread).
void *data;
It's not useful to declare data as void*. Declare it as A *data; instead.
fwrite(&dB.data, dSize, 1, fp);
...
fread(&dB2.data, dSize, 1, fp);
dB2.data is a pointer, just use dB2.data. See example below. For better readability you can declare ptr_data instead of data
typedef struct tagA {
int t_int;
float t_float;
char t_char;
char t_vchar[30];
} A;
typedef struct tagB {
A *data;
struct tagB *prox;
} B;
int main() {
A dA = { 1, 1.5, 'a', "teste123" };
B dB = { 0 };
dB.data = &dA;
B dB2 = { 0 };
FILE *fp;
fp = fopen("Data.txt", "w");
size_t dSize = sizeof(dA);
printf("Struct size: %d", dSize);
if(fp == NULL) {
printf("nope");
}
else {
fwrite(dB.data, dSize, 1, fp);
}
fclose(fp);
fp = fopen("Data.txt", "r");
dB2.data = malloc(dSize);
fread(dB2.data, dSize, 1, fp);
printf("\n\n%s\n", dB2.data->t_vchar);
return 0;
}
Your error is at fwrite(), fread(). The first param of them is the pointer which points at the real data you want to write/read.
Since you want to read/write a struct A, the pointer must actually point at struct A, not struct A*.
Just replace
fwrite(&dB.data, dSize, 1, fp);
fread(&dB2.data, dSize, 1, fp);
with
fwrite(dB.data, dSize, 1, fp);
fread(dB2.data, dSize, 1, fp);
I tried many combinations but really nothing worked. It's been long enough so I decided to write this issue.
I just want an array of pointers to structures so I could later easiely sort it by swaping the addresses. I have a function to get data from file and write to array. Unfortunately, it's impossible for me to read this data outside the function.
My last attempt (I deleted file operations as those are not the issue):
Header.h:
#pragma once
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct {
int32_t year;
uint16_t month;
uint16_t day;
} Date;
typedef struct {
char name[16];
Date date;
uint32_t number;
} Player;
size_t readData(Player** players);
Source.c:
#include "Header.h"
size_t readData(Player** players) {
players = NULL;
players = realloc(players, sizeof(Player*) * 1);
players[0] = malloc(sizeof(Player));
strcpy(players[0]->name, "asd");
printf("$ %s\n", players[0]->name);//OK
return 1;
}
int main() {
Player **players = NULL;
uint32_t elemCount, i;
elemCount = readData(players);
printf("> %s", players[0]->name);//BUM - ERROR!
return 0;
}
I'm using Visual Studio Community 2015. I know it's not so much for coding in C but I managed to configure the solution and it doesn't seem to be the thing imo.
I will be very thankful for help. Yet, I'd like to see it as a remake of my code. I tried some other answers to questions on StackOverFlow like this but they didn't help.
If a parameter of a function is not only read and should be an output too, you have to pass a pointer to your data to the function. If your data type is Player** your paramter type has to be Player ***, so the list of players itselfe is able to be changed inside the function.
size_t readData(char* fname, Player*** players) {
// ^ players is a input and output parameter
Player **tempPlayers = *players; // local list of players
tempPlayers = realloc(tempPlayers, sizeof(Player*) * 1);
tempPlayers[0] = malloc(sizeof(Player));
strcpy(tempPlayers[0]->name, "asd");
printf("$ %s\n", tempPlayers[0]->name);//OK
*players = tempPlayers; // write back list of players to paramter
return 1;
}
int main() {
Player **players = NULL;
uint32_t elemCount, i;
char *fileName = NULL;
elemCount = readData(&players);
// ^
printf("> %s", players[0]->name);//BUM - ERROR!
return 0;
}
If you don't want to use *** you can do it like this:
Player* *readData(char* fname, Player** players, size_t *size) {
players = realloc(players, sizeof(Player*) * 1);
players[0] = malloc(sizeof(Player));
strcpy(players[0]->name, "asd");
printf("$ %s\n", players[0]->name);//OK
*size = 1;
return players;
}
players = readData( fileName, players, &elemCount );
You're passing the pointer to the function by value; the original players in main is not changed. This is no different from doing:
#include <stdio.h>
void func(int x)
{
x = 4;
}
int main()
{
int x = 0;
func(x);
printf("%d\n", x); // zero
}
I'm assuming it's the pointers that got you confused, so you should see your mistake now.
To make the modification visible outside of readData, you will need to use three levels of indirection:
size_t readData(Player*** players) {
*players = malloc(sizeof(Player*) * 1);
*(players)[0] = malloc(sizeof(Player));
strcpy((*players)[0]->name, "asd");
printf("$ %s\n", (*players)[0]->name);//OK
return 1;
}
int main() {
Player **players = NULL;
uint32_t elemCount, i;
elemCount = readData(&players);
printf("> %s", players[0]->name); // prints 'asd'
return 0;
}
There is no point using realloc when you know the pointer being passed is always NULL, so I've changed it to malloc instead.
And on a side note: Visual Studio is perfectly suited to C development, and implements parts of C99 and even C11 (historically MSVC was always stuck at C89). You can just use the .c extension for your source files, and this will make the compiler assume the code is C, or you can explicitly set this in the property pages.
parameter passed to readData() must be the address of the caller's pointer 'players'
each of the references in readData() to players must take that into account.
Othewise the caller's pointer 'player' will not be updated.
Then the main() function, call to printf() is trying to output from address 0, resulting in undefiined behaviour, leading to a seg fault event.
in the main() function, if you insert:
printf( "player value; %p\n", (void*)players);
before the current call to printf() you will see the players pointer still contains NULL.
There is a function that asks the user which text file to open, opens it and then passes the array of structures that was passed into the function along with the file pointer to another function that reads in data from file into the structure. The array structure for testing purposes only has the value char name[25];. I can assign one line at a time from the file to the same structure index all I want but when I try an increment it I get a segmentation fault no matter what approach I've taken.
The structure has been type defined as well.
The code is:
void oSesame(char usrTxt[], int len, FILE * pFile, Country * p)
{
pFile = fopen(usrTxt, "rw");
if(pFile != NULL)
{
readIn(pFile, &p);
}
else
{
printf("Error opening %s , check your spelling and try again.\n", usrTxt);
}
}
void readIn(FILE * pfile, Country ** p)
{
int count = 0;
int i = 0;
for(i = 0; i<3; i++)
{
fgets((*p[i]).cntName, MAX_COUNTRY_LENGTH, pfile);
}
fclose(pfile);
}
The header file:
//Header.h
#define COUNTRY_MAX 10
#define MAX_COUNTRY_LENGTH 25
#define MAX_CAPITAL_LENGTH 25
typedef struct country
{
char cntName[MAX_COUNTRY_LENGTH];
char capName[MAX_CAPITAL_LENGTH];
double population;
}Country;
int ask(char * usrTxt);
void oSesame(char usrTxt[], int len, FILE * pFile, Country * p);
void readIn(FILE * pFile, Country ** p);
The main code:
#include <stdio.h> //for testing within main
#include <string.h> //for testing within main
#include "headers.h"
int main()
{
int len;
FILE * fileP;
char UI[25];
Country c[10];
Country * ptr;
ptr = c;
len = ask(UI);
oSesame(UI, len, fileP, ptr);
return 0;
}
You are passing Country** for some reason and then handling it as *p[index]. This is wrong. You could use (*p)[index] but the correct way is not to take a reference to the Country* in the first place.
The way you're doing it means you have a pointer to pointer to Country. When you index that you are moving to next pointer to pointer, which is not the same as moving to the next pointer. Undefined behaviour happens.