i've been working on a RPG in C language lately, and i've come accross some difficulties for saving / loading some datas from a text file.
Here is the (simplified to make it short) code :
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct player{
int id;
char* name;
};
void save(struct player* player)
{
FILE* fp = 0;
char* buffer = 0;
fp = fopen("./gamesave.txt", "w");
fclose(fp);
buffer = malloc(80);
memset(buffer, 0, 80);
snprintf( buffer, 80, "%d|%s|\n", player->id, player->name);
fp = fopen("./gamesave.txt", "a");
fputs(buffer, fp);
free(buffer);
fclose(fp);
}
struct player* load()
{
FILE* fp = 0;
char* buffer = 0;
struct player* player = malloc(sizeof(struct player));
char tokens[] = "|\n";
buffer = malloc (80);
memset(buffer, 0, 80);
fp = fopen("./gamesave.txt", "r");
if (fgets(buffer, 80, fp)) {
char temp[20];
player->id = atoi(strtok(buffer, tokens));
strcpy(temp, strtok(NULL, tokens));
player->name = temp;
printf("during load : name is %s\n", player->name);
}
fclose(fp);
free(buffer);
return player;
}
void main()
{
struct player* player = malloc(sizeof(struct player));
player->id = 5;
player->name = "Bobby";
printf("before save : my name is %s and id is %d\n", player->name, player->id);
save(player);
struct player* playerN = load();
printf("after load : name is %s and id is %d", playerN->name, playerN->id);
}
This is my latest try in doing this but i've tried many differents ways of doing it, but it almost always ends in the same problem : i can't seem to get the right values for strings although it works for integers.I can print the name of the player before save, during the load function, but after the load it just prints garbage (the id works just fine).
My guess is that it has to do with some kind of memory problem but that's it, couldn't solve it after hours and hours.
Thanks in advance.
You need to allocate space for player->name.
You are using automatic variable temp for this, and its lifetime ends when the function returns. Auto variables are on the stack.
So you are taking an address on the stack and keeping it as your object. This leaks a pointer to your current stack level because you return it. It's OK to use pointers to automatic variables, but only while that function instance is active, i.e., in it's life cycle and in functions it calls.
Related
So i'm writing this code for an assignment for school, and what I have to do is have two functions as they're written, with createMonster returning a dynamically allocated monster with data, and readMonsters returning an array of pointers to the monsters made in createMonster. I've been having trouble understanding how to get the functions and structs to cooperate, and this is what I have so far:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
typedef struct monster {
int id;
char *name;
char *element;
int population;
} monster;
monster* createMonster(char *name, char *element, int population)
{
monster *amonster = (monster*)malloc(sizeof(monster));
amonster->name = (char*)malloc(sizeof(char)*sizeof(name));
amonster->element = (char*)malloc(sizeof(char)*sizeof(element));
amonster->population = (int)malloc(sizeof(int)*sizeof(population));
amonster->name = name;
amonster->element = element;
amonster->population = population;
return amonster;
}
monster** readMonsters(FILE* infile, int *monsterCount)
{
char n[15], e[15];
int p;
monster **a_array = malloc(*monsterCount * sizeof(struct a*));
for (int i = 0; i < *monsterCount; i++) {
a_array[i] = malloc(sizeof(monster));
fscanf(infile,"%s %s %d",n, e, &p);
printf("%s %s %d\n", n, e, p);
a_array[i] = createMonster(n,e,p);
}
monster ***m = &a_array;
return *m;
}
int main(){
int monsterCount;
char name[15];
FILE *fp = fopen ( "in.txt", "r" );
fscanf(fp,"%d %s",&monsterCount,name);
//printf("test %d\n",monsterCount);
monster **mptr = readMonsters(fp,&monsterCount);
printf("%s %s %d\n", (mptr)[3]->name,(mptr)[3]->element,(mptr)[3]->population);
fclose(fp);
return 0;
}
With the input file being:
8 monsters
StAugustine Grass 12
Zoysia Grass 8
WholeWheat Bread 6
MultiGrain Bread 10
Rye Bread 10
Cinnamon Spice 5
Pepper Spice 10
Pumpkin Spice 30
However, when I run it, I can see it works as far as making them the first time, but when I try and go back to access the data it returns garbage for the strings, with this being my output:
StAugustine Grass 12
Zoysia Grass 8
WholeWheat Bread 6
MultiGrain Bread 10
Rye Bread 10
Cinnamon Spice 5
Pepper Spice 10
Pumpkin Spice 30
`7 w├?]╨# 10
w├?]╨# 10
I've tried rearranging the functions, pointers, etc. and have tried many different versions of these functions trying to follow guides online, but each time it either doesn't work at all or returns garbage. I'm looking for any help in understanding how to get this working or how it could be organized better since I will readily admit my experience with C is moderate at best.
The code you posted has the following issues:
The line
amonster->name = (char*)malloc(sizeof(char)*sizeof(name));
does not make sense. sizeof(name) is the size of the pointer, which is 32-bit or 64-bit, depending on your platform. However, you probably need to allocate more than that. You need to allocate strlen(name) + 1 bytes, because that is the length of the string that is passed to the function (including the null terminating character). The same also applies to the next line:
amonster->element = (char*)malloc(sizeof(char)*sizeof(element));
Also, the line
amonster->population = (int)malloc(sizeof(int)*sizeof(population));
does not make sense, because amonster->population is not a pointer. Why are you trying to store a pointer to dynamically allocated memory in it? You shouldn't need dynamic memory allocation here, because amonster->population is not a string, but a fixed-length variables, for which you have already allocated space, because it is part of the struct. Therefore, you can delete this line. All you need is the line amonster->population = population;, which you already have.
Additionally, the line
amonster->name = name;
does not do what you want. It does not copy the string, it only copies the pointer, i.e. the memory address. That way, you are copying a pointer which will be a dangling pointer by the time you return to the function main. In order to copy the actual string, you must write strcpy( amonster->name, name );. The same applies for the following line:
amonster->element = element;
The line
a_array[i] = malloc(sizeof(monster));
is unnecessary and only creates a memory leak. You have already allocated space for all structs in the function createMonster.
The lines
monster ***m = &a_array;
return *m;
are unnecessarily cumbersome and can be simplified to the following:
return a_array;
After applying all of these fixes, your code should look like this:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
typedef struct monster {
int id;
char *name;
char *element;
int population;
} monster;
monster* createMonster(char *name, char *element, int population)
{
monster *amonster = (monster*)malloc(sizeof(monster));
amonster->name = (char*)malloc(sizeof(char)*(strlen(name)+1)); //fixed
amonster->element = (char*)malloc(sizeof(char)*(strlen(element)+1)); //fixed
//amonster->population = (int)malloc(sizeof(int)*sizeof(population)); //redundant
strcpy( amonster->name, name ); //fixed
strcpy( amonster->element, element ); //fixed
amonster->population = population;
return amonster;
}
monster** readMonsters(FILE* infile, int *monsterCount)
{
char n[15], e[15];
int p;
monster **a_array = malloc(*monsterCount * sizeof(struct a*)); //what is struct a???
for (int i = 0; i < *monsterCount; i++) {
//a_array[i] = malloc(sizeof(monster)); //redundant
fscanf(infile,"%s %s %d",n, e, &p);
printf("%s %s %d\n", n, e, p);
a_array[i] = createMonster(n,e,p);
}
//monster ***m = &a_array; //removed
//return *m; //removed
return a_array;
}
int main(){
int monsterCount;
char name[15];
FILE *fp = fopen ( "in.txt", "r" );
fscanf(fp,"%d %s",&monsterCount,name);
//printf("test %d\n",monsterCount);
monster **mptr = readMonsters(fp,&monsterCount);
printf("%s %s %d\n", (mptr)[3]->name,(mptr)[3]->element,(mptr)[3]->population);
fclose(fp);
return 0;
}
As demonstrated here, your program now provides the correct output.
My C program is crashing and I am too new to figure it out. It's very simple so far and I imagine the code is enough to figure out what is going wrong.
I am simply trying to read a file line by line. I will double the memory of a structure once I am out of memory. If this is not enough information, I will give whatever else you need.
Thank you very much for any help, as I have been stuck for hours now.
/*
John Maynard
1000916794
7/15/2013
HW-06
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define N 100
struct course
{
char subject[11];
int catalogNum;
int sectionNum;
int enrollmentTotal;
int enrollmentCap;
};
void readFile(struct course *d, char* filename);
void double_array_size(struct course *d, int new_size);
int main(void)
{
char *filename = "hw06-data.csv";
struct course *d;
d = malloc( N * sizeof(struct course));
readFile(d, filename);
}
void readFile(struct course *d, char* filename)
{
FILE* fp;
char buffer[100];
int i = 0, array_size = 100;
struct course *temp;
if( ( fp = fopen(filename, "r") ) == NULL)
{
printf("Unabale to open %s.\n", filename);
exit(1);
}
fgets(buffer, sizeof(buffer), fp);
while( fgets(buffer, sizeof(buffer), fp) != NULL)
{
if (i == array_size)
{
array_size *= 2;
double_array_size(d, array_size);
printf("reached limit...increasing array to %d structures\n", array_size);
}
i++;
}
fclose( fp );
}
void double_array_size(struct course *d, int new_size)
{
struct course *temp;
temp = realloc(d, new_size * sizeof(struct course));
if(temp == NULL)
{
printf("unable to reallocate\n");
exit(1);
}
else
d = temp;
}
realloc() may return a different pointer than the original one but you assign that to temp only so the calling function still works with the original pointer afterwards. Change double_array_size() to return the new pointer returned by realloc() and call
d = double_array_size(d, array_size);
Furthermore you should always check the result fo malloc(), realloc() etc. They may return NULL if there is no more memory available
Combining Ingo's and codroipo's answers, you have to either return the new pointer from double_array_size, or you have to pass in a pointer to d so you can update the pointer from double_array_size
Realloc reallocates memory, so probably memory pointed by d will be released, so double_array_size has to edit d, you could try:
void double_array_size(struct course** d, int new_size){
*d = realloc(*d, new_size * sizeof(struct course));
.
.
.
}
Hey so im trying to attempt to read in a file, store it in a hash and then copy it. However i get the incompatible pointer type
struct hash_struct {
int id;
char name[BUFFER_SIZE]; /* key (string WITHIN the structure */
UT_hash_handle hh; /* makes this structure hashable */
};
int main(int argc, char *argv[] )
{
char *lines[80];
FILE* fp = fopen("file.txt","r");
if(fgets(*lines, BUFFER_SIZE, fp) != NULL)
{
puts(*lines);
// do something
}
fclose(fp);
const char **n;
char *names[1024];
strcpy(*names, *lines);
struct hash_struct *s, *tmp, *users = NULL;
int i=0;
for (n = names; *n != NULL; n++)
{
s = (struct hash_struct*)malloc(sizeof(struct hash_struct));
strncpy(s->name, *n,10);
s->id = i++;
HASH_ADD_STR( users, name, s );
}
HASH_FIND_STR( users, "joe", s);
if (s) printf("joe's id is %d\n", s->id);
printf("Hash has %d entries\n",HASH_COUNT(users));
/* free the hash table contents */
HASH_ITER(hh, users, s, tmp) {
HASH_DEL(users, s);
free(s);
}
return 0;
}
The code works when i initialize const char **n, *names = {array elements here};
But it doesnt work with the code i have. Please help.
lines is declared to be an array of char pointers, but doesn't allocate any space for the strings they point to. In your working version, the compiler took care of allocating space for each string.
Plus, you can't use strcpy to copy an array of 80 pointers to an array of 1024 pointers.
Instead, each line you read in needs space to be allocated for it to be read into; then the addresses of each of those can be assigned to an element of names. In fact, as #BLUEPIXY suggests, line should be an array of 80 chars, not an array of 80 pointers-to-chars. Or you could just malloc the space for each new line, and put the address of that line into names.
I need help solving the code below. After the code is compiled using gcc it can be run like ./compiledFile inputFile.txt It should take the inputFile.txt read it while allocating memory dynamically for each variable in this case name and courseID, but my code is not working. The place that I don't understand the most and need help is allocating memory, inserting data into structures and printing the data like the example given below. By the look of this code you could tell that I am a newbie to c and dynamic memory allocation and structure in all.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct people
{
char* name[10];
char* courseID[15];
int grade;
};
void printData(struct people student[], int count);
int main(int argc, char* argv[])
{
FILE *in_file;
char buffer[30];
char *token, *del=",";
int count=0;
struct people student[20];
if(( in_file = fopen(argv[1], "r")) == NULL)
{
printf("unable to open the file");
}
while (fgets(buffer, sizeof(buffer), in_file))
{
student = malloc(sizeof(struct people));
token = strtok(buffer, del);
strcpy(student[count].name, token);
count++;
}
fclose(in_file);
printData(student, count);
}
void printData(struct people student[], int count)
{
int i;
for(i=0; i<count; i++)
{
printf("%s", student[i].courseID);
if (strcmp((student[i].name, student[i].courseID) > 0))
{
printf("%s %s", student[i].name, student[i].grade)
}
}
}
the data.txt file has the following content separated by a comman:
John,MATH 1324,90
David,SCI 1401,88
Omondi,MATH 1324,89
David,MATH 1324,90
when printed out it should look like the following:
MATH 1324
John 90
Omondi 89
David 90
SCI 1401
David 88
first of all, it would be great if you could also share what is the actual output or error you get while running this program.
most of the time dynamic memory allocation is used when we do not know the actual size of data elements, but here you have already fixed the size of struct people student as 20
struct people student[20];
this is absolutely fine, but then you do malloc in while loop
student = malloc(sizeof(struct student);
you have already alloted 20 locations using array declaration, now malloc is not required.
if you want to use dynamic memory allocation using pointers for learning purpose then you should first declare student as pointer to type struct people
struct people* student;
allocate memory dynamically in while loop
student=(struct people*) malloc(sizeof(struct people));
then access it
*(student+count)
hope this helps, if you still have doubts/problems edit the question and include the output/error you get while compiling/running this program.
Several issues with the Question code...
1) Definition of main():
int main(int argc, char* argv[])
It must return an integer. Add a return statement at the end of main(), and make a proper "CLEANUP" section:
printData(student, count);
CLEANUP:
if(in_file)
fclose(in_file);
return(0);
}
2) Better handling of fopen() error condition:
if(( in_file = fopen(argv[1], "r")) == NULL)
{
printf("unable to open the file");
goto CLEANUP;
}
And, initialize the in_file pointer:
int main(int argc, char* argv[])
{
FILE *in_file = NULL;
3) Next, the exact definition of student needs to be established. Is it a static array, or is a pointer to a dynamically allocated array? I will assume that you would like to use a dynamic array (given the question text). However, this assumption conflicts with the following line, which defines student as a static array:
struct people student[20];
Change it to:
struct people *student = NULL;
4) Now, the following question code allocates a new (separate) chunk of memory for each student:
student = malloc(sizeof(struct people));
However, what is needed is all the student records in one array, in the same chunk of memory. So, what is needed is to expand a chunk of memory to include student records as they are read, like this:
while (fgets(buffer, sizeof(buffer), in_file))
{
void *tmp = realloc(student, sizeof(struct people) * (count + 1));
if(NULL == tmp)
{
printf("realloc() failed.\n");
goto CLEANUP;
}
student = tmp;
token = strtok(buffer, del);
5) Take a look at the people structure:
struct people
{
char* name[10];
char* courseID[15];
int grade;
};
It appears that the question code has some difficulty when it comes to pointers & arrays. The code is attempting to define the name and courseID fields as both pointers, and arrays. Given that the question is to do with dynamically allocating stuff, I elect to go that direction. Hence, this structure should be changed to the following:
struct people
{
char *name;
char *courseID;
int grade;
};
6) So, each time through the loop, the student name will be placed in allocated storage, and pointed to by the .name field. So, change this:
token = strtok(buffer, del);
strcpy(student[count]->name, token);
count++;
}
to this:
token = strtok(buffer, del);
student[count].name = strdup(token);
count++;
}
7) I don't understand the intent of this line:
if (strcmp((student[i].name, student[i].courseID) > 0))
I am inclined to eliminate it.
8) The following line has flaws:
printf("%s %s", student[i].name, student[i].grade)
Change it to this to print the integer grade (and don't forget the ending semicolon):
printf("%s %d\n", student[i].name, student[i].grade);
The '\n' makes the output look better, one record per line.
9) Since student is a pointer to dynamically allocated memory (not a static array), change this:
void printData(struct people student[], int count)
to this:
void printData(struct people *student, int count)
10) Now, finish the task of parsing the data; from this:
token = strtok(buffer, del);
strcpy(student[count].name, token);
count++;
}
to this:
token = strtok(buffer, del);
student[count].name = strdup(token);
token = strtok(NULL, del);
student[count].courseID = strdup(token);
token = strtok(NULL, del);
student[count].grade = strtol(token, NULL, 10);
count++;
}
11) Now, to make life easier, sort the array. First by courseID, then by name:
...
count++;
}
/** Sort the array by coursID, then by name. **/
qsort(student, count, sizeof(*student), CmpStudentRecs);
printData(student, count);
...
Which will require an additional "Compare Student Recs" function:
int CmpStudentRecs(const void *recA, const void *recB)
{
int result = 0;
struct people *stuRecA = (struct people *)recA;
struct people *stuRecB = (struct people *)recB;
/** First compare the courseIDs **/
result=strcmp(stuRecA->courseID, stuRecB->courseID);
/** Second (if courseIDs match) compare the names **/
if(!result)
result=strcmp(stuRecA->name, stuRecB->name);
return(result);
}
12) Some finishing touches with the printData() function:
void printData(struct people *student, int count)
{
int i;
char *courseID = "";
for(i=0; i<count; i++)
{
if(strcmp(courseID, student[i].courseID))
{
printf("%s\n", student[i].courseID);
courseID = student[i].courseID;
}
printf("\t%s %d\n", student[i].name, student[i].grade);
}
}
Finished. Output:
SLES11SP2:~/SO> ./test data.txt
MATH 1324
David 90
John 90
Omondi 89
SCI 1401
David 88
SLES11SP2:~/SO>
SPOILER
Change the definition of people to:
struct people
{
char name[10];
char courseID[15];
int grade;
};
This assumes that name won't be longer than 9 characters and coursID won't be longer than 14 characters. If that is not true, change them accordingly.
The line:
student = malloc(sizeof(struct student);
is wrong in couple of ways.
student is already declared to be an array of people. You cannot assign it to point to memory allocated by malloc.
struct student is not a type.
That line can be removed.
The line
strcpy(student[count].name, token);
can cause problems if the length of token is longer than 10 (or whatever size you choose for name in people). The safer thing to do is use strncpy.
strncpy(student[count].name, token, 10);
student[count].name[9] = '\0';
You have not set the value of courseID anywhere. Yet, you are trying to print it in printData. You are doing the same thing for grade. You need to update the processing of input lines to set those correctly.
Change the while loop to:
while (fgets(buffer, sizeof(buffer), in_file))
{
token = strtok(buffer, del);
strncpy(student[count].name, token, 10);
student[count].name[9] = '\0';
token = strtok(NULL, del);
strncpy(student[count].courseID, token, 15);
student[count].courseID[14] = '\0';
token = strtok(NULL, del);
student[count].grade = atoi(token);
count++;
}
There are couple of syntax errors in printData. However, fixing those syntax errors does not take care of your printing requirements. It will be easier to print the data in the order that you want to if you sort the data. The following functions will help you sort the data.
int compareStudent(const void* ptr1, const void* ptr2)
{
struct people* p1 = (struct people*)ptr1;
struct people* p2 = (struct people*)ptr2;
return (strcmp(p1->courseID, p2->courseID));
}
void sortData(struct people student[], int count)
{
qsort(student, count, sizeof(struct people), compareStudent);
}
You can call sortData before calling printData or call sortData first in printData. The logic for printing the data needs to be updated a little bit. Here's an updated printData.
void printData(struct people student[], int count)
{
int i;
int j;
sortData(student, count);
for(i=0; i<count; ++i)
{
printf("%s\n", student[i].courseID);
printf("%s %d\n", student[i].name, student[i].grade);
for ( j = i+1; j < count; ++j )
{
if (strcmp(student[i].courseID, student[j].courseID) == 0)
{
printf("%s %d\n", student[j].name, student[j].grade);
}
else
{
i = j-1;
break;
}
}
}
}
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct people {
char name[10];//char *name[10] is array of pointer
char courseID[15];
int grade;
};
void printData(struct people student[], int count);
int main(int argc, char* argv[]){
FILE *in_file;
char buffer[30];
char *token, *del=",";
int count=0;
struct people student[20];
if((in_file = fopen(argv[1], "r")) == NULL){
printf("unable to open the file");
return -1;//It is not possible to continue the process
}
while (fgets(buffer, sizeof(buffer), in_file)){
//student = malloc(sizeof(struct people));//It is by securing an array already
token = strtok(buffer, del);
strcpy(student[count].name, token);
token = strtok(NULL, del);
strcpy(student[count].courseID, token);
token = strtok(NULL, del);
student[count].grade = atoi(token);
count++;
}
fclose(in_file);
printData(student, count);
}
int cmp(const void *a, const void *b){
const char *x = ((const struct people*)a)->courseID;
const char *y = ((const struct people*)b)->courseID;
return strcmp(x, y);
}
void printData(struct people student[], int count){
qsort(student, count, sizeof(struct people), cmp);//sort by courseID
char *prev = "";
int i;
for(i=0; i<count; i++){
if(strcmp(prev, student[i].courseID)!=0){
prev = student[i].courseID;
printf("\n%s\n", prev);
}
printf("%-9s %d\n", student[i].name, student[i].grade);
}
}
My C program is crashing and I am too new to figure it out. It's very simple so far and I imagine the code is enough to figure out what is going wrong.
I am simply trying to read a file line by line. I will double the memory of a structure once I am out of memory. If this is not enough information, I will give whatever else you need.
Thank you very much for any help, as I have been stuck for hours now.
/*
John Maynard
1000916794
7/15/2013
HW-06
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define N 100
struct course
{
char subject[11];
int catalogNum;
int sectionNum;
int enrollmentTotal;
int enrollmentCap;
};
void readFile(struct course *d, char* filename);
void double_array_size(struct course *d, int new_size);
int main(void)
{
char *filename = "hw06-data.csv";
struct course *d;
d = malloc( N * sizeof(struct course));
readFile(d, filename);
}
void readFile(struct course *d, char* filename)
{
FILE* fp;
char buffer[100];
int i = 0, array_size = 100;
struct course *temp;
if( ( fp = fopen(filename, "r") ) == NULL)
{
printf("Unabale to open %s.\n", filename);
exit(1);
}
fgets(buffer, sizeof(buffer), fp);
while( fgets(buffer, sizeof(buffer), fp) != NULL)
{
if (i == array_size)
{
array_size *= 2;
double_array_size(d, array_size);
printf("reached limit...increasing array to %d structures\n", array_size);
}
i++;
}
fclose( fp );
}
void double_array_size(struct course *d, int new_size)
{
struct course *temp;
temp = realloc(d, new_size * sizeof(struct course));
if(temp == NULL)
{
printf("unable to reallocate\n");
exit(1);
}
else
d = temp;
}
realloc() may return a different pointer than the original one but you assign that to temp only so the calling function still works with the original pointer afterwards. Change double_array_size() to return the new pointer returned by realloc() and call
d = double_array_size(d, array_size);
Furthermore you should always check the result fo malloc(), realloc() etc. They may return NULL if there is no more memory available
Combining Ingo's and codroipo's answers, you have to either return the new pointer from double_array_size, or you have to pass in a pointer to d so you can update the pointer from double_array_size
Realloc reallocates memory, so probably memory pointed by d will be released, so double_array_size has to edit d, you could try:
void double_array_size(struct course** d, int new_size){
*d = realloc(*d, new_size * sizeof(struct course));
.
.
.
}