Suppose I have a "class" (struct) in C as follows:
typedef struct{
int age;
}
Person;
I have seen various possibilities on how to implement e.g. a constructor function (but also links to other structs etc), and I am not sure which one I should be using and when. The first that comes to mind is:
Person newPerson(int age){
Person person;
person.age = age;
return person;
}
Person person = newPerson(4);
This would work the same as it does in Java... But I often see something different online
Person* /*why?*/ newPerson(int age){
Person person;
person.age = age;
return &person; //why?
}
Person* person = newPerson(4);
//also, why not just: Person person = *newPerson(4);
I know how to work with it, but I am not sure WHY I would return a pointer to the struct instead of just the struct itself. Some websites even go as far as to use malloc:
Person* newPerson(int age){
Person* person = (Person*)malloc(sizeof(Person)); //why?
person->age = age;
return person;
}
Person* person = newPerson(4);
Again, I still know HOW to work with all of these other approaches, I just do not understand WHY I would want to do so? The first method seems easier to understand and implement, so there must be some other reasons why I would want to use method 2 or 3 (or similiar) instead? If this is the case, could someone explain some of these reasons?
Edit: If you know a website (or search term) that explains this well, I could also use a link to that website instead of an answer
Don't confuse a C struct with a Java object. A Java object is implicitly represented as a pointer even though you don't see that in the code, but a C struct is not: it's the entire contents of all the struct's fields. If you assign a returned struct to a variable, all the fields are copied. If you assign that variable to another variable of the same type, all the fields are copied, unlike Java, where both variables would refer to the same memory and same field instances.
As commented by @wohlstad, this option works, but causes Person to be copied on return:
Whether you want an "object" to be copied or not is similar in C and Java; if it's OK to copy the object then this is fine, especially for small structs. But often, you want the identity of the object to remain constant throughout its life, in which case you want to pass a pointer to the object rather than copy the object.
The second option is wrong and dangerous:
It's wrong because
personis allocated on the stack, which is popped on return fromnewPerson()and that stack space is reused for different purposes in subsequent code, such as when other functions are called. But meanwhile the variableperson1still points to that memory that's being reused for other purposes, and bad things will result.The third option (removing the cast on
malloc) is the canonical choice:malloc()allocates memory for the new object. In Java you don't need to do this, the language hides that detail from you, but C does not. This newly allocated memory remains allocated until you release it usingfree(), so in addition tonewPerson()you'd want to define adeletePerson()function, and user code would need to call it explicitly.To write well-structured code that provides type safety and hides details from calling code, you'd implement
Personin one C module, with its associated include file defining the interface, and use C "incomplete types":person.h:person.cUser code:
struct person_s;is the incomplete type declaration: there's a struct namedperson_s, but for now, its contents are left unspecified.Note that the user code doesn't have a clue what's in the struct and can't use
sizeof(person_s). All the details are hidden. Many coders usevoid *for this, but that's a bad habit, as it's too easy to mix up differentvoid *variables being used for different purposes. For example, if you have apersonAPI and agroupAPI and both returnvoid *as the type, you could accidentally pass a person where a group is required, and the compiler won't detect it. Using incomplete types, the compiler would catch the error.