I'm having problem with this small program:
UPDATED (As per some requests, I've included everything here so to make clear what I'm doing. Sorry for it being too long):
Student.h file:
typedef struct Student {
char *name;
int age;
char *major;
char *toString;
} *Student;
extern Student newStudent(char *name, int age, char *major);
Student.c file:
c
har *studentToString(Student s);
static void error(char *s) {
fprintf(stderr,"%s:%d %s\n",__FILE__,__LINE__,s);
exit(1);
}
extern Student newStudent(char *name, int age, char *major) {
Student s;
if (!(s=(Student)malloc(sizeof(*s)))){
error("out of memory");
}
s->name=name;
s->age=age;
s->major=major;
s->toString = studentToString(s);
return s;
}
char *studentToString(Student s) {
const int size=3;
char age[size+1];
snprintf(age,size,"%d",s->age);
char *line=newString();
line=catString(line,"<");
line=catString(line,s->name);
line=catString(line," ");
line=catString(line,age);
line=catString(line," ");
line=catString(line,s->major);
line=catString(line,">");
return line;
}
Students.c file:
static void error(char *s) {
fprintf(stderr,"%s:%d %s\n",__FILE__,__LINE__,s);
exit(1);
}
static StudentList alloc(StudentList students, Student student) {
StudentList p;
if (!(p=(StudentList)malloc(sizeof(*p)))){
error("out of memory");}
p->student=student;
p->students=students;
return p;
}
extern Students newStudents() {
Students p;
if (!(p=(Students)malloc(sizeof(*p)))){
error("out of memory");
}
p->cursor=0;
p->students=0;
return p;
}
extern void addStudent(Students students, Student student) {
StudentList p=students->students;
if (!p) {
students->students=alloc(0,student);
return;
}
while (p->students)
p=p->students;
p->students=alloc(0,student);
}
extern void initStudent(Students students) {
students->cursor=students->students;
}
extern Student currStudent(Students students) {
StudentList cursor=students->cursor;
if (!cursor)
return 0;
return cursor->student;
}
extern void nextStudent(Students students) {
students->cursor=students->cursor->students;
}
And my main method:
int main() {
Students students=newStudents();
addStudent(students,newStudent("Julie",22,"CS"));
addStudent(students,newStudent("Trevor",32,"EE"));
for (initStudent(students);
currStudent(students);
nextStudent(students)) {
char *line=currStudent(students)->toString;
printf("%s\n",line);
free(currStudent(students));
free(line);
}
free(students->students);
free(students);
return 0;
}
I'm using valgrind to check memory leaks, and it is popping following error:
8 bytes in 1 blocks are definitely lost in loss record 1 of 1
==9520== at 0x40054E5: malloc (vg_replace_malloc.c:149)
==9520== by 0x8048908: alloc (Students.c:13)
==9520== by 0x80489EB: addStudent (Students.c:42)
==9520== by 0x804882E: main (StudentList.c:10)
I understand that I need to free the memory allocated for p in alloc function, but where should I call free(p)? Or is there something else I'm doing wrong? Please help!
The question is what do you do when you are finished with a Student or a StudentList and don't need it any more. That's the point where you should call free() for all the allocated things in that structure.
Probably you would want a freeStudents function that walks through a list of students and frees all the Students and all the StudentList items in it. Then you call that function whenever you want to get rid of a list of students.
Sorry, this is tangential, but you could really do a lot to make your code more readable.
You make a struct and then redefine its type to be a pointer to it. Yikes, that's just asking for trouble when it comes to maintainability. It's usually a bad idea to hide the fact that pointers are pointers, because when people see something like this:
Students newStudents()
{
Students p;
// ...
return p;
}
convention forces us to assume that you're returning a struct that was allocated on the stack, which would be obviously incorrect. (Edit: Not necessarily "obviously incorrect", but a wasteful copy.)
Things get even hairier when you add your malloc...
Students p;
if (!(p=(Students)malloc(sizeof(*p))))
{
error("out of memory");
}
For one thing, as mentioned, people assume that with no *, Students is a full structure on the stack. This will make anyone that sees "sizeof(*p)" do a double take. It's not obvious what you're doing.
And while squishing assignments and comparisons into one if statement is perfectly valid C and C++, it's usually not the most readable solution. Much improved:
Students* newStudents ()
{
Students* p = (Students*) malloc (sizeof (Students));
if (p == NULL)
{
// ...
}
// ...
return p;
}
And people enjoy pointing out that casting the return value of malloc isn't necessary in C, but it is in C++.
As for your leak, well... valgrind didn't report your catString usage, but it's still pretty sketchy since you're hiding the memory usage. Using snprintf is a better, more idiomatic way to create the string you want.
The leak valgrind is reporting: it looks like you're just freeing the first "students" node in your list. You need to traverse it and free them all, probably like this:
Students p = students;
while (p)
{
Students next = p->students;
free (p);
p = next;
}
I think you have a problem in alloc.
If you want a pointer it should be
StudentList *p;
p = (StudentList*)malloc(sizeof(StudentList));
I don't ever use sizeof with a variable if I am using malloc(). You should be using malloc(n * sizeof(StudentList)). With that said...
You've got some major concerns. First of all, you don't tell us what Students and StudentList are specifically defined to be. You pass 0 in some cases when I think you meant NULL -- never use 0 when you mean NULL. And if malloc() fails -- meaning, it returned NULL--then you don't call free() on the result. Never, ever free NULL.
You should only free a block of memory that was (a) successfully allocated, and (b) when you don't need it anymore. That doesn't seem to enter into your code here.
There are other issues as well (you assume students in addStudent is non-NULL when you initialize p), but they don't quite address your question concerning the usage of free().
What is this doing:
if (!(p=(StudentList)malloc(sizeof(*p)))){
free(p);
So, if p can't be allocated, free it?
I am assuming that StudentList is typedefined to be some type of pointer to Student. This being the case, this line in your alloc function is a problem.
p=(StudentList)malloc(sizeof(*p))
You are attempting to dereference the pointer before it has been assigned and that is not good.
You should free the memory when you no longer need to use the objects you've allocated.
What are StudentList and Student? It looks to me like your data structure is constructed improperly. Why is your StudentList storing a pointer to another StudentList? You should probably create a StudentList using malloc and return the pointer to that using your newStudents function, and rather than returning a new StudentList struct with a copy of the old one in addStudent, simply modify the structure you created originally.
So it would go something like this:
StudentList *newStudentList() {
//malloc a new StudentList and return the pointer
}
void freeStudentList(StudentList *list) {
//free the list pointer and all its child resources (i.e. the students in the list)
}
Student *newStudent(const char *name, etc) {
//malloc a new Student and return the pointer
}
void addStudentToList(StudentList *list, Student *student) {
//modify the StudentList which has been passed in by adding the student to it.
}
You could consolidate newStudent into addStudent if you don't plan on using them separately, and you may need a freeStudent and removeStudentFromList function if you plan on doing those things separately from freeStudentList as well. You should look at Google for examples of dynamic data structures in C (here is the first result on Google, but there are many others).
You really need to define what your types are. Could you edit your post to include the definitions of Student, StudentList and Students?
Also,
StudentList p;
p = (StudentList) malloc(sizeof(*p);
Unless StudentList is a typedef for a pointer, I'm not sure how that's compiling.
We also have the line:
StudentList p=students->students
With p being defined as a Students. Then Students must be a typedef for a pointer as well.
Also, what I think is your issue in the end, is that when you try to insert a student in the linked list, you end up losing any existing list. What would be interesting for you to try would be to insert 3 students into the list, and then attempt to print the list.
Related
My data gets overwritten whenever I insert a new value ,If I omit my free() in the main program my program works fine.Why ?How to recify this issue.Is memory allocation of structure is correct?
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
struct List
{
char val[20] ;
};
struct Hashtable
{
struct List *start;
};
struct Hashtable ht[26];
void init();
void insert(struct List*);
void init()
{
register int j;
for (j=0;j<26;j++)
{
ht[j].start=NULL;
}
}
int main(void)
{
init();
int i=0;
for (int i=0;i<5;i++)
{
struct List *newnode=(struct List*)malloc(sizeof(struct List));
scanf("%s",newnode->val);
insert(newnode);
free(newnode);
newnode=NULL;
}
return 0;
}
void insert(struct List *node)
{
if ( ht[node->val[0]-97].start==NULL)
{
ht[node->val[0]-97].start=node;
return;
}
else
{
printf("The value is %s\n", ht[node->val[0]-97].start->val);
}
}
-------------------------------------------------------------------------------------------------------------------------------------------------___________________________________________________________________________
When you assign pointers, you only copy the pointers themselves, not the memory they might point to.
When you call free using a pointer, all copies of that pointer become invalid and can no longer be used.
You either need to make a completely new copy in the insert function (including allocating a new List structure), or you should not call free.
My personal recommendation is that you don't allocate at all in the main function, and that the insert function takes the string to "insert" as an argument (instead of the List pointer it currently takes).
Perhaps something like this:
void insert(char *val)
{
// Get the hash-table index (note that it only works with ASCII encoding)
char hash = tolower(val[0]) - 'a';
// First check if it exists
if (ht[hash].start == NULL)
{
// No, then add it
// First allocate memory for the node
struct List *node = malloc(sizeof *node);
// Then copy the string
strcpy(node->val, val);
// And finally add it
ht[hash].start = node;
}
}
Then your loop in the main function could be like
for (unsigned i = 0; i < 5; ++i)
{
char val[20];
scanf("%19s", val);
insert(val);
}
Imagine memory is a row of lockers, each containig an 8 bit value, and numbered as 1, 2, 3... (Well, higher values usually, but you get it). A pointer is just the number of that locker, nothing more. What malloc does, is:
Find a spot with free memory
Mark that memory as used
Return the number of the first locker
When you free, you mark again that memory as free. There are many algorithms to do this, but it would be wise to search for a small free memory space whith its number as low as possible, since leaving empty spaces is a waste of memory.
When you assign a pointer, you do not assign the values it points to, but the locker number.
When you free, you give the algorithm the possibility to use that locker, and when you use malloc again, it probably finds the same locker as before, and then you modify its contents.
Remember that your hashtable still uses that locker number, and when it opens the locker, it will find out its contents are modified.
Solution below:
TL;DR:
Your hash table uses still the same memory, so don't free it, since it might be modified when found by malloc.
Free it from the hashtable once you are finished using that pointer, to avoid wasting memory
Note: If you just read the solution, understanding heap (above), will help you with a lot of headaches, plus it is nice to know what the computer is doing with your code.
Hello I have a question regarding malloc and free type of things.
suppose I have a structure with another structure inside it
typedef struct All{
int number;
} A;
typedef struct Bet{
A *point;
} B;
Then I create a B.
B* first = malloc(sizeof(B));
first->point=malloc(sizeof(A));
Now lets say I want to make a function that deletes the struct B entirely.
For the delete function I know we have to use
free(first);
So do I also have to free(first->point) or will it disappear automatically if i do free(first);
I want to add more to what #thisisbenmanley said and build an actual explination that will also show how to wrap part of the code you write.
The basics
I'll cover just malloc() and free() here. Whenever you call malloc() there should be a free() also. Let's take this example with a simple int.
int *p = malloc(sizeof(int));
*p = 5;
printf("%d", *p);
free(p);
It is the same for a struct. Another example:
typedef struct Person
{
int Age;
int Height;
}Person;
Person* p = malloc(sizeof(Person));
p->Age = 20;
p->Height = 185;
// do something with it ...
free(p);
Another very important thing you have to know: malloc() can return NULL if the allocation fails. It actually means that there is no space to allocate the block. It is very important to actually check for it. For example:
int* v = malloc(sizeof(int)*999999999);
if (v == NULL)
{
printf("Allocation failed.");
return -1;
}
// Allocation successfull. Do something with the vector ...
Structures in structures and structures with pointers
If structures do not contain pointers just free() will do the job. Example:
typedef struct Point
{
int x;
int y;
}Point;
typedef struct Line
{
Point a;
Point b;
}Line;
// this will allocate actually 4 ints
Line* p = malloc(sizeof(Line));
// this is how you access each point's coordinates.
p->a.x;
// do something ...
// this will free both points (the 4 ints)
free(p);
Now this is where code can get very unclear depending on each situation. If a structure contains one or more pointers if might prove difficult to keep the code simple to read. Suppose the following struct:
typedef struct Person
{
int Age;
int Height;
char* FName;
char* LName;
char* Address;
}Person;
If you want to allocate a Person you need 4 malloc() calls. If you also add error checking it will be quite voluminous. Definetly this should be wrapped inside a function:
Person* AllocPerson()
{
// i'm skipping it now so that my point is clear, but checking
// if malloc returned NULL is recommended
Person* p = malloc(sizeof(Person));
p->FName = malloc(sizeof(char)*30);
p->LName = malloc(sizeof(char)*30);
p->Address = malloc(sizeof(char)*40);
return p;
}
Now whenever you need a Person you can just Person* p = AllocPerson();. Same goes with a FreePerson() function which will take 4 free() calls so that after you finished working with the struct, you simply call FreePerson(p).
void FreePerson(Person* p)
{
free(p->FName);
free(p->LName);
free(p->Address);
free(p);
}
You can evolve the AllocPerson function even further and turn it into a Create function:
Person* CreatePerson(int Age, int Height, char* FirstName, char* LastName, char* Address)
{
// i'm skipping it now so that my point is clear, but checking
// if malloc returned NULL is recommended
Person* p = malloc(sizeof(Person));
p->FName = malloc(sizeof(char)*30);
p->LName = malloc(sizeof(char)*30);
p->Address = malloc(sizeof(char)*40);
p->Age = Age;
p->Height = Height;
strcpy(p->FName, FirstName);
strcpy(p->LName, LastName);
strcpy(p->Address, Address);
return p;
}
Now you can just do this whenever you need a person:
Person* p = CreatePerson(20, 180, "Alex", "Boris", "Street nr. 5");
The CreatePerson() function both allocates and initialises fields of a Person instance. This aproach of making a Create and Delete function to a structure is widely used in C, especially when you have to use an already made API.
Important notes
Always free() memory even though after exiting main() your OS will take care of the blocks still allocated. This is considered good practice.
Try to use dynamic memory as less as possible. The Heap is slower than the Stack!
Try to reuse allocated space whenever possible. Allocating and Freeing are expensive operations!
You would also have to free(first->point). When you free(first), all that will deallocate is the bytes holding the struct first, which only holds a pointer. That alone will not touch the actual memory address pointed to by point; free(first->point) beforehand would cover that.
Learning C through "Learning C the hard way", and doing some of my own exercises. I stumbled upon the following problem.
Let's say I have the following structure:
struct Person {
char name[MAX_INPUT];
int age;
}
In main(), I have declared the following array:
int main(int argc, char *argv[]) {
struct Person personList[MAX_SIZE];
return 0;
}
Now let's say 2 functions away (main calls function1 which calls function2) I want to save a person inside the array I declared in the main function like so:
int function2(struct Person *list) {
struct Person *prsn = malloc(sizeof(struct Person));
assert(prsn != NULL); // Why is this line necessary?
// User input code goes here ...
// Now to save the Person created
strcpy(prsn->name, nameInput);
ctzn->age = ageInput;
list = prsn; // list was passed by reference by function1, does main need to pass the array by
// reference to function1 before?
// This is where I get lost:
// I want to increment the array's index, so next time this function is called and a
// new person needs to be saved, it is saved in the correct order in the array (next index)
}
So if I return to my main function and wanted to print the first three persons saved in it like so:
...
int i = 0;
for(i = 0; i < 3; i++) {
printf("%s is %d old", personList[i].name, personList[i].age);
}
...
Basically how to reference the array across the application while keeping it persistent. Keeping in mind that main does not necessarily call the function directly that makes use of the array. I'm suspecting someone might suggesting declaring it as a global variable, then what would be the alternative? Double pointers? How do double pointers work?
Thank you for your time.
Here are a few pointers (no pun intended!) to help you along:
As it stands, the line struct Person personList[MAX_SIZE]; allocates memory for MAX_SIZE number of Person structs. You don't actually need to allocate more memory using malloc if this is what you are doing.
However, you could save some memory by only allocating memory when you actually need a person. In this case, you want the personList array to contain pointers to Person structs, not the structs themselves (which you create using malloc).
That is: struct Person * personList[MAX_SIZE];
When you create the person:
struct Person * person = (struct Person *) malloc(sizeof(struct Person));
personList[index] = person;
And when you use the person list: printf("%s", personList[index]->name);
Arrays don't magically keep a record of any special index. You have to do this yourself. One way is to always pass the length of the array to each function that needs it.
void function1(struct Person * personList, int count);
If you wanted to modify the count variable when you returned back to the calling function, you could pass it by reference:
void function1(struct Person * personList, int * count);
A possibly more robust way would be to encapsulate the count and the array together into another structure.
struct PersonList { struct Person * list[MAX_SIZE]; int count; }
This way you can write a set of functions that always deal with the list data coherently -- whenever you add a new person, you always increment the count, and so on.
int addNewPerson(struct PersonList * personList, char * name, int age);
I think that much should be helpful to you. Just leave a comment if you would like something to be explained in more detail.
First of all, malloc does not guarantee to allocate new space from the memory and return it. If it cannot allocate the requested memory, it returns a NULL value. That's why it is necessary to check the pointer.
While you are calling function two, you can pass the address of the next element by using a variable that holds the current count of the array in function1;
function2(&personList[count++]);
then you return the current count from function1 to the main function;
int size=function1(personList);
I have a generic linked-list that holds data of type void* I am trying to populate my list with type struct employee, eventually I would like to destruct the object struct employee as well.
Consider this generic linked-list header file (i have tested it with type char*):
struct accListNode //the nodes of a linked-list for any data type
{
void *data; //generic pointer to any data type
struct accListNode *next; //the next node in the list
};
struct accList //a linked-list consisting of accListNodes
{
struct accListNode *head;
struct accListNode *tail;
int size;
};
void accList_allocate(struct accList *theList); //allocate the accList and set to NULL
void appendToEnd(void *data, struct accList *theList); //append data to the end of the accList
void removeData(void *data, struct accList *theList); //removes data from accList
--------------------------------------------------------------------------------------
Consider the employee structure
struct employee
{
char name[20];
float wageRate;
}
Now consider this sample testcase that will be called from main():
void test2()
{
struct accList secondList;
struct employee *emp = Malloc(sizeof(struct employee));
emp->name = "Dan";
emp->wageRate =.5;
struct employee *emp2 = Malloc(sizeof(struct employee));
emp2->name = "Stan";
emp2->wageRate = .3;
accList_allocate(&secondList);
appendToEnd(emp, &secondList);
appendToEnd(emp2, &secondList);
printf("Employee: %s\n", ((struct employee*)secondList.head->data)->name); //cast to type struct employee
printf("Employee2: %s\n", ((struct employee*)secondList.tail->data)->name);
}
Why does the answer that I posted below solve my problem? I believe it has something to do with pointers and memory allocation. The function Malloc() that i use is a custom malloc that checks for NULL being returned.
Here is a link to my entire generic linked list implementation: https://codereview.stackexchange.com/questions/13007/c-linked-list-implementation
The problem is this accList_allocate() and your use of it.
struct accList secondList;
accList_allocate(&secondList);
In the original test2() secondList is memory on the stack. &secondList is a pointer to that memory. When you call accList_allocate() a copy of the pointer is passed in pointing at the stack memory. Malloc() then returns a chunk of memory and assigns it to the copy of the pointer, not the original secondList.
Coming back out, secondList is still pointing at uninitialised memory on the stack so the call to appendToEnd() fails.
The same happens with the answer except secondList just happens to be free of junk. Possibly by chance, possibly by design of the compiler. Either way it is not something you should rely on.
Either:
struct accList *secondList = NULL;
accList_allocate(&secondList);
And change accList_allocate()
accList_allocate(struct accList **theList) {
*theList = Malloc(sizeof(struct accList));
(*theList)->head = NULL;
(*theList)->tail = NULL;
(*theList)->size = 0;
}
OR
struct accList secondList;
accList_initialise(secondList);
With accList_allocate() changed to accList_initialise() because it does not allocate
accList_initialise(struct accList *theList) {
theList->head = NULL;
theList->tail = NULL;
theList->size = 0;
}
I think that your problem is this:
You've allocated secondList on the stack in your original test2 function.
The stack memory is probably dirty, so secondList requires initialization
Your accList_allocate function takes a pointer to the list, but then overwrites it with the Malloc call. This means that the pointer you passed in is never initialized.
When test2 tries to run, it hits a bad pointer (because the memory isn't initialized).
The reason that it works when you allocate it in main is that your C compiler probably zeros the stack when the program starts. When main allocates a variable on the stack, that allocation is persistent (until the program ends), so secondList is actually, and accidentally, properly initialized when you allocate it in main.
Your current accList_allocate doesn't actually initialize the pointer that's been passed in, and the rest of your code will never see the pointer that it allocates with Malloc. To solve your problem, I would create a new function: accList_initialize whose only job is to initialize the list:
void accList_initialize(struct accList* theList)
{
// NO malloc
theList->head = NULL;
theList->tail = NULL;
theList->size = 0;
}
Use this, instead of accList_allocate in your original test2 function. If you really want to allocate the list on the heap, then you should do so (and not mix it with a struct allocated on the stack). Have accList_allocate return a pointer to the allocated structure:
struct accList* accList_allocate(void)
{
struct accList* theList = Malloc( sizeof(struct accList) );
accList_initialize(theList);
return theList;
}
Two things I see wrong here based on the original code, in the above question,
What you've seen is undefined behaviour and arose from that is the bus error message as you were assigning a string literal to the variable, when in fact you should have been using the strcpy function, you've edited your original code accordinly so.. something to keep in mind in the future :)
The usage of the word Malloc is going to cause confusion, especially in peer-review, the reviewers are going to have a brain fart and say "whoa, what's this, should that not be malloc?" and very likely raise it up. (Basically, do not call custom functions that have similar sounding names as the C standard library functions)
You're not checking for the NULL, what if your souped up version of Malloc failed then emp is going to be NULL! Always check it no matter how trivial or your thinking is "Ah sher the platform has heaps of memory on it, 4GB RAM no problem, will not bother to check for NULL"
Have a look at this question posted elsewhere to explain what is a bus error.
Edit: Using linked list structures, in how the parameters in the function is called is crucial to the understanding of it. Notice the usage of &, meaning take the address of the variable that points to the linked list structure, and passing it by reference, not passing by value which is a copy of the variable. This same rule applies to usage of pointers also in general :)
You've got the parameters slightly out of place in the first code in your question, if you were using double-pointers in the parameter list then yes, using &secondList would have worked.
It may depend on how your Employee structure is designed, but you should note that
strcpy(emp->name, "Dan");
and
emp->name = "Dan";
function differently. In particular, the latter is a likely source of bus errors because you generally cannot write to string literals in this way. Especially if your code has something like
name = "NONE"
or the like.
EDIT: Okay, so with the design of the employee struct, the problem is this:
You can't assign to arrays. The C Standard includes a list of modifiable lvalues and arrays are not one of them.
char name[20];
name = "JAMES" //illegal
strcpy is fine - it just goes to the memory address dereferenced by name[0] and copies "JAMES\0" into the memory there, one byte at a time.
Good day!
I need to use malloc in creating a student list system.... In order to be efficient, our professor asked us to use it on a struct so i created a struct as follows:
struct student {
char studentID[6];
char name[31];
char course [6];
};
struct student *array[30];
everytime i add a record, that is when i use malloc...
array[recordCtr]=(struct student*)malloc(sizeof(struct student));
recordCtr++;
then i free it like this.
for(i = 0; i < recordCtr; i++){
free(array[i]);
}
Am i using malloc properly??? what is the effect if i free it like this instead of the loop above.
free(array);
Thanks in advance. Your opinion will be highly appreciated.
You are doing fine.
free(array); would be undefined behavior because array itself was not allocated via malloc therefore you can't free it and don't need to - the memory will be managed by the compiler.
A good tip is to always do:
type *something;
something = malloc(n * sizeof(*something));
This is because, if you change the type of something, you don't have to change all sorts of other code. And sizeof is really a compiler operation here, it won't turn into anything different at runtime.
Also, don't cast the void* pointer returned by malloc, there's no reason to do so in C and it just further ties your code together.
So in your case, don't do:
(struct student*)malloc(sizeof(struct student));
but
malloc(sizeof(**array));
There is nothing illegal about the way you are using malloc but this isn't a list, it's an array of pointers.
To use a list you do not fix the size in advance and have a pointer to the next element. You can either make this intrusive of non-intrusive.
For an intrusive list you put struct student * next in the declaration of student.
For a non-intrusive list you create another struct student_list_node which contains an instance of struct student and a pointer struct student_list_node * next;
This is an exacmple of the non-intrusive version:
struct student_list_node
{
struct student data;
struct student_list_node * next;
};
struct student_list_node * head;
struct student_list_node * tail;
struct student_list_node * addStudentToTail()
{
struct student_list_node * newnode = (struct student_list_node *)(malloc( sizeof(struct student_list_node ) );
/* check malloc did not fail or use a checking vesrion of malloc */
if( !tail )
{
head = tail = newnode;
}
else
{
tail->next = newnode;
tail = newnode;
}
return newnode; // which is also "tail"
}
int main()
{
struct student_list_node * node = addStudentToTail();
struct student * pstud = &node->data;
/* write to pstud student details */
}
If you do really want to use an array, you might want to make it an array of student rather than student * in which case you can use calloc rather than malloc
struct student * array = (struct student *)calloc( 30, sizeof( student ) );
Then using free(array) would be the correct way to dispose of it. You also have the option of allocating more if you need it later with realloc. (Be careful with this one: you must keep a copy of your original pointer until you know realloc succeeds).
The array itself isn't allocated on the heap. Assuming it's a global variable, it is allocated in global memory at program startup and doesn't need to be freed. Calling free on it will probably corrupt your program.
Your current solution is correct.
What you're doing is correct.
You can think of *array[30] as an array of 30 pointers
When you are allocating memory for each of those pointers, you'd also need to call free() on each of them.
Yes, you are using it correctly. There are better ways to organize the storage than this, but this will work. At least until you need more than 30 students...
Note that you must call free() with each pointer that is returned by malloc(). That means that your loop over the array of pointers is the correct approach for your chosen architecture.
Your attempt to call free on the array itself will not work. It invokes Undefined Behavior because you are passing a pointer (to the base of the array itself) to free() that did not come from a call to malloc().
Looks fine.
You could (if it fits your problem) allocate space for all 30 structs in one go
struct student *array = (struct student *)malloc(30*sizeof(struct student));
whhen you want to dispose of the space, you can then do
free(array)
What you have will work just fine. As others have mentioned, you've created an array of pointers on the stack and need to malloc and free each of them individually as you are doing.
However, you don't have to use malloc and free one struct at a time, you can do something like this:
int arraySize = 30;
student * ourArray = (student*)malloc(sizeof(student) * arraySize);
and a single free on the pointer will take care of it. with this pointer, you can still use the bracket notation and the compiler will understand that it's a typed pointer and behave appropriately, giving you essentially the same thing. Which method you use depends on if you need your array a dynamic size or not as well as personal preference.
Hope that helps some.
Initialize your array of pointer to struct student with NULL values
for(i = 0; i < recordCtr; i++){
array[i] = NULL;
}
Free memory if array[i] is not NULL
for(i = 0; i < recordCtr; i++){
if(NULL != array[i])
{
free(array[i]);
}
}
There's simple rule: Every malloc() should be paired with free() with pointer returned by malloc. Not less, not more.