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.
Related
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);
}
I have written a program which read values from a the file. The data in the file is in following format.
100 Full Name SomeDetails.
234 Full Name SomeDetails
Following is the program which i wrote.
#include <stdio.h>
#include <stdlib.h>
#include<string.h>
#define MAX 10
struct student
{
int rollno;
char name[20];
char course[25];
};
int main()
{
FILE *fptr;
fptr=fopen("records.txt","r");
struct student s[10];
int i=0,tstudent=0;
char che;
char line[100];
//SECTION : 1.1 -> READING NUMBER OF LINES
while(!feof(fptr))
{
che=fgetc(fptr);
if(che=='\n')
{
tstudent++;
}
}
printf("Total Lines in File = %d\n",tstudent);
fclose(fptr);
//SECTION : 1.2 -> READING RECORDS FROM FILE
fptr=fopen("records.txt","r");
char newString[20][20];
int ii,j,ctr;
j=0;
ctr=0;
for(i=0; i<tstudent; i++)
{
fgets(line,100,fptr);
printf("Value of Line %d = %s",i,line);
for(ii=0; ii<=(strlen(line)); ii++)
{
// if tab or NULL found, assign NULL into newString[ctr]
if(line[ii]=='\t'||line[ii]=='\0')
{
newString[ctr][j]='\0';
ctr++; //for next word
j=0; //for next word, init index to 0
}
else
{
newString[ctr][j]=line[ii];
j++;
}
}
}
for(ii=0; ii < ctr; ii++)
{
printf("\n%s",newString[ii]);
}
printf("Value of ctr = %d",ctr);
fclose(fptr);
}
Above code is working fine, BUT all the code is in main function, but i want to make a separate function which can be called from main file and return me every data of file in two dimensional or one dimensional array as return value.
Any help/suggestions would be appreciated.
I tried following CODE as a separate function.. NOT WORKING.
#include <stdio.h>
#include <stdlib.h>
#include<string.h>
char readfile(int tstudent,FILE* filename)
{
//FUNCTION TO READ RECORDS FROM FILE.
FILE *fptr;
int i,k;
char line[100];
char newString[20][20];
int j=0,ctr=0;
fptr=fopen("records.txt","r");
for(i=0; i<tstudent; i++)
{
fgets(line,100,fptr);
printf("Value of Line %d = %s",i,line);
for(k=0; k<=(strlen(line)); k++)
{
// if tab or NULL found, assign NULL into newString[ctr]
if(line[k]=='\t'||line[k]=='\0')
{
newString[ctr][j]='\0';
ctr++; //for next word
j=0; //for next word, init index to 0
}
else
{
newString[ctr][j]=line[k];
j++;
}
}
}
return newString;
}
I defined a new variable char results[] in main function.. and tried to called the function as follows
results[]=readfile(tstudent,fptr)
but when trying to read results.. its showing garbage
char readfile(int tstudent,FILE* filename)
...
char newString[20][20];
...
return newString;
That can't be a good thing, right? You define readFile to return one single character (not a pointer, just one byte) and then return an array instead. We should not be surprised the compiler complained.
If you "fix" that be redefining the return type, you still have a problem because newString is an automatic local variable. The storage (memory) it defines is undefined outside the function.
The easiest way for a function to populate a structure (or array) in C is for the caller to pass it as a function parameter. So you wind up with something more like:
int readfile( FILE* input, char newString[][20], int tstudent )
where newString is defined the same way as you have it, but by the caller, not in readfile. Cf. the stdio functions like fgets; most of them require the caller to define the buffer they read into.
I'll just point out a few more mistakes.
Whenever you call a function -- especially an I/O function -- check for errors. You may want to read tstudent records, but how many are there? If you ask for 5 and find only 1, what then? Your read loop must test for end-of-file, and readfile must return the number of records read, else the caller will never know. Compare with how fread(3) works. Those Unix guys knew a thing or two about how to define a function!
Now your function looks something like this:
int readfile( FILE* input, char newString[][20], int tstudent ) {
char line[100], *s;
int i=0;
for( ; i < tstudent && (s = fgets(line, sizeof(line), input)) != NULL; i++ ) {
/* do stuff with line */
}
/* check for EOF/error if s is NULL, and report */
return i;
}
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);
Hey I'm not sure why when I pass a Struct array to a function; When I try to access it's members it prints random number. Below the statement "printf("%d\n", netTopo[0].nodes[1]);" works correct but I'm in the function and try print the same data, it prints a bunch of random number? Not sure what I'm doing wrong.
int main(int argc, char *argv[]) {
if (argc != 3){
printf("Incorrect command line arguments. Required 2 files.\n");
exit(-1);
}
FILE *netFile, *schFile; // put into a loop
netFile = fopen(argv[1], "r");
schFile = fopen(argv[2], "r");
int *sched = getSchedFile(schFile);
struct nodeInfo *netTopo = getTopology(netFile);
printf("%d\n", netTopo[0].nodes[1]);
int nodeSocks[nodeCount];
for (int i=0; i<nodeCount; i++){
nodeSocks[i]=getSocketNo();
}
get_elapsed_time(); // start clock
for (int i=0; i<nodeCount; i++){
if (fork()==0){
nodeExecution(i, nodeSocks, netTopo, sched);
exit(0);
}
}
}
void nodeExecution(int id, int nodes[], struct nodeInfo *netTopo, int *schd){
printf("%d\n", netTopo[0].nodes[1]);
......
so you return a pointer to local var on stack from getTopology()? that's the bug.
netTopo is on stack and when you return from getTopology() there are other function calls which would reuse the memory region where netTopo is stored. That memory is modified and you get different output when calling nodeExecution()
ADD: to fix this you may allocate memory in getTopology():
struct nodeInfo* getTopology(FILE *file){
int id, digit=0, totLinks=0;
fscanf(file, "%d", &nodeCount);
struct nodeInfo * netTopo = malloc(sizeof(struct nodeInfo)*nodeCount);
....
I would like to read a file that has the sample number, values and status(1.1, 23,0). I used a Struct to hold that information. I will pass the function struct array and the file location.
#include <stdio.h>
struct Data_point
{
long sampleNumber;
double value;
int status;
};
int filldata(struct Data_point *a, const char *filelocation)
{
FILE *f;
if((f=fopen(filelocation,"r"))==NULL)
{
printf("You cannot open");
}
fscanf(f, "%ld%lf%d", a.sampleNumber, a.value, a.status);
}
int main(void)
{
struct Data_point data[10];
filldata(data, "/home/alexchan/IntrotoC/rec11/dataPoints.txt");
return 0;
}
But, I got an error saying, "request for member not a structure"...
One problem is that the filldata() is taking a pointer argument. So you use -> to address members not ".". So a.sampleNumber should be a->sampleNumber for example.
Another issue is that filldata() is reading in a single struct, but you are passing it the pointer to the top of the array, which is synonymous with &(data[0]). So this function will just overwrite that first element if you call it repeatedly (which you didn't). If you call it in a loop you will need to pass it in pointers to the individual array members:
for(int i = 0; i < 10; ++i){
filldata(&(data[i]), "/home/alexchan/IntrotoC/rec11/dataPoints.txt");
}
You could actually use data + i as the first arg instead of &(data[i]) but I like the latter as I find it more readable.
struct Data_point *a is your function arugument and you are passing data which is a array. So basically you are trying to acess members from a array which is not a struct.
May be
for( int i=0; i<10;++i)
filldata(data[i],.....)
and
int filldata( struct Data_point a,...) //as you are using a.
fscanf requires a pointer-to-data for each passed argument. Use the AddressOf operator & to get a reference to each struct member:
int filldata(const char *filelocation, struct Data_point *a, int nElements)
{
int n = 0;
FILE *f = fopen(filelocation, "r");
if(f)
{
while (fscanf(f, "(%ld,%lf,%d)", &(a[n].sampleNumber), &(a[n].value), &(a[n].status)) == 3 && n < nElements)
n++;
fclose(f);
}
else { printf("Unable to open '%s'\n", filelocation); }
return n;
}
Now, this function is slightly different to yours. You need to tell it how long the array you're passing in as the "a" parameter is. It will return the number of successfully filled entries.
i.e
int main(int argc, char **argv)
{
struct Data_point data[10];
int n = filldata("C:\\Users\\254288b\\temp.txt", data, sizeof(data) / sizeof(struct Data_point));
printf("%d Data_point's were filled successfully.\n\n", n);
for(int i = 0; i < n; i++)
{
printf("Sample Number: %ld\n", data[i].sampleNumber);
printf("Value: %lf\n", data[i].value);
printf("Status: %d\n", data[i].status);
printf("----------------------------\n");
}
return 0;
}
Do note, my pattern for fscanf expects your file to be like:
(100,1.1,10)(200,2.2,20)(300,3.3,30)(400,4.4,40)
Each set is enclosed in parenthesis.