I have a problem passing structure pointer to a function with fscanf().
Here's my struct:
typedef struct {
int health;
int y;
int z;
} save;
in main I have:
save save = { 0 }; //init with zeros
loadgame(&save);
health = save.health;
and my function loadgame() looks like that:
bool loadgame(save* save) {
FILE* fptr;
fptr = fopen("savegame.txt", "r");
if (fptr == NULL)
return 0;
fscanf(fptr, "health= %d", save->health);
return 1;
};
my savegame.txt file has line:
health= 5
and my function doesn't change save->health, after finish of this function my health equals zero.
I tried to do my function like that and it also has the same solution in function loadgame() I changed
fscanf(fptr, "health= %d", save-health);
to
fscanf(fptr, "health= %d", &(save-health));
fscanf(fptr, "health= %d", save->health); -> fscanf(fptr, "health= %d", &save->health);
Here you have working verison to play with https://godbolt.org/z/5CuZwR
As in my example always check the result of the scanf
Looks like your fscanf is passing the value of save->health instead of it's address.
You need to do
fscanf(fptr, "health= %d", &save->health);
Since -> has precedence over & this will give you the address of the health member.
fscanf needs a pointer so it knows where to save the value.
Other than that your code has a bunch of other small issues.
I have addressed those in the comments below:
#include <stdio.h>
#include <stdbool.h>
typedef struct {
int health;
int y;
int z;
}save_tp;
//using the same name for a typedef and a variable is a bad idea;
//typedef struct{...} foo; foo foo; prevents you from declaring other varibles of type foo
//as the foo variable overshadows the foo typedef name;
//better give types a distinct name
bool loadgame(save_tp* save){
FILE* fptr;
fptr = fopen("savegame.txt", "r");
if (fptr == NULL)
return false;
bool r = (1==fscanf(fptr, "health= %d", &save->health)); //needs an address + may fail
fclose(fptr); //need to close the file or you'd be leaking a file handle
return r;
} //shouldn't have a semicolon here
int main(void)
{
save_tp save={0};
if (!loadgame(&save)) perror("nok");
else printf("ok, health=%d\n", save.health);
}
Related
I'm trying to pass an array of structs to a function which fills them with data.
When I try to compile the code I am told that there is an error:
In function 'main':
error: expected expression before 'Robot_t'
loading_Profiles (Robot_t RobotInfo[]);
I am not sure what I am missing?
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <math.h>
#include <string.h>
typedef struct {
int Robot_Number;
char Robot_Name[30];
int Year_Manufacturer;
float Top_Speed;
float Mass;
float Best_Score;
} Robot_t;
void loading_Profiles();
int main()
{
Robot_t RobotInfo[5];
loading_Profiles (Robot_t RobotInfo[]);
int i;
for (i = 0; i < 50; i++) {
printf("%d\t\t%s\t\t%d\t\t\t%.2f\t\t%.2f\t\t%.2f\n",
RobotInfo[i].Robot_Number, RobotInfo[i].Robot_Name,
RobotInfo[i].Year_Manufacturer, RobotInfo[i].Top_Speed,
RobotInfo[i].Mass, RobotInfo[i].Best_Score);
}
return 0;
}
void loading_Profiles()
{
int Counter = 0;
int i;
Robot_t RobotInfo[5];
FILE *ROBOTtxt = fopen("Robot.txt", "r");
if (ROBOTtxt == NULL) {
perror("an error occured during the loading of the file\n");
exit(-1);
}
for (i = 0; i < 50; i++) {
char LineNumber[100] = "";
fgets(LineNumber, 100, ROBOTtxt);
sscanf(LineNumber, "%d %s %d %f %f %f",
&RobotInfo[i].Robot_Number,
RobotInfo[i].Robot_Name,
&RobotInfo[i].Year_Manufacturer,
&RobotInfo[i].Top_Speed,
&RobotInfo[i].Mass,
&RobotInfo[i].Best_Score);
Counter++;
if (feof(ROBOTtxt)) {
break;
}
}
if (ferror(ROBOTtxt)) {
perror("an error has occured");
exit(-1);
}
fclose(ROBOTtxt);
}
There are several issues with your program. The obvious one is that your function prototypes do not match:
void loading_Profiles()
should be
void loading_Profiles(Robot_t *robots)
in both the declaration and definition.
The array Robot_t RobotInfo[5] in main, and the Robot_t RobotInfo[5] in loading_Profiles do not refer to the same array. They are separate arrays, local to each function. You need to pass the array from main to the loading_Profiles function, which should then modify the array.
Your code also contains various size errors. You are defining an array of 5 elements, and then trying to read and write up to 50 elements. Beyond the mismatch, you need to think about what happens if your file contains less lines than expected.
Counter is unused. As are the return values of some functions that can indicate status / errors:
fgets already partially indicates if it has reached EOF by returning NULL.
sscanf returns the numbers of conversions that took place, which can be used to make sure a partial set of data wasn't stored.
Here is a rewritten example that showcases how to pass arrays around, fill them to a maximum, and utilize the return values of stdio functions. Notice how the type signature of load_robot_profiles matches exactly between the declaration, definition, and invocation of the function.
#include <stdio.h>
#include <stdlib.h>
typedef struct {
int number;
char name[32];
int year_manufactured;
float top_speed;
float mass;
float best_score;
} Robot;
size_t load_robot_profiles(Robot *, size_t, const char *);
int main(void) {
Robot robots[5];
size_t length = load_robot_profiles(robots, 5, "robots.txt");
for (size_t i = 0; i < length; i++) {
Robot *r = &robots[i];
printf("%d\t%s\t%d\t\t%.2f\t%.2f\t%.2f\n",
r->number, r->name, r->year_manufactured,
r->top_speed, r->mass, r->best_score);
}
}
size_t load_robot_profiles(Robot *robots, size_t size, const char *fname) {
size_t i = 0;
FILE *file = fopen(fname, "r");
char input[128];
if (!file)
return 0;
while (i < size && fgets(input, sizeof input, file)) {
Robot *r = &robots[i];
if (6 == sscanf(input, "%d %s %d %f %f %f",
&r->number, r->name, &r->year_manufactured,
&r->top_speed, &r->mass, &r->best_score))
i++;
}
fclose(file);
return i;
}
Also note: Defining a type with a _t suffix is ill-advised, as eventually you will brush up against a conflict with an existing POSIX type, or other standard.
Your definition and declaration of the function void loading_Profiles() don't include any arguments, but you're calling it with an argument: loading_Profiles (Robot_t RobotInfo[]);.
You need to change the function to accept Robot_t RobotInfo[] as an argument and then modify the RobotInfo[] array.
The function signature should be like that:
void loading_Profiles(Robot_t* RobotInfo);
Also, there is no need to redeclare the Robot_t RobotInfo[5] inside your loading_Profiles function, since it is already passed by the function call.
Do I need a * in front of my struct array (in the function arguments) when passing the struct to a function by reference? The reason I am thinking we don't is because an array is essential going to pass the address in which the first object is located.
I feel like I just got lucky my code is working:
#include <stdio.h>
struct member {
char lastName[30];
char gender;
int age;
};
void readAndUpdate(struct member *people[]);
// begin main function
int main(void){
struct member *people[30];
readAndUpdate(people);
} // end main function
// begin function which reads a .dat file and propogates the array with the data in the .dat file
void readAndUpdate(struct member *people[]){
}
I worked on my code some more from the help of the commentors and I have the following which works properly. I accidentally created an array of pointers.
#include <stdio.h>
#define MAXPEOPLE 3
struct member {
char lastName[30];
char gender;
int age;
};
void readAndUpdate(struct member *person, size_t maxpeople);
void populateDatFile();
void displayMembers(struct member *person, size_t maxpeople);
// begin main function
int main(void){
struct member people[2];
populateDatFile(); // program will first populate the .dat file with the given specs
readAndUpdate(people, MAXPEOPLE);
printf("The data was read and input as follows:\n\n");
displayMembers(people, MAXPEOPLE);
} // end main function
// function which displays the entire array of struct members
void displayMembers(struct member *person, size_t maxpeople){
int i=0;
for (i=0;i<3;i++){
printf("%s ", person[i].lastName);
printf("%c ", person[i].gender);
printf("%d ", person[i].age);
printf("\n");
}
} // end displayMembers function
// function which loads the .dat file with hardcoded structs
void populateDatFile(){
struct member person1={"Gates", 'M', 60};
struct member person2={"Jobs", 'M', 55};
struct member person3={"Jane", 'F', 45};
FILE *file;
file = fopen("question3.dat","w");
if(file == NULL)
printf("question3.dat cannot be opened!\n");
else
printf("question3.dat was opened successfully.\n");
fprintf(file, "%s %c %d\n", person1.lastName, person1.gender, person1.age);
fprintf(file, "%s %c %d\n", person2.lastName, person2.gender, person2.age);
fprintf(file, "%s %c %d\n", person3.lastName, person3.gender, person3.age);
fclose(file);
} // end function populateDatFile
// begin function which reads a .dat file and propogates the array with the data in the .dat file
void readAndUpdate(struct member *person, size_t maxpeople){
int i=0;
FILE *file;
file = fopen("question3.dat","r");
if(file == NULL)
printf("question3.dat cannot be opened!\n");
else
printf("question3.dat was opened successfully.\n");
fscanf(file, "%s", &person->lastName);
fscanf(file, " %c", &person->gender);
fscanf(file, "%d", &person->age);
fscanf(file, "%s", &person[1].lastName);
fscanf(file, " %c", &person[1].gender);
fscanf(file, "%d", &person[1].age);
fscanf(file, "%s", &person[2].lastName);
fscanf(file, " %c", &person[2].gender);
fscanf(file, "%d", &person[2].age);
fclose(file);
} // end function readAndUpdate
The code you have is 'OK, but…'. And there are some quite significant "buts" to be worried about.
The first issue is whether what you wrote is what you intended to write. You've defined an array of pointers to structures, but not initialized it at all. You may have intended to define an array of structures rather than an array of pointers, which then alters the rest of the discussion. For the time being, I'm taking what you wrote as "it's OK — that's what I intended to write".
You pass the array to the function correctly. The function has no idea how big an array you passed, though. You should get into the habit of telling functions how big the array is.
You don't reference the array inside the function. That's not all bad; you haven't defined the memory that each of the pointers in the array is pointing to. You'll presumably dynamically allocate the items as you add them, and then reference them correctly using arrows -> and not dots .:
void readAndUpdate(size_t max, struct member *people[max])
{
for (size_t i = 0; i < max; i++)
{
people[i] = malloc(sizeof(*people[i]));
if (people[i] == NULL)
…handle error appropriately…
strcpy(people[i]->lastName, "Unknown");
people[i]->gender = 'N'; // Neuter — unknown
people[i]->age = 0; // Babies only
}
}
int main(void)
{
struct member *people[30] = { NULL };
readAndUpdate(30, people);
return 0;
}
If the number of entries isn't actually fixed, then the readAndUpdate() function should report how many were initialized.
I didn't intend to create an array of pointers.
OK; then the rules of the game change:
void readAndUpdate(size_t max, struct member people[max])
{
for (size_t i = 0; i < max; i++)
{
strcpy(people[i].lastName, "Unknown");
people[i].gender = 'N'; // Neuter — unknown
people[i].age = 0; // Babies only
}
}
int main(void)
{
struct member people[30] = { { "", 0, 0 } };
readAndUpdate(30, people);
return 0;
}
The structures are already allocated, and initialized to all bytes zero. The code in the function uses . instead of -> to reference members. The * goes from the variable and parameter definitions.
#include <stdio.h>
void output(score_t s);
#define STDNT 20
struct score {
char name[STDNT];
int s1;
int s2;
int s3;
};
typedef struct score score_t;
int main(void)
{
score_t new;
FILE *afile;
afile = fopen("students.txt", "r");
printf("N NAME S1 S2 S3\n");
printf("== =========== == == ==\n");
output(afile, new);
printf("-- ----------- -- -- --\n");
fclose(afile);
return (0);
}
void output(FILE *ifile, score_t s) {
int i;
i=0;
while(fscanf(ifile, "%s %d %d %d", s.name, &s.s1, &s.s2, &s.s3) != EOF) {
i++;
printf("%0d%10s%2d %2d %2d\n", i, s.name, s.s1, s.s2, s.s3);
}
}
I am attempting to read a name and three scores from a file and print it into a chart using a function and a structure. When I run this code I get this error message:
error: unknown type name ‘score_t’
Does anyone see what I'm doing wrong?
You are using the type before defining it. C programs are read strictly from top to bottom.
To fix this, move the method declaration below the type definition.
I want to read data which is written in fprintf(fp,"%s %s %s\n", p->name,p->surname,p->tc); format. I created struct patients **p in with
struct patients **create_array(struct patients **ptr,int length){
int i;
ptr=(struct patients **)malloc(length*sizeof(struct patients));
return ptr;
}
function above create array of pointers and give it to main. Main calls read_file() function to read data from file which is written in known format. But my data is not filled when i try to print them in main it prints meaningless things. I thought problem in reading data that's why i put only reading function. What is my problem? All suggestons are welcome.
#include<stdio.h>
struct patients
{
int importance;
char name[10], surname[10], tc[11];
};
FILE *file_opening(char x[])
{
return (fopen(x,"w+"));
}
writing_file (FILE *fp, struct patients *p)
{
fprintf(fp,"%s %s %s\n", p->name,p->surname,p->tc);
}
struct patients **read_file (FILE *fp,struct patients **p)
{
int i=-1;
do{
i++;
}while(fscanf(fp,"%s %s %s",p[i]->name,p[i]->surname,p[i]->tc) !=EOF);
return p;
}
void show_all_patients(struct patients **p, int start_index, int length){
int i;
for(i=start_index;i<length;i++)
printf("%s %s %s",p[i]->name,p[i]->surname,p[i]->tc);
}
struct patients **create_array(int length){
return (struct patients **)malloc(length*sizeof(struct patients));
}
int menu(void){
int choice;
printf("1)take patient\n2)show all patients\n3)exit");
scanf("%d",&choice);
return choice;
}
main(){
int i=0,j,choice,cured_patient=0,length=1;
FILE *fp;
struct patients **ptr;
char file_name[40]="patient_list.txt";
ptr=create_array(length);
fp=file_opening(file_name);
ptr=read_file(fp,ptr);
do{
choice=menu();
if(choice==1){
printf("%s %s %s\n",ptr[i]->name,ptr[i]->surname,ptr[i]->tc);
i++;
}
else if(choice==2){
show_all_patients(ptr,i,length);
}
}while(choice!=3);
for(j=i;j<length;j++)
writing_file(fp,ptr[j]);
fclose(fp);
}
Problems with the code:
There is no return value of writing_file. Add void as the return type.
void writing_file (FILE *fp, struct patients *p)
{
fprintf(fp,"%s %s %s\n", p->name,p->surname,p->tc);
}
You need #include <stdlib.h>.
Without that the return value of malloc is assumed to be an int, which can result in strange problems.
Syntax errors:
Given your definition of ptr, the lines
ptr=create_array(length);
and
ptr=read_file(fp,ptr);
are syntactically invalid. I get the following errors with gcc:
soc.c: In function ‘main’:
soc.c:58:7: error: assignment to expression with array type
ptr=create_array(length);
^
soc.c:60:7: error: assignment to expression with array type
ptr=read_file(fp,ptr);
If your compiler does not report errors on those two lines, it's time to use a different compiler.
Reading and writing to the same file
You are using the same FILE* to read from and write to. It's not clear whether you meant to do that or it was an error on your part.
When you open the file using:
return (fopen(x,"w+"));
the contents of the file are truncated. See http://en.cppreference.com/w/c/io/fopen for more info. Pay special attention to the table under Parameters. It says:
"w+" | write extended | Create a file for read/write | destroy contents | create new
If you want to just read the data from the file, use
return (fopen(x,"r"));
If you want to read the data from the file and write back the data to it, read it first using the above mode, close the file, then reopen it using:
return (fopen(x,"w"));
Handling the array of patients
I think you should use:
FILE* fp = NULL;
int length=10;
struct patients *ptr = NULL;
ptr=create_array(length);
fp=file_opening(file_name);
read_file(fp, ptr, length);
Adjust the rest of your code appropriately.
Add code to deallocate memory
Every call to malloc should have a corresponding call to free. I would add
void delete_array(struct patients *ptr)
{
free(ptr);
}
and call it from main before the end of the function.
Things that I would change.
Return value and input arguments of create_array.
The function can also be simplified to:
struct patients* create_array(int length)
{
return malloc(length*sizeof(struct patients));
}
Return value and arguments to read_file
struct patients* read_file (FILE *fp ,struct patients *p)
{
int i=-1;
do
{
i++;
} while(fscanf(fp, "%s %s %s",p[i].name, p[i].surname, p[i].tc) != EOF);
return p;
}
Make sure that you provide the maximum number of characters to be read. Otherwise, you might end up reading more than the array are capable of holding.
Change
} while(fscanf(fp, "%s %s %s",p[i].name, p[i].surname, p[i].tc) != EOF);
to (given the size of the arrays in your struct)
} while(fscanf(fp, "%9s %9s %10s",p[i].name, p[i].surname, p[i].tc) != EOF);
This is my new question since I'm so newbie!!! :)
I have a text file like:
3
55.33 44.27 STN1
77.26 33.44 STN2
22.11 23.12 STN5
I would like to read it in c.
so I have defined a structure in a file header entitle read_stn.h for the file like:
#include <stdio.h>
#include <sys/file.h>
typedef struct station
{
double lat, lon;
char name[5];
} station;
and try to read the file using following codes
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "read_stn.h"
void read_stn(char filename[], station **sta,int *station_no)
{
FILE *fp;
int i;
char sta_str[5];
float longitude, latitude;
fp = fopen(filename,"r");
fscanf(fp,"%d",station_no);
printf("%d\n", *station_no);
*sta = (station*)malloc(*station_no*sizeof(station *));
for(i=0;i<*station_no;i++)
{
fscanf(fp,"%f %f %s", &longitude, &latitude, sta_str);
printf("%f %f %s\n", longitude, latitude, sta_str);
sta[i]->lon=(double)longitude;
sta[i]->lat=(double)latitude;
strcpy(sta[i]->name,sta_str);
}
fclose(fp);
}
and a main routine:
#include <stdio.h>
#include <stdlib.h>
#include "read_stn.h"
int main()
{
station *sta;
int i,stn_no;
read_stn("station.dat",&sta,&stn_no);
for(i=0;i<stn_no;i++)
{
printf("%d %s %f %f\n",i+1, sta[i].name, sta[i].lon, sta[i].lat);
}
free(sta);
return 1;
}
but when I tried to read file I got segmentation core dump. Is there any error in my files. I think there is something error in defining the pointer to pointer member assigning. Would you help me?
You weren't far off, a couple errors.
This:
*sta = (station*)malloc(*station_no*sizeof(station *));
should be this:
*sta = malloc(*station_no * sizeof(station));
since you want to allocate memory for several structs, not for several struct pointers.
Then this:
sta[i]->lon=(double)longitude;
sta[i]->lat=(double)latitude;
strcpy(sta[i]->name,sta_str);
should be:
(*sta)[i].lon = (double) longitude;
(*sta)[i].lat = (double) latitude;
strcpy((*sta)[i].name, sta_str);
since your dynamic array is stored at *sta, not at sta.
Full working version:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct station {
double lat, lon;
char name[5];
} station;
void read_stn(char filename[], station ** sta, int *station_no) {
FILE *fp;
int i;
char sta_str[5];
float longitude, latitude;
fp = fopen(filename, "r");
fscanf(fp, "%d", station_no);
printf("%d\n", *station_no);
*sta = malloc(*station_no * sizeof(station));
for (i = 0; i < *station_no; i++) {
fscanf(fp, "%f %f %s", &longitude, &latitude, sta_str);
printf("%f %f %s\n", longitude, latitude, sta_str);
(*sta)[i].lon = (double) longitude;
(*sta)[i].lat = (double) latitude;
strcpy((*sta)[i].name, sta_str);
}
fclose(fp);
}
int main() {
station *sta;
int i, stn_no;
read_stn("station.dat", &sta, &stn_no);
for (i = 0; i < stn_no; i++) {
printf("%d %s %f %f\n", i + 1, sta[i].name, sta[i].lon, sta[i].lat);
}
free(sta);
return 0;
}
outputs:
paul#local:~/src/c/scratch/station$ ./station
3
55.330002 44.270000 STN1
77.260002 33.439999 STN2
22.110001 23.120001 STN5
1 STN1 55.330002 44.270000
2 STN2 77.260002 33.439999
3 STN5 22.110001 23.120001
paul#local:~/src/c/scratch/station$
There are some other improvements you could make, e.g. you could fscanf() directly into double rather than via a float, you should check the return from fopen() and malloc(), and things like that, but I'll leave those as an exercise to you.
Also, for future reference, in your read_stn() function, it's often easier to create a local pointer, do all your work in that, and then assign the value to the output parameter just at the end. It helps you to avoid dealing with all this indirection within your function, for instance:
void read_stn(char filename[], station ** sta, int *station_no) {
FILE *fp;
station * psta;
int st_num;
if ( (fp = fopen(filename, "r")) == NULL ) {
fprintf(stderr, "Couldn't open file %s\n", filename);
exit(EXIT_FAILURE);
}
fscanf(fp, "%d", &st_num);
printf("%d\n", st_num);
if ( (psta = malloc(st_num * sizeof(*psta))) == NULL ) {
fprintf(stderr, "Couldn't allocate memory\n");
exit(EXIT_FAILURE);
}
for ( int i = 0; i < st_num; ++i ) {
fscanf(fp, "%lf %lf %s", &psta[i].lon, &psta[i].lat, psta[i].name);
printf("%f %f %s\n", psta[i].lon, psta[i].lat, psta[i].name);
}
*sta = psta;
*station_no = st_num;
fclose(fp);
}