I have this struct.
Struct Wizard{
char name[50];
int maxHealth;
int attackMin, attackRange;
int curHealth, winCount;
};
And I need to make a function save the stats to a txt file.
I was given this bit of code to start it.
int saveWiz(struct Wizard * wiz)
{
File * fp = fopen("champion.txt","w");
char * buff = malloc(100);
sprintf(buff,... );
sprintf(buff,...);
//Todo: replace ... with appropriate string, using tags like %d for variables
fputs(buff,fp);
fclose(fp);
}
Any help would be much appreciated. I think I am supposed to use the wiz pointer to access the struct but I'm not exactly sure.
I don't know if I understand exactly what you are asking but I'm guessing it's this:
You have a wiz variable of type struct Wizard * . You access the elements like this : wiz->name , wiz->maxHealth and so on,
wiz->name is equivalent to (*wiz).name . So you'll have :
int saveWiz(struct Wizard * wiz) //assuming you return 1 for success 0 for failure
{
File * fp = fopen("champion.txt","w");
if(!fp) {
//file opening failed
return 0;
}
char * buff = malloc(200); // 200 to be sure
if(buff) {
sprintf(buff,"%s",wiz->name);
sprintf(buff,"%d %d %d %d %d" , wiz->maxHealth, wiz->attackMin, wiz->attackRange, wiz->curHealth, wiz->winCount);
fputs(buff,fp);
fclose(fp);
free(buff);
return 1;
}
else {
//memory allocation failed
return 0;
}
}
Also as Martin James pointed out it is best to free dynamically allocated memory after you're done using it ( otherwise causes ugly memory leaks on long-running programs) . On that note , it's also good to check if the file opening was successful as well as the memory allocation. Also another thing I noticed is the number of elements in the buff array , 100 might not be enough ( considering an int is 32 bit (10 digits) and the name has a full 50 characters, along with the spaces it might cause an overflow).
Related
Good Morning,
I have just finished my source code that contains a function to take the elements of an array and write it to a file and another function after that that copies the elements in the newly created file to a new array. Note that I will be frank here, this is homework for me, and I am finished. However, the weird thing is that when I perform my own tests, it works, but when I submit it to an online correctness checker, it seems to fail with large numbers.
My code is here:
#include<stdio.h>
#include<stdlib.h>
#include<math.h>
typedef struct {
int* data;
unsigned int len;
} intarr_t;
int intarr_save_binary( intarr_t* ia, const char* filename )
{
FILE *fp;
int* tmp = malloc(sizeof(int)*ia->len+1);
tmp[0] = ia->len;
int i=0;
for(i = 0; i<ia->len+1; i++)
{
tmp[i+1] = ia->data[i];
}
fp = fopen( filename , "wb" );
fwrite(tmp, sizeof(int), ia->len+1, fp );
fclose( fp );
return 0;
}
intarr_t* intarr_load_binary( const char* filename )
{
int i = 0;
intarr_t *tmp = malloc( sizeof( intarr_t ) );
if(tmp==NULL)
{
return NULL;
}
FILE *fp;
fp = fopen( filename , "r" );
int len;
fread( &len, sizeof(int), 1, fp );
tmp->len = len;
tmp->data = malloc( ( len ) * sizeof( int ) );
if(tmp->data == NULL)
{
return NULL;
}
fread(tmp->data, sizeof(int), len, fp);
fclose(fp);
return tmp;
}
So, the first function takes a char array containing the file name and an structure which I have defined at the very top and the gist of it is that I make a new array of length +1 and store the length of the original array passed to me by the struct in first index and the remaining indice will store the elements. Then I store all the stuff in it into a file. After that I call my SECOND function and open the very same file and read the first thing in the file which is the length and then I use that length to read the remaining things in the file to a new array. After that I return a pointer to it.
The hint the online code check gives me is that it aborted possibly due to a failed assert or length of original array does not match length of new array.
**The only major problem I personally see is that in my typedef I have unsigned int len and in my functions I use int len. Or that I am not freeing the first tmp array I make using malloc in first function. but I don't think that should affect the result..
Without better diagnostics from the online code check that you are using, it is really hard to pinpoint the error. If you suspect that the online code check fails for larger numbers (array sizes?) you may want to extend your own tests to better cover that scenario. If you suspect that the error is a segmentation fault (which I think could be the case, see below) you can also try to run your program in a tool like valgrind, which can tell you if you have any illegal memory access. A bug like that could work by accident on your local system but fail on a different system.
That being said, I think you have a bug in intarr_save_binary when you access the memory that you allocate with malloc. Look closely at how many bytes of memory you allocate. Say that ia->len is 3 and sizeof(int) is 4, in this case you are allocating 3*4 + 1 = 13 bytes, clearly not enough to hold 4 ints. In addition you seem to have an Off-by-one error in the following loop.
So i need help returning a pointer of string arrays from a function obtained from a file. The strings being no larger than 10.
Input file:
3
102
A3B
50
The first number being how many strings I need, the following numbers being what I need to store in the strings.
The function:
char ** buildArray(){
int x, i;
fscanf(fp, "%d", &x);
char answer[x][10];
for(i=0; i<x; i++){
fscanf(fp, "%s", answer[i]);
}
return answer;
}
I can get the values to be stored on the string array 'answer' properly, i just cant return it properly.
Main Function:
int main() {
char * answers;
fp = fopen("data.txt", "r");
if(fp == NULL){
printf("Could not find file.\n");
exit(EXIT_FAILURE);
}
answers = buildAnsArray();
printf("%s", answer[1]); //used as a test to see if i passed the array of strings correctly
fclose(fp);
return 0;
}
in the main function when i try and print a value it just ends up crashing, or printing weird symbols
Assuming this is some flavor of C, you are trying to return a pointer to a variable, answer, which was automatically allocated inside a function, which means it gets automatically deallocated when you exit that function. So either create answer outside of this function and pass it in to be filled, or allocate the space for it explicitly so it can live once the function returns.
As #Scott Hunter pointed out, when you exit the function buildArray, answer goes out of scope, and the memory it occupied is free to be used by another variable. If you want to allocate the memory within buildArray, then you will need to use malloc. A starting point would be:
char *answer;
answer = malloc(x * 10 * sizeof(*answer));
But, using malloc does require you to pay attention to memory and manage it appropriately. This means:
1) Checking that malloc was successful in allocating memory. If it fails it returns Null, so you can add
if(answer == NULL){
//Error Code here
}
2) Freeing the memory when you are done. This should also be done safely. I would suggest the following just before fclose
if(answer != NULL)
{
free(answer);
}
3) Performing your own handling the double indexing yourself. This means that answer[i] becomes either answer + i*10 or &(answer[i*10])
Also, not related to your question, but important to notice:
1) You have a type mismatch between your definition char *answer in main and returning a char** from buildArray. Turning on -Wall and -Wextra on your compiler should warn you about these types of things. And you should try to clean up all of the warnings that are generated. They tend to mean that you either made a subtle mistake, or are using a bad coding practice that you should get out of the habit of, before it creates a major debugging headache.
2) You appear to be using fp as a global variable. You should be trying to avoid global variables as much as possible. There may be times when they are necessary, but you should think about that long and hard before comitting to it.
3) You (correctly) checked that fopen was successful. But, you didn't check that fscanf was successful. You should get in this habit as a failed read from file won't automatically generate a runtime error. You will just get strange results when it uses whatever bits were already in memory for your logic later on.
sample code
#include <stdio.h>
#include <stdlib.h>
#define STRING_SIZE 10
#define S_(x) #x
#define S(x) S_(x)
char (*buildAnsArray(FILE *fp))[STRING_SIZE+1]{
int x, i;
char (*answer)[STRING_SIZE+1];
fscanf(fp, "%d", &x);
answer = malloc(x * sizeof(*answer));
for(i=0; i<x; i++){
fscanf(fp, "%" S(STRING_SIZE) "s", answer[i]);//fscanf(fp, "%10s", answer[i]);
}
return answer;
}
int main(void) {
char (*answers)[STRING_SIZE+1];
FILE *fp = fopen("data.txt", "r");//error proc omit
answers = buildAnsArray(fp);
printf("%s\n", answers[1]);//A3B
fclose(fp);
free(answers);
return 0;
}
This question already has answers here:
Writing and reading (fwrite - fread) structures with pointers
(3 answers)
Closed 8 years ago.
I tried to write and read from a file with pointers in structures. But when I read from file I see some garbage value. I am using GCC 4.7.2 on Linux. Need some help.
Read:
//read from a file
#include<stdio.h>
typedef struct
{
char* name;
char* phone;
}LISTING;
int main(void)
{
LISTING phoneList[14];
FILE * fp = NULL;
fp = fopen("/media/Study/PhoneDirectory.dat","rb");
if(fp == NULL)
printf("Error opening file!!!");
fseek(fp,0,SEEK_SET);
if(fread(&phoneList[1],sizeof(LISTING),1,fp)==1)
printf("%s %s",phoneList[1].name,phoneList[1].phone);
fclose(fp);
return 0;
}
And write:
//Write to file
#include<stdio.h>
typedef struct
{
char* name;
char* phone;
}LISTING;
int main(void)
{
LISTING phoneList[2];
FILE * fp = NULL;
fp = fopen("/media/Study/PhoneDirectory.dat","wb");
phoneList[1].name = "Santosh";
phoneList[1].phone = "9657681798";
if(fwrite(&phoneList[1],sizeof(LISTING),1,fp)==1)
printf("inserted");
fclose(fp);
return 0;
}
Pointers are only meaningful in the application process that they originate from. If you write them to a file, as you're doing here, the values you read back will be meaningless — they will most likely point to uninitialized memory, or to memory which is being used for something else entirely.
You will need to come up with another way of writing this data to a file.
The problem you have is equivocating between char* and char[]. You can certainly assign a string literal to a char*, but you need to understand what the contents of a LISTING structure contain, and how you want to serialize and deserialize data to a file.
It does not make sense to save pointers from one process and read them into another process, so you probably want to save the contents (what a pointer points at). You want to store two values, (name, phone) to the file. Since you likely want to store the literal name and literal phone, let us consider what the file might look like:
roast duck|212-333-4444
peking duck|411-511-61111
duck soup|314-222-3333
free duck|800-111-2222
...
You need functions to serialize and deserialize your data. Since your LISTING type is pointers, you will need to allocate appropriate space for those values, as you read them, and you need functions (methods) to read serialized data from a file and write serialized data to a file.
Reading (you will need to allocate enough space),
int
listing_read(FILE*fp, LISTING* listing)
{
char name_buffer[100];
char phone_buffer[100];
if(!fp) return(-1);
if(!listing) return(-2);
int res = fscanf(fp,"%s|%s\n",name_buffer,phone_buffer);
if( !res ) {
//handle error here
}
//careful here, you cannot free if you didn't malloc/strdup
if(listing->name) free(listing->name);
if(listing->phone) free(listing->phone);
listing->name = strdup(name_buffer);
listing->phone = strdup(phone_buffer);
return(0);
}
Writing (you will need to provide proper formatting),
int
listing_write(FILE*fp, LISTING* listing)
{
if(!fp) return(-1);
if(!listing) return(-2);
fprintf(fp,"%s|%s\n",listing->name,listing->phone);
return(0);
}
Here is how you need to modify your code,
//read from a file
#include<stdio.h>
typedef struct
{
char* name;
char* phone;
}LISTING;
int main(void)
{
LISTING phoneList[14];
FILE* fp = NULL;
if( !(fp = fopen("/media/Study/PhoneDirectory.dat","rb")) ) {
printf("Error opening file!!!");
exit(1);
}
fseek(fp,0,SEEK_SET);
if( listing_read(fp,&phoneList[0]) >= 0 ) {
printf("%s %s",phoneList[0].name,phoneList[0].phone);
}
fclose(fp);
return 0;
}
And here is how writing the file would change,
//Write to file
#include<stdio.h>
typedef struct
{
char* name;
char* phone;
}LISTING;
int main(void)
{
LISTING phoneList[14];
FILE* fp = NULL;
if( !(fp = fopen("/media/Study/PhoneDirectory.dat","wb")) ) {
printf("error, cannot write file\n");
exit(1);
}
phoneList[0].name = "Santosh";
phoneList[0].phone = "9657681798";
if( listing_write(fp,&phoneList[0])>=0) {
printf("inserted");
}
fclose(fp);
return 0;
}
Note that in you writing program you assign the string literals "Santosh" and "9657681798" to the LISTING members name and phone. Though legal to do, you need a better understanding of what C does here. C takes the address of these C-string constants and assigns those addresses to the phonelist[1].name and phonelist[1].phone member pointers.
Consider that if you did this assignment,
phoneList[0].name = "Santosh";
phoneList[0].phone = "9657681798";
You have assigned the pointers to constant strings to your structure members.
But if you were to allocate space (for example, using strdup()),
phoneList[0].name = strdup("Santosh");
phoneList[0].phone = strdup("9657681798");
You have allocated space for the strings, assigning independent locations for these member elements. Which is is more likely what you want to do.
Note that I used phonelist[0] since C has zero-based arrays.
printf("%s %s",phoneList[1].name,phoneList[1].phone);
The above statement invokes undefined behaviour.
Since the pointers name & phone of struct object phoneList[1] are not initialized dereferencing them invokes UB. In your case they are throwing out garbage values but it could have lead to a crash also.
To fit your case of reading the contents of file and storing it in the struct objects use getline function to read them row-wise(assuming that all the details are stored line-wise) and then dynamically allocate the memory for char pointers then assign them to the read value. But, this approach leads to lot of memory management which is error prone.
First, I declare variables before the main() function:
// Files
FILE *density_model_file;
char *density_model_filename;
float *density_array;
Next, I open the FILE * for reading and allocate memory for the density array:
density_model_file = open4read(density_model_filename, program_name);
density_array = allocator(density_model_size, sizeof(float));
Up to this point, the debugger shows everything is working fine. Here
is the step that I can't seem to fix, where I am attempting to load
data into the calloc'd array:
density_array = floatfromfile(sizeof(float), density_model_size, density_model_file, density_model_filename);
The density_array has a NULL value after this step for some reason.
Here is the code for this function (contained in a separate .c file).
I have bolded the part where I think the issue exists:
float * floatfromfile(unsigned long int entrysize, int numentries, FILE *inputfile, const char *filename)
{
/* Declaration of density model array size variable */
int numbytes;
**void *temparray = 0;
/* Writes the gravity model to file */
numbytes = (int)fread(temparray, entrysize, numentries, inputfile);**
/* Checks that the forward model file has a sufficient number of entries */
if (numbytes == numentries)
{
printf("loaded %i values from %s using fread()\n", numbytes, filename);
return((float *)temparray);
}
else
{
printf("ERROR: %i data points read from %s of %i needed\n", numbytes, filename, numentries);
return((float *)temparray);
}
}
Any insight would be much appreciated. I think the issue might be that calloc() returns a pointer to a void array. I can provide the other functions if needed.
You seem to have a misunderstanding about how pointers work. What you need to do is pass density_array into floatfromfile as an argument.
What you are doing instead is overwriting the pointer to your allocated memory, with the return value from floatfromfile. That return value is always NULL because that's what you assigned it to (as temparray).
fread expects to be able to write its results into an allocated memory block. But you're giving it temparray which has not been allocated—in fact its value is 0. So you're giving fread the address 0 to write into, which is likely to cause the program to crash. Instead you need to pass your allocated pointer density_array at this point.
I have a structure with the following definition:
typedef struct myStruct{
int a;
char* c;
int f;
} OBJECT;
I am able to populate this object and write it to a file. However I am not able to read the char* c value in it...while trying to read it, it gives me a segmentation fault error. Is there anything wrong with my code:
//writensave.c
#include "mystruct.h"
#include <stdio.h>
#include <string.h>
#define p(x) printf(x)
int main()
{
p("Creating file to write...\n");
FILE* file = fopen("struct.dat", "w");
if(file == NULL)
{
printf("Error opening file\n");
return -1;
}
p("creating structure\n");
OBJECT* myObj = (OBJECT*)malloc(sizeof(OBJECT));
myObj->a = 20;
myObj->f = 45;
myObj->c = (char*)calloc(30, sizeof(char));
strcpy(myObj->c,
"This is a test");
p("Writing object to file...\n");
fwrite(myObj, sizeof(OBJECT), 1, file);
p("Close file\n");
fclose(file);
p("End of program\n");
return 0;
}
Here is how I am trying to read it:
//readnprint.c
#include "mystruct.h"
#include <stdio.h>
#define p(x) printf(x)
int main()
{
FILE* file = fopen("struct.dat", "r");
char* buffer;
buffer = (char*) malloc(sizeof(OBJECT));
if(file == NULL)
{
p("Error opening file");
return -1;
}
fread((void *)buffer, sizeof(OBJECT), 1, file);
OBJECT* obj = (OBJECT*)buffer;
printf("obj->a = %d\nobj->f = %d \nobj->c = %s",
obj->a,
obj->f,
obj->c);
fclose(file);
return 0;
}
When you write your object, you're writing the pointer value to the file instead of the pointed-to information.
What you need to do is not just fwrite/fread your whole structure, but rather do it a field at a time. fwrite the a and the f as you're doing with the object, but then you need to do something special with the string. Try fwrite/fread of the length (not represented in your data structure, that's fine) and then fwrite/fread the character buffer. On read you'll need to allocate that, of course.
Your first code sample seems to assume that the strings are going to be no larger than 30 characters. If this is the case, then the easiest fix is probably to re-define your structure like this:
typedef struct myStruct{
int a;
char c[30];
int f;
} OBJECT;
Otherwise, you're just storing a pointer to dynamically-allocated memory that will be destroyed when your program exits (so when you retrieve this pointer later, the address is worthless and most likely illegal to access).
You're saving a pointer to a char, not the string itself. When you try to reload the file you're running in a new process with a different address space and that pointer is no longer valid. You need to save the string by value instead.
I would like to add a note about a potential portability issue, which may or may not exist depending upon the planned use of the data file.
If the data file is to be shared between computers of different endian-ness, you will need to configure file-to-host and host-to-file converters for non-char types (int, short, long, long long, ...). Furthermore, it could be prudent to use the types from stdint.h (int16_t, int32_t, ...) instead to guarantee the size you want.
However, if the data file will not be moving around anywhere, then ignore these two points.
The char * field of your structure is known as a variable length field. When you write this field, you will need a method for determining the length of the text. Two popular methods are:
1. Writing Size First
2. Writing terminal character
Writing Size First
In this method, the size of the text data is written first, followed immediately by the data.
Advantages: Text can load quicker by block reads.
Disadvantages: Two reads required, extra space required for the length data.
Example code fragment:
struct My_Struct
{
char * text_field;
};
void Write_Text_Field(struct My_Struct * p_struct, FILE * output)
{
size_t text_length = strlen(p_struct->text_field);
fprintf(output, "%d\n", text_length);
fprintf(output, "%s", p_struct->text_field);
return;
}
void Read_Text_Field(struct My_STruct * p_struct, FILE * input)
{
size_t text_length = 0;
char * p_text = NULL;
fscanf(input, "%d", &text_length);
p_text = (char *) malloc(text_length + sizeof('\0'));
if (p_text)
{
fread(p_text, 1, text_length, input);
p_text[text_length] = '\0';
}
}
Writing terminal character
In this method the text data is written followed by a "terminal" character. Very similar to a C language string.
Advantages: Requires less space than Size First.
Disadvantages: Text must be read one byte at a time so terminal character is not missed.
Fixed size field
Instead of using a char* as a member, use a char [N], where N is the maximum size of the field.
Advantages: Fixed sized records can be read as blocks.
Makes random access in files easier.
Disadvantages: Waste of space if all the field space is not used.
Problems when the field size is too small.
When writing data structures to a file, you should consider using a database. There are small ones such as SQLite and bigger ones such as MySQL. Don't waste time writing and debugging permanent storage routines for your data when they have already been written and tested.