Home > Software design >  In C, does struct variable needs to be pointer if there is a pointer inside it?
In C, does struct variable needs to be pointer if there is a pointer inside it?

Time:12-06

In my code I have this (it is given by the exercise):

typedef struct student{
    int id;
    float *grades;
    char name[100];
}Student;

Student* fillStudent();

My question is: Why does the function "fillStudent()" returns a pointer of type Student? Is it because there's a pointer of grades inside it? Initially I thought that it was because you were supposed to return a vector of various students, but it doesn't make sense, because of the other struct:

typedef struct subject{
    Student *V;
    float average[5];
    int nStudents; 
}Subject;

The exercise asks you to call the function "fillStudent()" nStudents times to fill the vector V, so it wouldn't make sense to return all the students in a single call. So why does fillStudent() needs to return a pointer? Couldn't it simply be a variable of type Student, and doind a malloc on the grades? If this was it, then would the variable be considered a pointer after all?

CodePudding user response:

I can only tell you that you reasoning makes sense. First of all, a function called fillStudent is supposed to "fill" something already existing. Therefore, a likely signature would be:

void fillStudent(Student *dest);

This would be ideal as you'd pre-allocate nStudents and then call the function fillStudent nStudents times.

Also, the struct Student is not very usable as you have a pointer grades but you don't have a variable to know how many grades are stored there (so I guess you can't loop through them without some type of convention such as "last grade is -1" which is a malpractice).

To your question, there is no problem in C to return struct by value even if they have a vector inside.

CodePudding user response:

I want to first address some of your subquestions that came up as you speculated to the reasoning.

Question 1, does the function fillStudent() return a pointer of type Student because there's a pointer of grades inside it?

A: No. A structure can [generally] contain any kind of variable in it's fields, without having to be in a pointer variable itself, and be correct C code.

Q2: Why does fillStudent() need to return a pointer? Couldn't it simply be a variable of type Student, and call malloc on the grades?

It could absolutely return a Student structure, that had the grades field initialized with malloc. (Though as @Zeppe pointed out, there can be problems in doing this if you don't know how long the array is). This would look something like this,

#include <string.h>

Student createStudent(char* name, id, int num_of_grades) {
    Student s;
    s.id = id;
    //since s.name is a fixed length array in the struct, memory was reserved
    //for it when we declared the variable s. 
    strcpy(s.name, name)
    //s.grades is a pointer to memory on the heap, so we have to 
    //allocate with something like malloc or calloc
    s.grades = malloc(num_of_grades * sizeof(float));
    
    return s; //perfectly okay to return a struct from a function.
}

Q3: Is the variable returned by the function in Q2 considered a pointer?

A: Nope. We returned a struct not a pointer. A function that returns a pointer looks something like this,

Student * foo() {
    Student * s = malloc(sizeof(Student));

    //code to initialize/do something with s
   
    //s is a pointer to a Student structure, so by returning s, we are returning
    //a pointer to a Student structure
    return s;
}

So all an all, there is no reason given the setup you described where the function must signature return a pointer, it could even be a mistake! But there's nothing wrong doing it the way they did, and depending on what the rest of the code looks like, there could be a few advantages.

The first is mundane. If all the rest of your code is dealing with Student*'s rather than Student's, it might just feel more convenient to have the function you use to construct them give you Student*'s rather than Student's. You can have an opinion for if this is a good reason or not, but it is my guess for the most likely reason it's written this way in your case.

The next reason has a more clear advantage. If creating a Student can fail (like maybe they haven't signed up for enough classes or something), then you want to be able to indicate that to the rest of your program. If you just return a struct, then you can't generally know from the return value if it is valid. You could do something like this

#include <stdbool.h>
//value of is_error gets set to true if there was an error, and false otherwise
Student createsStudent(char * name, /* other fields */, bool * is_error)

which can be used like

bool is_error;
Student s = createsStudent(/* ... */, &is_error)
if(is_error) {
    //respond to error
}

which works great, but you can end up with a lot of extra is_error variables which can be slightly annoying/distracting. If instead the method to create the Student returns a pointer to the Student, then you can check if it is null or not, and use that to know if there was an error.

Finally, if you have a bunch of functions that modify a Student, you might want to call them on the same variable one after another. But if they're written like this,

Student createStudent(/*...*?);
void foo(Student* s);
void bar(Student* s);
void baz(Student* s);

You have to call them like this

Student s = createStudent(/*...*/);
foo(&s);
bar(&s);
baz(&s);

But if you write this, where the functions return the pointer to their argument (they just return s),

Student * createStudent(/*...*/?);
//returns s
Student * foo(Student* s);
//returns s
Student * bar(Student* s);
//returns s
Student * baz(Student* s);

you can do this,

Student * s = baz( bar( foo( createStudent(/*...*/) ) ) );

which is a small thing but can be really nice.

All in all, neither way is right or wrong.

  • Related