Using pointers and structs - c

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.)

Related

malloc'd pointer inside struct that is passed by value

I am putting together a project in C where I must pass around a variable length byte sequence, but I'm trying to limit malloc calls due to potentially limited heap.
Say I have a struct, my_struct, that contains the variable length byte sequence, ptr, and a function, my_func, that creates an instance of my_struct. In my_func, my_struct.ptr is malloc'd and my_struct is returned by value. my_struct will then be used by other functions being passed by value: another_func. Code below.
Is this "safe" to do against memory leaks provided somewhere on the original or any copy of my_struct when passed by value, I call my_struct_destroy or free the malloc'd pointer? Specifically, is there any way that when another_func returns, that inst.ptr is open to being rewritten or dangling?
Since stackoverflow doesn't like opinion-based questions, are there any good references that discuss this behavior? I'm not sure what to search for.
typedef struct {
char * ptr;
} my_struct;
// allocates n bytes to pointer in structure and initializes.
my_struct my_func(size_t n) {
my_struct out = {(char *) malloc(n)};
/* initialization of out.ptr */
return out;
}
void another_func(my_struct inst) {
/*
do something using the passed-by-value inst
are there problems with inst.ptr here or after this function returns?
*/
}
void my_struct_destroy(my_struct * ms_ptr) {
free(ms_ptr->ptr);
ms_ptr->ptr = NULL;
}
int main() {
my_struct inst = my_func(20);
another_func(inst);
my_struct_destroy(&inst);
}
I's safe to pass and return a struct containing a pointer by value as you did it. It contains a copy of ptr. Nothing is changed in the calling function. There would, of course, be a big problem if another_func frees ptr and then the caller tries to use it or free it again.
Locality of alloc+free is a best practice. Wherever possible, make the function that allocates an object also responsible for freeing it. Where that's not feasible, malloc and free of the same object should be in the same source file. Where that's not possible (think complex graph data structure with deletes), the collection of files that manage objects of a given type should be clearly identified and conventions documented. There's a common technique useful for programs (like compilers) that work in stages where much of the memory allocated in one stage should be freed before the next starts. Here, memory is only malloced in big blocks by a manager. From these, the manager allocs objects of any size. But it knows only one way to free: all at once, presumably at the end of a stage. This is a gcc idea: obstacks. When allocation is more complex, bigger systems implement some kind of garbage collector. Beyond these ideas, there are as many ways to manage C storage as there are colors. Sorry I don't have any pointers to references (pun intended :)
If you only have one variable-length field and its size doesn't need to be dynamically updated, consider making the last field in the struct an array to hold it. This is okay with the C standard:
typedef struct {
... other fields
char a[1]; // variable length
} my_struct;
my_struct my_func(size_t n) {
my_struct *p = malloc(sizeof *p + (n - 1) * sizeof p->a[0]);
... initialize fields of p
return p;
}
This avoids the need to separately free the variable length field. Unfortunately it only works for one.
If you're okay with gcc extensions, you can allocate the array with size zero. In C 99, you can get the same effect with a[]. This avoids the - 1 in the size calculation.

Function return a pointer in 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.

free(struct variable) doesn't clear previous stored values associated with that variable when I malloc again?

I created a struct like the following:
typedef struct header{
int hc;
char src[18];
char dst=[18];
char reason[15];
char d[3];
char m[3];
char y[4];
struct measurements{
char h_ip[17];
int h_ttl;
int h_id;
float h_rtt;
}HOPS[100];
}HEADER;
INSIDE MAIN:
HEADER *head;
for(...){
head=(HEADER*) malloc(sizeof(HEADER));
.....
free(head);
}
Will the above malloc automatically allocate memory for the inner struct as well? Also, I'm facing a weird problem here. After I free the header, I'm still able to print the values of head->HOPS[i].h_ip. Should I explicitly free the inner struct as well so that even the values get cleared?
Yes, it allocates memory for the inner structure. And you need not free the inner structure separately.
If you have a pointer defined inside your structure, in that case you have to allocate separately for that pointer member of the structure and free that separately.
Consider freeing memory as a black box. All what you know is that after freeing you shouldn't refer to freed memory.
You may find that that memory block still exists and still contains some old values. That's ok: it just was marked as freed and probably it will be used again soon by allocator.
For example when you call malloc again and realized that just allocated block contains values from the old structure. It happens and that's alright. Just use this block as usually.
So, after the problem with the wrong declaration of head was resolved:
free returns a previously allocated memory block to the heap. It does not clear anything (for performance reasons). However, you are not supposed to access that block anymore afterwards. Doing so results in undefined behaviour and might let your computer fly out of the window.
Worst that can happen is ... nothing ... Yes, you might even not notice anything strang happens. However, that does not mean your program run correctly, it just does not show any symptoms.
To catch illegal accesses, you might set the pointer to NULL once you freed the object it points to. Some operating systems catch accesses to addresses near the null pointer address, but there is no guarantee. It is a good practice anyway and does no harm.
For your other question: malloc allocates a block of memory large enough to store that many bytes you passed as argument. If it cannot, it will return a null pointer. You should always check if malloc & friends returned a valid pointer (i.e. not a null pointer).
int *p = malloc(sizeof(int));
if ( p == NULL ) {
error: out of memory
}
...
Notice the omission of the cast of the result of malloc. In C you should not cast void * as returned by malloc & friends (but also elsewhere). As much as you did not for free(head). Both take the same type: void *, btw. (so why cast one and not the other?). Note that in C any object pointer can freely be assigned to/from void * without cast. Warning functions are no objects in the C standard!
Finally: sizeof(HEADER) returns the size of the struct. Of course that include all fields. A nested struct is a field. A pointer to another struct is a field. For the latter, however note: the pointer itself is a field, but not what it points to! If that was another struct, you have to malloc that seperately **and also free seperately (remember what I wrote above).
But as you do not have pointer inside your struct, that is not your problem here. (keep it in mind, if you continue programming, you will eventually need that!)

Concern about struct pointer

I'm doing a project where our professor has given us code with variables and prototype declarations that we are unable to change. One is a struct, with a pointer to that struct is typedef'd as pStudentRecord:
typedef struct
{
char* firstName;
char* lastName;
int id;
float mark;
}* pStudentRecord;
With a pointer to this type called g_ppRecords. This will be a dynamic array of pointers to the structs above.
Here's where my question comes in. The records will be read from a file. If the filename specified doesn't exist then it creates a new one. I'm initializing the g_ppRecords pointer whenever the user adds the first new record:
if(!n) //where n = number of records
g_ppRecords = (pStudentRecord*) malloc(sizeof(pStudentRecord));
g_ppRecords[n] = (pStudentRecord) malloc(16);
This code works every time I've ran it so far, but I'm not sure how. If I add more records then a new pointer (pStudentRecord) will be created in the next position in g_ppRecords. By my understanding, I haven't allocated the space for that new pointer, yet every time it works without even a hint of a problem. I can access the members of the new structs fine and I'm not getting a heap corruption error or access violation or anything like that. Are my concerns correct or am I doubting myself?
Based on the code that you've shown, your concerns are valid.
This line:
g_ppRecords = (pStudentRecord*) malloc(sizeof(pStudentRecord));
Allocates only enough space for a single pStudentRecord. Think of this as an array of pStudentRecord with only a single element, at g_ppRecords[0].
If I add more records then a new pointer (pStudentRecord) will be created in the next position in g_ppRecords.
Now the problem is what might happen when you do what you've described here. What happens when you add a new pointer? Unless you use realloc to get more space for g_ppRecords, you don't have room in that array for more pointers to records. If you malloc a new pointer at the second element, i.e.:
g_ppRecords[1] = (pStudentRecord) malloc(16);
Then you're using memory, g_ppRecords[1], that you haven't allocated. This may appear to work, but this memory doesn't belong to you. Keep adding new pointers and eventually your program will break. Or your program may break because of something totally unrelated in another part of your code.
The fix is that you should initially allocate your array so that it can hold multiple pointers, instead of only one. How can you do this with your first malloc line?
I should add that when you allocate memory for a struct using malloc(16) you're making assumptions about the data structure that you shouldn't make, specifically that the struct will always occupy 16 bytes. Given your typedef: straight to a pointer from an anonymous struct, you can change that 16 to something more general, but this isn't directly related to your question, and is something that you should ask your professor about.
As a general rule, try to avoid malloc( sizeof( type )), especially when the type is obfuscated by a typecast. It is much safer to call sizeof on a variable: malloc( sizeof x ). Also, in C, you should not cast the return from malloc. In other words, instead of allocating space for only one record with:
g_ppRecords = (pStudentRecord*) malloc(sizeof(pStudentRecord));
it would be better to allocate space for n records by writing:
g_ppRecords = malloc( n * sizeof *g_ppRecords );
typedef struct
{
char* firstName;
char* lastName;
int id;
float mark;
}* pStudentRecord;
This is an anonymous struct. A bit weird here, but maybe to teach you something. Do this to create a new one:
pStudentRecord ptr;
ptr = malloc(sizeof(*ptr));
This will automatically malloc the right amount of memory.
You've still got problems because you need to malloc the array to hold the pointers. For that do this:
pStudentRecord* g_ppRecords = malloc(n * sizeof(pStudentRecord));
You can then use g_ppRecords like this:
pStudentRecord ptr = g_ppRecords[3];
Putting it all together we have our custom allocator:
pStudentRecord* g_ppRecords = malloc(n * sizeof(pStudentRecord));
for (size_t i = 0; i < n; ++i)
{
pStudentRecord ptr;
g_ppRecords[i] = malloc(sizeof(*ptr));
}
I wrote all this without compiling and testing, so there may be errors (but its not my homework :-) )

malloc in c, struct

I made a struct like this:
struct a{
char *name;
char *value;
struct a *next;
};
when I malloc for memory at first time, it's ok, and I can set 'name' and 'value' a corresponding value.
but when I malloc for the second time, errors come. And It's a cgi, just show me "500 Internal server error".
I changed the pointer 'name' and 'value' to array, everything works.
I thought maybe the complier doesn't know how much memory to assign.
And do you have some ideas with this? I will appreciate every answer!
struct a {
char *name;
char *value;
struct a *next;
};
struct a *head = malloc(sizeof *head);
The above allocates space for a single struct a object, but it doesn't initialize any of hte three pointers contained in a struct a. In particular, if you want name and value to point to strings, you'll need to allocate space for those strings:
head->name = malloc(5);
strcpy(head->name, "Fred");
head->value = malloc(8);
strcpy(head->value, "abcdefg";
This is considerably oversimplified. 5 and 8 are "magic numbers"; you should specify the sizes in a way that will remain consistent if you change the initial values. And you should always check whether malloc() returns a null pointer (even if you just terminate the program with an error message).
If you don't initialize name and value to point to some chunk of allocated memory, you might still be able to initialize what they point to (e.g., by doing the strcpys above without the mallocs). More precisely, the system won't necessarily diagnose the error.
Finally, you'll need a call to free() corresponding to each malloc() call.
Note that this is largely a guess based on your description. If you can show us your actual code, we can help you better.
If you use malloc with sizeof(struct a) it's just going to assign enough space to store the pointers name and value. You want these to be char arrays, then it'll know how much space to set aside for each instance of a.

Resources