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).
Related
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].
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.
I was reading Skeina's book. I could not understand this code. Basically what is the use of double pointer. And what is the use of *l = p? Can anyone please explain by diagram.
void insert_list(list **l, item_type x) {
list *p; /* temporary pointer */
p = malloc(sizeof(list));
p->item = x;
p->next = *l;
*l = p;
}
You shouldn't call it a "double pointer" because that would be a pointer to a double. It is a pointer to a pointer, and it is used to allow the function to alter the value of an argument which happens to be a pointer. If you're familiar with C#, it's like an out argument. In this situation the l argument used to get both IN and OUT behavior, but you may often see this used for output only.
Since this function returns type void it very well could have been written without the use of the pointer to a pointer like this:
list * insert_list(list *l, item_type x) {
{
list *p; /* temporary pointer */
p = malloc(sizeof(list));
p->item = x;
p->next = l; // note that this is not *l here
return p;
}
This change would require the code that calls the function to update it's own handle to the list since the head of the list is what's being changed.
This function performs a very simple task: it inserts a list node at
the position for which it receives a pointer. There is nothing
special about double pointers, they are just pointers to pointers. They hold the address of a pointer, which contains the address of an object.
void **l contains the address of a list * pointer. *l retrieves
this address and *l = p stores it.
malloc is used to allocate a
list structure, p receives the address of the allocated structure.
The code is somewhat sloppy as p is not checked for NULL before
dereferencing it. If malloc fails because of lack of memory, the
program will invoke undefined behaviour, hopefully stopping with a
segmentation fault or something potentially worse.
The node is initialized, its next pointer is set to the node pointed
to by the l argument, and finally the new node's address is stored
at the address passed as the l argument. The effect is simple: the node is inserted at *l.
This method is clever ad it allows the same function to insert a new node anywhere is a list. For example:
list *head = NULL;
...
/* Insert a LIST_A node at the beginning of the list */
insert_list(&head, LIST_A);
...
/* insert a LIST_B element as the second node in the list */
insert_list(&head->next, LIST_B);
...
/* find the end of the list */
list *node;
for (node = head; node->next; node = node->next)
continue;
/* insert a LIST_Z node at the end of the list */
insert_list(&node->next, LIST_Z);
The only tricky thing above is the concept of pointer itself, here is a simple overview:
Memory can be conceptualized as a (large) array of bytes, addresses are offsets in this array.
char variables by definition are single bytes,
int variables occupies a number of bytes specific to the architecture of the system, typically 4 or 8 bytes in current hardware.
Think of pointers as variables holding the address in memory of another variable. They need to be large enough to hold any valid address in the system, in current systems with more than 4 GB of physical and addressable memory, they are 64 bit long and occupy 8 bytes.
There is a special address value NULL which represents no object and is used to specify that a given pointer does not point to any real object. The address 0 is used for this purpose. malloc will return NULL if it cannot allocate the memory requested, the return value should be tested, as storing a value at this address is forbidden and usually caught as an invalid access (segmentation fault).
This summary is purposely simplistic. I used the term variable instead of object to avoid the confusion with OOP concepts.
I have declared a structure in my .h file, as such:
struct node{
char* string;
}
I intend this to have created a structure, example, with one member, a character pointer named string.
Now, I figure out how long the string is and malloc an array of appropriate size, taking the input with that.
char* test;
test = (char*) malloc( n * sizeof(char) );
Insofar as I am aware, this has created character pointer test, and has assigned it to point at the head of the array I just malloc'd. I then proceed to assign each array slot to a character that the user has entered, and I read it back out - this all compiles and works appropriately. My problem comes when I try to assign this character pointer to the character pointer in a structure node passed in to this structure, as:
int f1( struct node* new ){
So I try to assign the pointer in the structure to the value of the pointer to the array, like so:
new->string = test;
But I segfault.
To me, this seems like I am assigning a char* to something that expects a char* so this should be working fine...I'm probably missing something stupid, but does anyone have a direction to point me in? Thanks much!
Check your caller of f1() and make sure your parameter is valid.
This will work:
struct node *you = malloc(sizeof(struct node));
if (you)
{
f1(you);
free(you);
}
As will this:
struct node me;
f1(&me);
This will NOT work
struct node *me;
int f1(me);
Some observations to note:
sizeof(char) is one by definition.
Recommended way of calling malloc is one that avoids casts as well as gets sizeof to infer the type from the pointer being allocated. (See below)
Avoid using names such as new for your identifiers (to the extent possible).
Remember to free the string member within node when you are done.
To elaborate #2, see below --
struct MyType *foo;
foo = malloc( n * sizeof *foo ); /* allocate n elements where n > 0 */
For some reason, if you chose foo to be of some other type, the malloc call is one less line for you to worry about. Also, should you forget to include stdlib.h, the compiler will let you know about it.
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.