Freeing char* memory in struct without losing the string - c

I assign a string in my structs like this:
struct REC {
char *name;
char *number;
};
records[records_size - 1].name = NAME;
records[records_size - 1].number = NUMBER;
where NAME and NUMBER are *char.
When I try to free() them in runtime, my saved struct loses the data (I suppose that's because it's a pointer and I freed that pointer indirectly).
My question is: how am I supposed to free memory without losing string data in all structs?
Only thing I can think of is saving all pointers somewhere and then free()ing them in a for loop at the end.

Instead of just assign the pointer NEME to records[rocords_size -1], you can allocate a separate memory for records[rocords_size -1], then using strcpy to copy the data from NAME pointer.
Once you copied the data we can free NAME pointer which will not affect data in records[rocords_size -1].

Related

Is the value stored on the stack? C

I'm currently studying C and im exploring how a generic Binary Tree is implemented and I stumbled upon this example.
I have this node struct:
struct node{const char * value; struct node * left; struct node * right;};
typedef struct node node;
And this function that creates a tree:
node * create_tree(const void * value, node * left, node * right) {
node * root = malloc(sizeof(node));
if (root) {
root->value=value;
root->left=left;
root->right=right;
}
return root;
}
Then this is used in the main as such:
node * root = create_tree("b", create_tree("a", NULL, NULL), create_tree("c", NULL, NULL));
I understand what the result of this is. What I want to make sure I understand is where in memory "b", "a" and "c" end up. From what I understand these are equivalent to a char pointer (since they are in double quotes) and thus my thinking is that the characters 'b' and '\0' are stored on the stack and are pointed to from the heap. Is that what actually happens?
And another question since the struct definition takes a void pointer does create_tree allocate space on the heap based on the argument type once it is called?
String literals such as "a", "b" and "c" are normally stored in read-only memory. They won't be stored on the stack.
If you want them to be stored on the stack, you will have to allocate an array on the stack and copy the content of the string literal into that array. You are merely copying the pointer to the string literal and storing that pointer in the heap (memory allocated by malloc), not the stack.
You are possibly confusing char arrays with char *. Here is an explanation of the differences:
int main(void)
{
//This will be stored on the stack, because although it
//is using a string literal for initialization, the
//array itself is declared as a local variable.
char str1[] = "Test";
//This is identical to the previous declaration, except
//for the fact that the length of the array is specified
//explicitly.
char str2[5] = "Test";
//In this case, only the pointer will be stored on the
//stack, but not the string itself, because the pointer
//is pointing to a string literal, which is probably
//stored in read-only memory.
char *str3 = "Test";
}
And another question since the struct definition takes a void pointer does create_tree allocate space on the heap based on the argument type once it is called?
The function create_tree is only aware of the value of the pointer value, but not the type of object that it is pointing to. Therefore, it is unable to allocate space based on the type of the referenced object. Also, it will only allocate space on the heap if you tell it to, for example using the function malloc (which you do call once in that function).

Does malloc assigns memory to custom struct's properties?

I've been working on some C projects and was wondering if I create a custom structure, for example, Student, define a variable of the custom structure type, and allocate memory to it using malloc, does it also allocate memory for variables' properties separately or are they all kept in the same space? if yes, will there be any difference if I allocate memory using malloc separately for every property?
For example:
typedef struct {
unsigned int ID;
char *first_name;
char *last_name;
int num_grades;
float *grades;
unsigned short int days_absent;
char *memo;
} Student;
int main() {
// Declare students array
Student *students = NULL;
int students_size = 0;
// Allocate memory for students array
students = (Student *) malloc(sizeof(Student));
return 0;
}
That allocates enough memory for the struct, which includes enough memory for ID, first_name, etc and all padding requirements.
Note that while it allocates memory for the pointer first_name, it doesn't allocate a buffer to hold the name. It just allocates memory for first_name, a pointer. If you want memory in which to store the names, you will need to allocate it.
If the struct had a char first_name[40]; field, it would be a different story. To allocate enough memory for first_name, it needs to allocate enough memory for an array of 40 char instead of enough for a pointer. This does provide a space in which a string could be stored.
No, it doesn't. It allocates uninitialized memory for the number of chars you want - which is usually a calculation based on sizeofs.
If you want it to allocate memory to store values that your struct has pointers to, you'll have to add that after having allocated the memory for the struct.
You'll also have to "go backwards" when you free such a struct.
Example:
typedef struct {
char *data;
} foo;
foo *foo_create() {
foo *retval = malloc(sizeof *retval ); // try allocation
if(retval == NULL) return NULL; // check that it worked
retval->data = malloc(10) ; // allocate something for a member
if(retval->data == NULL) { // check that it worked
free(retval); // oh, it didn't, free what you allocated
return NULL; // and return something to indicate failure
}
return retval; // all successful
}
void foo_free(foo *elem) {
if(elem != NULL) { // just a precaution
free(elem->data); // free the member's memory
free(elem); // then the memory for the object
}
}
Does it also allocate memory for variables' properties separately
No. After allocating for students, allocate for students->first_name, students->last_name, etc.
does it also allocate memory for variables' properties separately or are they all kept in the same space?
No. malloc() is given a size to indicate how much contiguous memory to allocate, and it returns a pointer pointing to it... malloc() knows nothing about what you are going to do with the pointer. When you assign it to a pointer variable to Student type is, somehow, dressing a bunch of memory with structure. But the char * fields that you have defined inside (or if you have other fields pointing to other structured data) those have to be allocated separately (or ask for more memory to allocate them all in the same returned segment, but this requires practice and knowledge of the alignment issues that arise from it)

double linked list in c send data from function

This is my code:-
typedef struct Frame
{
char* name;
unsigned int duration;
char* path; // may need to scan (with fgets)
}frame_t;
typedef struct Link
{
frame_t* frame;
struct Link* next;
}link_t;
void addNewFrame(void)
{
link_t* newLink = (link_t**)malloc(sizeof(link_t*));
printf(" *** Creating new frame ***\n\n");
printf("Please insert frame path:\n");
// newLink->frame->name = (char*)malloc(sizeof(char) * MAX_LEN);
fgets(newLink->frame->name, MAX_LEN,stdin);
printf("%s", newLink->frame->name);
}
I just need to add a data to name variable in the "Frame" link list, please help me by reviewing this code.
You want to allocate the right types here:-
link_t* newLink = malloc(sizeof(link_t)); //Pointer to Link st
if(newLink){
newLink->frame = malloc(sizeof(frame_t)); //Pointer to frame member
if(newLink->frame){
newLink->frame->name = malloc(sizeof(char) * MAX_LEN); //Pointer to name member
if(newLink->frame->name){
//Rest of your code
}
}
}
EDIT:-
1. As pointed out in comments there's no need to cast the pointer returned by malloc()
2. Another very imp point you may want to check validity of the pointers before de-referencing them
First. You don't need to cast void * so (link_t **)malloc(... can be only malloc(....
Second. You allocated enough memory for a pointer not for a struct. I think you mean malloc(sizeof(link_t)) or even better malloc(sizeof(*newLink))
Third newLink->frame is a pointer so you need to allocate data for it too, newLink->frame = malloc(sizeof(frame_t))
Fourth newLink->frame->name is still a pointer so you need to allocate data for it too. newLink->frame->name = malloc(MAX_LEN)
The confusion that you are doing is pretty common. When you say type *something you are allocating a pointer to type in the stack. A pointer needs to point to somewhere else or NULL, or bad things happen. This applies to structures too. If your structure has a pointer member you need to point it to somewhere else. The somewhere else is where the real "type" object resides.
This also applies to arrays. If you say 'int foo[10]' you are allocating ten integers in the stack. If you say int *foo[10] you are allocating ten pointers in the stack. If you say int **foo you are allocating one pointer in the stack. Again all pointers need to be initialized, I mean, they need to point to some valid object, allocated somewhere else in the memory.
I hope this helps.
Some other points.
Always check pointer coming from malloc, if allocation failed you'll receive NULL. Dereferencing NULL will break your program.
Always initialize memory coming from malloc, fill it with zeros or something.
Don't use _t suffix, is POSIX reserved.
I didn't test any of this.

Using strncpy on struct

Let's say I have this student struct defined:
struct student {
char *name;
};
typedef struct student Student
Now I have the following function:
void add_student(const char *student_name) {
// create new student
Student *new_s;
new_s = malloc(sizeof(Student));
strncpy(new_s->name, student_name, sizeof(new_s->name) - 1)
}
I want to add the student_name to the name of the new student struct. However because const char and char are different I have to use strncpy.
I tried it this way, however I get a segmentation fault, what's wrong?
You are only allocating memory for the structure new_s in this line
new_s = malloc(sizeof(Student));
This includes the variable char* name, which is a pointer to a char. Although, you also need memory to which this pointer will point at.
So, you need to allocate memory for the character pointer name inside the structure.
// create new student
Student *new_s;
new_s = malloc(sizeof(Student));
new_s->name = malloc(100); //assuming you need to store a string of len 100
As Johan Wentholt correctly outlined in his answer, you must allocate memory for both the Student structure and the string its member name points to, but you must return the new structure so the caller can do something with it:
Student *add_student(const char *student_name) {
Student *new_s = malloc(sizeof(Student));
if (new_s) {
new_s->name = strdup(student_name);
}
return new_s;
}
You code invokes undefined behavior because you did not allocate memory for the string, worse even, you left the name member uninitialized (malloc does not initialize the memory it returns).
Furthermore, you should not use strncpy. It is not some safe version of strcpy, it is a very error prone function, whose semantics are poorly understood by most programmers. NEVER use this function. If you see it used, there are chances you are either in front of a bug or there is a better method to replace it.
For completeness, your code:
strncpy(new_s->name, student_name, sizeof(new_s->name) - 1);
Would attempt to copy at most sizeof(char*)-1 characters from student_name into the array pointer to by new_s->name.
If the student_name is longer, the destination will not be null terminated,
If it is shorter, the destination will be padded with null bytes upto the given size.
Here the destination pointer is uninitialized and the size information is bogus anyway: you really want to copy all characters in the string plus the null terminator, which is exactly what strcpy does. But you need to allocate enough memory for that. You could use:
new_s->data = malloc(strlen(student_name) + 1);
strcpy(new_s->data, student_name);
The Posix function strdup() does both operations in one call:
new_s->data = strdup(student_name);
Haris gave a good solution. But like Florian Zwoch said in the comments you can also use strdup like so:
void add_student(const char *student_name)
{
Student *new_s = malloc(sizeof(Student));
new_s->name = strdup(student_name);
}
Keep in mind that you have to free new_s->name and than free new_s.
You should also check the return value of malloc and strdup for NULL value. Since it returns NULL if insufficient memory is available.
As side note, you can shorten up the struct and typedef to one statement like so:
typedef struct student {
char *name;
} Student;

C generic linked-list

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.

Resources