Function return a pointer in C - c

I am new to C and I try to create a function return a pointer. I used different ways to do it:
1.
typedef struct student{
int age;
}student;
student *create_student(){
student* s;
s-> age= 24;
return s;
}
int main() {
student* s = create_student();
printf("the student age is %d", s->age);
return 0;
}
It compiles but doesn't seem to work.
2.
typedef struct student{
int age;
}student;
student *create_student(){
student* s;
student s2;
s2.age = 24;
s = &s2;
return s;
}
int main() {
student* s = create_student();
printf("the student age is %d", s->age);
return 0;
}
It seems to work, and print "the student age is 24", but if I added one printf statement before previous printf:
int main() {
student* s = create_student();
printf("This is a test\n");
printf("the student age is %d", s->age);
return 0;
}
It gives me:
This is a test
the student age is -1422892954
3.
If I use following ways:
typedef struct student{
int age;
}student;
student *create_student(){
student* s = malloc(sizeof(student));
s -> age = 24;
return s;
}
int main() {
student* s = create_student();
// printf("This is a test\n");
printf("the student age is %d", s->age);
return 0;
}
It works in both cases, with and without the commented printf
I just want to know what are the reasons it fails for 1 and 2. And why it works for 3?
And generally speaking when should we use malloc and when we shouldn't?
Thanks

Example 1
Your example 1 doesn't work because no student object is ever created.
student* s;
This creates a pointer s that is supposed to point to a student but currently points to an unknown memory location because it is an uninitialized variable. It definitely doesn't point to a new student, since none was created so far.
s->age = 24;
This then writes to the unknown memory location that s is currently pointing to, corrupting memory in the process. You now entered the realm of undefined behavior (UB). Your process may crash at this very moment, or later on, or it may do something crazy and unexpected.
It doesn't make sense to think about what happens after this point, because your process is already doomed by now and needs to be terminated.
Example 2
Your example 2 sort-of works but only sometimes, because yet again UB comes into play.
student s2;
Here you are creating a student as a local variable. It is probably created on the stack. The variable is valid until you leave the function create_student.
However, you are then creating a pointer to that student object and returning it from your function. That means, the outer code now has a pointer to a place where a student object once was, but since you returned from the function and it was a local variable, it no longer exists! Sort of, that is. It's a zombie. Or, better explained, it's like when you delete a file on your harddisk - as long as no other file overwrote its location on the disk, you may still restore it. And therefore, out of sheer luck, you are able to read the age of it even after create_student had returned. But as soon as you change the scenario a bit (by inserting another printf), you ran out of luck, and the printf call uses its own local variables which overwrite your student object on the stack. Oops. That is because using a pointer to an object that no longer exists is also undefined behavior (UB).
Example 3
This example works. And it is stable, it doesn't have undefined behavior (almost - see below). That is because you create the student on the heap instead of the stack, with malloc. That means it now exists for eternity (or until you call free on it), and won't get discarded when your function returns. Therefore it is valid to pass a pointer to it around and access it from another place.
Just one small issue with that - what if malloc failed, for example you ran out of memory? In that case we have a problem yet again. So you should add a check for whether malloc returned NULL and handle the error somehow. Otherwise, s->age = 24 will attempt to dereference a null pointer which again won't work.
However, you should also remember to free it when you are done using it, otherwise you have a memory leak. And after you did that, remember that now your pointer did become invalid and you can no longer use it, otherwise we are back in UB world.
As for your question when to use malloc: Basically whenever you need to create something that lives on after you leave your current scope, or when something is local but has to be quite big (because stack space is limited), or when something has to be of variable size (because you can pass the desired size as argument to malloc).
One last thing to note though: Your example 3 worked because your student only had one field age and you initialized that field to 24 before you read it again. This means all the fields (since it had only that one) got initialized. If it had had another field (say, name) and you hadn't initialized that one, you would still have carried an "UB time bomb" if your code elsewhere would have attempted to read that uninitialized name. So, always make sure all fields get initialized. You can also use calloc instead of malloc to get the memory filled with zeroes before it's passed to you, then you can be sure that you have a predictable state and it is no longer undefined.

Because in case1 and 2, the variable "age" is in the sub-function create_student()'s scope, on a stack. So once the sub-function is finished, that area is released, which means that the "age" is released also. So s now points to a non-sense area. If you are lucky enough, that area still stores the "age", you can print that info out, that's why it works in case2's first part.
But in case3, student* s points to a heap area and when that sub-function is finished that heap area won't be free. So s->age still works.

Continuing from the comments, you can think of it this way:
Case 1.
student* s;
s in an uninitialized pointer that does not point to any memory you can validly use. It's value is indeterminate.
Case 2.
student* s;
student s2;
s2.age = 24;
s = &s2;
s is an uninitialized pointer (as in 1.), s2 declares a valid struct and s2.age is validly initialized. s = &s2; assigns the address of s2 (declared local to the function) to s. When s is returned, s2 is invalidated -- its life limited to the function, so the address return to main() is invalid.
Case 3.
student* s = malloc(sizeof(student));
Yes!! s now holds the starting address to a valid block of memory with allocated storage duration (good for the life of the program or until freed). The only problem is you need to validate the allocation succeeds:
if (s == NULL) {
perror ("malloc-s");
return NULL;
}
Now you can have confidence that the return is either a valid address or NULL indicating allocation failed.
When to use
To your last question, you dynamically allocate when you don't know how many of something you will need, so you declare some number of them, keep track of how many you have used, and realloc more when the initially allocated block is filled. Or, you need more of something than will fit on the program stack, you can allocate or declare as static (or declare globally). Otherwise, if you know how many you need beforehand, and that will fit on the stack, just declare an array of them and you are done.
Let me know if you have further questions.

Related

is this use of realloc inside a function correct? (and the use of free() in case of failure)

I found some questions similar to this but there are some differences.
here is my code:
student.h:
#define NUM_GRADES 5
#define NAME_LENGTH 21
#define ADDRESS_LENGTH 21
typedef struct
{
char name[NAME_LENGTH]; //name of a student - up to 20 chars.
char add[ADDRESS_LENGTH]; //address - up to 20 chars.
int grades[NUM_GRADES]; //stores the grades of a student.
}student;
//the current size of the students array.
extern int currentTotal;
//add a new student space, return 0 if failed, 1 if succeeded.
int addStudent(student **studentClass);
student.c:
int addStudent(student **studentClass)
{
//adds the count for the new student.
currentTotal++;
//a temporary pointer to hold studentClass array in case realloc fails.
student *temp=NULL;
//reallocating space for the new student.
if (!(temp = (student*)realloc(*studentClass, currentTotal * sizeof(student))))
{
printf("Not enough memory.\n");
free(*studentClass);//free the original array.
currentTotal = 0;
return 0;
}
*studentClass = temp;//point class to the newly allocated space.
printf("Added space for a student.\n");
return 1;
}
main.c:
#include <stdio.h>
#include <stdlib.h>
#include "student.h"
void main()
{
student *studentClass=NULL;
....
if(addStudent(&studentClass)
....
currentTotal is an external int variable.
Is the use of realloc correct?
And the use of free?
I always mix up whether I should use * or ** inside the function, once I send the address of a pointer to another function. (i.e. having a pointer like *studentClass and then sending &studentClass to another function).
if this is indeed correct then what happens to the original data that *studentClass pointed before the line
"*studentClass = temp;" (in student.c) ?
does it need to be freed?
edit:
Please don't be confused that initially *studentClass is NULL, it's only like that at the start, addStudent() is meant to be called in a loop, so after the first time, *studentClass is no longer NULL. addStudent() increase the size of *studentClass after each call.
Thanks.
It's "save" in the sense that it does not introduce undefined behaviour and it does not leak memory. You take care that if realloc fails, the original data is not freed (so you do it), and you store the result of realloc in a temp variable in order not to loose the pointer to the original data. So far everything is OK.
It contains, however, a pitfall for the caller of addStudent if realloc fails. In this case, you free the original memory block without providing a new one, but you do not reset the pointer to NULL. So the variable passed to addStudent still points to some memory, but this memory has been freed. A caller might attempt to free this memory a second time (yielding undefined behaviour then).
In case of realloc having failed, I'd suggest to do either of two options depending on who shall be responsible for freeing student's array memory:
a. addStudent is responsible: Free the original memory AND set the pointer to NULL, such that no one outside can attempt to free memory twice. So you'd add a *studentClass=NULL after the free.
b. the caller is responsible: Don't free original memory in case of realloc failure; return - as you do - a failure code and let the caller do the rest. So you'd remove the free.
This is all fine. Because *studentClass is NULL, realloc(3) behaves just like malloc(3). In the case that realloc(3) fails, it does not modify the passed pointer, meaning that *studentClass is still NULL. Calling free(3) on a NULL pointer is perfectly valid, but nothing will happen (a no-op). Then the function returns, and *studentClass is still NULL. You just need to check its value after the call to addStudent: if it's NULL the addition of a student failed, otherwise it succeeded.
Your use of double-pointers is also valid. One way to reason about this is like so. Pointers allow modification of the data at the pointed-to location. If one wants to pass an integer to a function to be modified, one can pass it as an int*. Inside the function, one dereferences it to modify it, such as *my_int = 0;. So if one wants to modify a student* inside a function, it must be passed as a student** and then dereferenced whenever one wants to change its contents.
In addition to the fact mentioned by Stephan Lechner that code should either leave the allocation alone or null out the pointer to it (as opposed to freeing the memory without nulling out the pointer) there are a couple of other issues I see:
If a caller isn't expected to recover from out-of-memory conditions, allocation functions shouldn't return when they occur. Instead they should signal the fault (perhaps by raising a signal) and then exit (if signalling the fault didn't already force an exit). Having a function guarantee that it will never return unsuccessfully can greatly simplify client code.
If the function is going to use a global variable to hold the size of the allocation, it should use a global pointer to the allocation. If it is going to use an object-pointer identified by a passed-in address, it should use an object count that is also identified by a passed-in address.
I can see four good approaches to fixing that second issue:
Pass in a double-indirect pointer as you do now, but also pass a pointer to an integer-type object holding the number of students.
Pass in a pointer to a structure which holds a student pointer and the number of students.
Define a structure which holds a count and a flexible array member of the student type, and keep a pointer to that rather than to the first student.
As above, but keep a pointer to the first student (as opposed to the start of the allocated region). This would require that any code that uses malloc/realloc/free be adjusted to offset the pointers exchanged with such functions.
Approaches #3 and #4 could have a slight advantage if the number of students can grow instead of shrink. If the size of the allocated region shrinks to zero, there would be no way to determine whether a request to realloc a region's size to zero bytes succeeds (if the region previously had a zero size, the Standard would allow realloc(prevPtr,0) to return null after having successfully released the previous allocation, but the Standard would also allow the realloc to fail (and return null) without having released the previous allocation.

Is mallocing a object in this manner illegal?

I have a while loop, where in each itteration of the while loop I malloc a struct object. Then at the end of the loop I add it to the end of a linked list. Now my concern is, I have the same variable name when I malloc each time in the loop, so I dont understand how this works after I attach the lastest malloced object to the linked list, what happens to the others that I malloced that had the same name.
My code:
struct Student{
char *name;
int IDno;
}Student;
someFunction(){
x = 0;
while(x!=6){
Student *name = malloc(sizeof(Student)); <----- Confusion here
addToLinkedList(LinkedListStruct,name);
}
Is it alright that I have name being malloced each time in the loop. Can someone explain to me what happens if I malloc in this way, add it to the end of a linked list and then go into the next loop and do this same.
The malloc() call has no relationship to the name of the variable you assign its return value to. malloc() doesn't know that you assign its return value to a variable named name. What you do is completely valid and well-defined.
There is still an error with your code though as malloc() returns a pointer to a memory area, not the memory itself. Thus the declaration should have been
Student *name = malloc(sizeof (Student));

How to initialize a struct using pointer?

I am new to C, and I am facing this problem when practicing.
The struct:
typedef struct {
char name[20];
int score[3];
int no;
} Student;
The initialize:
Student *pStudent;
strcpy(pStudent->name,"Kven");
pStudent->score[0]=1;
pStudent->score[1]=2;
pStudent->score[2]=3;
pStudent->no=1;
If the "pStudent" is not a pointer I can get it right. But if it is a pointer, I will always get a "bad access" error. How to solve it if I must use a pointer? Thank you.
A variable of type pointer stores the address in memory of another variable (it points to another variable).
Your variable pStudent is not initialized. It doesn't point to anything. In fact, it contains some garbage value that, most of the times, is the address of a memory area where the program is not allowed to write. This is why when you attempt to write there (the call to strcpy()) the operating system slaps your program over its "hand" and terminates it with the error message you mentioned.
How to correctly use pointers
One usage of pointers is to point to a certain variable that is statically allocated (defined in the program):
Student s, *pStudent = &s;
This example declares the variable s of type Student and the variable pStudent that points to it. Notice it is initialized with &s which is the address of s. Now, your code uses pStudent to initialize the fields of s.
Another usage of pointers is to dynamically allocate memory during runtime:
Student *pStudent;
pStudent = malloc(sizeof(Student));
In this situation, pStudent is initialized with the address of a new variable of type Student that is created during runtime (it doesn't have a name) by calling malloc(). malloc() allocates a block of memory (of the specified size) and return its address.
When you don't need it, you have to free the memory allocated by malloc() (using free()) for reuse:
free(pStudent);
After this call, the value of the variable pStudent doesn't change, it still points to the same address but it is invalid and using it produces undefined behaviour. You have to put the address of another Student structure in pStudent (using one of the two ways presented here) before using it again.
Student *pStudent;
should be
Student *pStudent = malloc(sizeof(Student));
Allocate memory for the pointer before writing data once done using it
free(pStudent);
Student *pStudent;
creates a pointer of type Student*. This pointer is not initialized and points to some "random" location. Allocate memory for it using malloc(or calloc) from stdlib.h:
pStudent = malloc(sizeof(Student));
and then free it after its use using:
free(pStudent);
You don't allocate any memory, you just allocate a pointer. You'll need to study pointers and how they work.
Fix the code like this:
Student student;
strcpy(student.name,"Kven");
student.score[0]=1;
student.score[1]=2;
student.score[2]=3;
student.no=1;
Alternatively you could allocate memory dynamically. But it doesn't make any sense in this case (and yet I bet at least 3 people will post answers telling you to do that...). Plus you really need to study pointers before doing dynamic allocation.
Student *pStudent = calloc(1, sizeof(Student);
if(pStudent == NULL)
{
// error handling
}
strcpy(pStudent->name,"Kven");
pStudent->score[0]=1;
pStudent->score[1]=2;
pStudent->score[2]=3;
pStudent->no=1;
...
free(pStudent);
You need to allocate memory for your structure first.
You can allocate memory with:
Student *pStudent = malloc(sizeof(Student));
And then you must release it at the end with
free(pStudent);

Pointer and Function ambiguity in C

Please look at the following code:
char* test ( )
{
char word[20];
printf ("Type a word: ");
scanf ("%s", word);
return word;
}
void main()
{
printf("%s",test());
}
When the function returns, the variable word is destroyed and it prints some garbage value. But when I replace
char word[20];
by char *word;
it prints the correct value. According to me, the pointer variable should have been destroyed similar to the character array and the output should be some garbage value. Can anyone please explain the ambiguity?
Undefined behavior is just that - undefined. Sometimes it will appear to work, but that is just coincidence. In this case, it's possible that the uninitialized pointer just happens to point to valid writeable memory, and that memory is not used for anything else, so it successfully wrote and read the value. This is obviously not something you should count on.
You have undefined behavior either way, but purely from a "what's going on here" viewpoint, there's still some difference between the two.
When you use an array, the data it holds is allocated on the stack. When the function returns, that memory will no longer be part of the stack, and almost certainly will be overwritten in the process of calling printf.
When you use the pointer, your data is going to be written to whatever random location that pointer happens to have pointed at. Though writing there is undefined behavior, simple statistics says that if you have (for example) a 32-bit address space of ~4 billion locations, the chances of hitting one that will be overwritten in the new few instructions is fairly low.
You obviously shouldn't do either one, but the result you got isn't particularly surprising either.
Because the char array is defined and declared in the function, it is a local variable and no longer exists after the function returns. If you use a char pointer and ALLOCATE MEMORY FOR IT then it will remain, and all you need is the pointer (aka a number).
int main(int argc, char* argv[]) {
printf("%s", test());
return 0;
}
char* test(void) {
char* str = (char*)malloc(20 * sizeof(char));
scanf("%19s", str);
return str;
}
Notice how I used %19s instead of %s. Your current function can easily lead to a buffer overflow if a user enters 20 or more characters.
During program execution first it will create activation records for the function main in stack segment of the process memory. In that main activation records it will allocate memory for the local variable of that function(main) and some more memory for internal purpose. In your program main doesn't has any local variable, so it will not allocate any memory for local variables in main activation records.
Then while executing the statement for calling the function test, it will create one more activation records for the calling function(test) and it will allocate 20 bytes for the local variable word.
Once the control exits the function test, activation record created for that function will be poped out of that stack. Then it will continue to execute the remaining statment (printf) of the called function main. Here printf is trying to print the characters in the test function's local variable which is already poped out of the stack. So this behaviour is undefined, sometimes it may print the proper string or else it will print some junk strings.
So in this situation only dynamic memory comes into picture. With the help of dynamic memory we can control the lifetime(or scope) of a variable. So use dynamic memory like below.
char *word = NULL:
word = (char *) malloc(sizeof(char) * 20);
Note : Take care of NULL check for the malloc return value and also dont forget to free the allocated memory after printf in main function.

Using pointers and structs

Hello i'm not sure if I'm understand the following piece of code. I would glad if someone could read my explanations and correct me if I'm wrong.
So first of all I'm declaring a struct with three arrays of char and an integer.
struct Employee
{
char last[16];
char first[11];
char title[16];
int salary;
};
After that I declare a function which takes three pointers to char and an integer value. This function uses malloc() and sizeof() to create a struct on the heap. Now this creations of the object on the heap is not really clear to me. When I use struct Employee* p = malloc(sizeof(struct Employee)), what happens there exactly? What happens when I use the function struct Employee* createEmployee (char* last, char* first, char* title, int salary) several times with different input. I know that I will get back a pointer p but isn't that the same pointer to the same struct on the heap. So do I rewrite the information on the heap, when I use the function several times? Or does it always create a new object in a different memory space?
struct Employee* createEmployee(char*, char*, char*, int);
struct Employee* createEmployee(char* last, char* first, char* title, int salary)
{
struct Employee* p = malloc(sizeof(struct Employee));
if (p != NULL)
{
strcpy(p->last, last);
strcpy(p->first, first);
strcpy(p->title, title);
p->salary = salary;
}
return p;
}
I would be glad if someone could explain it to me. Thank you very much.
The malloc function allocates some new bytes on the heap and returns the pointer.
So the createEmployee function allocates new memory every time it's called, then fills it with some data (in an unsafe way - consider using strncpy instead) and returns the pointer to that memory. It will return a different pointer every time it's called.
Each instance you create with this function will exist as long as you don't call free on its pointer.
Your first question is a question about malloc. You might get better results searching for "How does malloc work?" The answer is different for different operating systems and C libraries.
The createEmployee function creates an all-new struct Employee every time it is called.
I also see that createEmployee is written in a very dangerous way. No checking is done to ensure that the strings fit into their destinations before calling strcpy. This is how buffer overflows are created.
malloc assigns you a block of memory equal to its first argument, in this case the size of the Employee.
Every time you call createEmployee, you call malloc a separate time, and every time you call malloc, it gives you a fresh piece of memory.
This is what allows you to have different employees: if they all used the same memory, you would only be able to create one.
This is why calling free, and freeing that memory is important: the operating system has no other way of knowing whether you're using the memory or not.
If you want to edit an existing employee, maintain a pointer reference to it, and add a strcpy(p->title, newTitle); to change its title to newTitle.
Also, something that has been mentioned, strcpy is dangerous, as it will continue to write its strings regardless of whether it has exceeded the 11 characters allotted for it.
Every time you call malloc(), you're telling it to give you a new chunk of memory, at least as long as you've asked for, not currently in use anywhere else. So the following gives you three different pointers:
void *p1 = malloc(100);
void *p2 = malloc(100);
void *p3 = malloc(100);
It's like hitting a button on a vending machine. Each time, you get a different candy bar that conforms to your requests ("Caramilk" for instance.)

Resources