1

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?

4
  • This exercise seems to be created by someone who is not terribly familiar with programming in C, so it is hard to tell what they had in mind. Commented Dec 5, 2021 at 19:44
  • There no technical reason to return a pointer. Maybe, but that's just a guess, it's intended to remind the user that the structure was allocated dynamically and needs to be free()d after use. However, that is impossible to tell just from the function signature and it is also impossible to tell how the V pointer needs to be treated. Look at the function documentation. If there is none, the code is bad and shouldn't be taken as example for learning. Commented Dec 5, 2021 at 20:50
  • Yeah, this exercise seems odd, for many other reasons too. Thanks for both of you! Commented Dec 5, 2021 at 20:54
  • 1
    In the 1970s and early 1980s some compilers didn't support returning a struct by value; so the version returning the pointer was standard practice. And in C you will find that there is a lot of "inertia" from old coders and old code; many people still cast malloc (another pre-ANSI habit) and many people don't know about changes to array types that were added 22 years ago , etc. Commented Dec 5, 2021 at 21:31

2 Answers 2

2

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.

Sign up to request clarification or add additional context in comments.

Comments

1

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.

2 Comments

Awesome answer, yours and Zeppe made me understand much more of this concept. But honestly, I just think it's a mistake on the exercise. There have been some exercises with weird stuff like this on the past (I'm having C classes on college), so I guess this is just another one of them... Thanks for taking your time to explain it :)
You are probably right and I would not be surprised if it was a mistake. But there are valid reasons to return a pointer over returning the plain struct, and I wanted to illustrate those. Particularly, the standard library uses the pattern return-null-on-error and function-chaining patterns a lot, and nice to know why they're written that way. And no problem :)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.