I was writing a singly-linked list in C and cannot understand this following code.
#include <stdlib.h>
typedef struct ListNode {
int val;
struct ListNode* next;
} ListNode;
int main() {
/*Say I allocate this list to say 1->2->3->4->NULL*/
ListNode* node = malloc(sizeof(ListNode));
ListNode* n1 = node; // An ordinary pointer
ListNode* n_heap = malloc(sizeof(ListNode)); // A heap allocated pointer
n_heap = node->next; // 2->3->4->NULL
n1->next = NULL;
}
Now from the above example, I had assumed that n_heap to be NULL as well. But even after n1->next = NULL;, n_heap is still 2->3->4->NULL. So is the node->next copied to n_heap? or is it that n_heap now points to the original heap located node->next, and the n1->next now set to NULL? Does this mean node wasn't initially the owner of node->next data?
int main() {
/*Say I allocate this list to say 1->2->3->4->NULL*/
ListNode* node = malloc(sizeof(ListNode));
node is a pointer to dynamically allocated data. On a C implementation with a stack / heap distinction, that will be on the heap.
ListNode* n1 = node; // An ordinary pointer
n1 now points to the same dynamically-allocated data that node does. It is no more or less "ordinary" than node is.
ListNode* n_heap = malloc(sizeof(ListNode)); // A heap allocated pointer
(On success) n_heap points to different dynamically allocated data. The pointer itself has the same scope and storage duration as node and n1, and is exactly as "ordinary" as they are.
n_heap = node->next; // 2->3->4->NULL
Now the memory to which n_heap previously pointed is leaked, because no pointers to it remain. n_heap points to the same memory that node->next does. Which is the same memory that n1->next does.
n1->next = NULL;
Now the value of the pointer object accessible as n1->next (and also as node->next) is set to NULL. This has no effect on the data to which that pointer object previously pointed, nor on variable n_heap, which still points to the data in question.
}
Now from the above example, I had assumed that n_heap to be NULL as
well. But even after n1->next = NULL;, n_heap is still 2->3->4->NULL.
You are failing to distinguish between pointer objects and the objects to which their values point. Assigning NULL to n1->next sets the value of one pointer object. It does nothing to the object to which the old pointer value pointed. It does nothing to other, distinct pointers to the same object. And on the other hand, n_heap is not 2->3->4->NULL, It is a pointer distinct from all those nodes, whose value points to the node containing 2.
So is the node->next copied to n_heap?
Yes, that's what an assignment does, but not in the sense I think you mean. Again, assigning n_heap = node->next copies the pointer value stored in node->next to n_heap. Those two distinct pointer objects then both contain values pointing to the same data.
or is it that n_heap now points
to the original heap located node->next, and the n1->next now set to
NULL?
Yes and no. Again, neither the object designated by n_heap nor the one designated by node->next contain the data you are describing as 2->3->4->NULL. Instead, they both contain the address of the node containing the first of those values.
Does this mean node wasn't initially the owner of node->next
data?
It depends on what you mean by "owner". Certainly neither node nor *node is a container of the data to which node->next points. As the term "owner" is usually applied to pointers, it is about responsibility for freeing the pointed to data, not about storage layout. Responsibility to free is a question of data and control flow in the program, not a property of the data itself.
Related
I am trying to understand the pointer logic behind prepending.
I declared a struct as follows:
typedef struct myList{
int info;
struct myList *link; //self referential structure;
} Node;
For memory allocation in the heap memory segment, I use the following function:
Node *getNode(){
return ((Node *)malloc(sizeof(Node)));
}
In the main function, I allocate memory for the first node, I assign its link to NULL and its value to 2.
Node *head = getNode();
head -> link = NULL;
head -> info = 2;
Then comes the prepend function:
void prepend(Node **headPointer, int value) {
Node *new_node;
new_node = getNode();
new_node -> info = value;
new_node -> link = *headPointer;
*headPointer = new_node;
}
I am using the following function call:
prepend(&head, 5)
As you can see, I'm using a pointer to a pointer. I store the address of head in headPointer. I create new_node and allocate memory to it. I assign its info field, then the link field gets the dereferenced headPointer, which is the value stored in head, which is in turn the address for the chunk of memory in the Heap segment.
So, I basically link new_node to head, right? Now comes the confusing part, for me. The dereferenced headPointer, which is head's pointed chunk of memory in the Heap segment, gets the value stored in new_node which is another address from the Heap segment, I guess. Then, both new_node and headPointer go out of scope. (?)
How does this all add up? Is there a simpler way to describe the situation or implement prepending?
Then, both new_node and headPointer go out of scope. (?)
At the end of the prepend() newnode goes out of scope but not the memory allocated since it is allocated on heap.If it were something like int a, then at the end of prepend(), a is gone out of scope and referencing a after that would be undefined behavior.Please read this and this to know about heap.
Also since you pass head of the list as pointer to pointer, when you change what the headPointer points to inside prepend(), it is reflected outside the function so you still have a pointer to the head of the list.
|2|-->NULL
^
|
head
After call to prepend()
1) |5|--> |2|-->NULL
^
|
head
2) |5|----> |2|--->NULL
^
|
head
Also remeber to have some way of accessing the heap allocated memory in order to deallocate it.If you don't have any means of pointing to a memory allocating on a heap, then you are left with a memory leak.
I have a type node whose pointer is being used in another struct as shown below.
typedef struct element {
void* data;
struct element *next;
} node;
typedef struct queue {
node *tail;
node *head;
int num_items;
} queue_t;
I create an empty queue using the following code, but I am not sure if head and tail should be set to NULL since temp is not pointing anywhere yet.
queue_t *temp;
temp = malloc(sizeof(queue_t));
if (temp == NULL){
return NULL;
}
temp->head = NULL;
temp->tail = NULL;
temp->num_items = 0;
As per my understanding, malloc will only make temp point to some address space whose size is equal to the size of the struct queue_t. The address space does not contain a valid queue element yet. So how are temp->head = NULL; and temp->tail = NULL; valid statements?
Can someone please explain why this works?
The address space does not contain a valid queue element yet.
Correct, the allocated memory only contains a queue_t
So how are temp->head = NULL; and temp->tail = NULL; valid statements?
head and tail are not part of struct element. head and tail are part of queue_t. You have allocated a queue_t so it is OK to assign values to head and tail. In this case you assign the NULL value to show that they don't point to anything valid yet.
When you allocate a node (aka struct element) you update head and tail like:
// Add first node
temp->head == malloc(sizeof(node));
temp->tail == temp->head;
if (temp->head == NULL){
return NULL;
}
temp->num_items = 1;
// Initialize the new node
temp->head->next = NULL;
temp->head->data = NULL;
// Note: Adding more node requires more complex code
What is the definition of a "valid queue element"? If it's "sufficient space to hold a queue element and where the locations that hold the head and tail pointers have valid values", then setting them, to NULL makes it valid. If it's not that, what is it?
As per my understanding, malloc will only make temp point to some
address space whose size is equal to the size of the struct queue_t.
Correct.
The address space does not contain a valid queue element yet.
Not sure what you what you meant by "valid", but that statement is also correct.
So how are temp->head = NULL; and temp->tail = NULL; valid statements?
It is precisely those statements that makes your allocated space a valid queue element!
Can someone please explain why this works?
Your question fundamentally is no different from a statement such as int i;. Your implementation sets aside a space to hold an integer. However, it is as yet invalid because you have not given it any (meaningful) value. Once you set i = 0; or i = 42;, the space that you call i is now a valid integer.
Hope that helps.
As per my understanding, malloc will only make temp point to some address space whose size is equal to the size of the struct queue_t.
The malloc function call returns an address to the beginning of the allocated memory of size specified in the argument of malloc function call(in bytes). The allocated memory space will be of size specified in the argument of the malloc. However, the address returned by malloc will be the beginning of that memory space. Therefore, you can access upto the size of the memory space safely using the pointer to that memory space.
The address space does not contain a valid queue element yet.
The C Standard library has allocated a valid memory space for your pointer variable temp to point to. However, the values stored at that memory space could be garbage. Therefore, the pointer to node and num_items data members which have some valid memory space allocated to them within your queue_t may have garbage value. For example, after allocating the memory for queue_t, you can try to print the value of num_items using printf function.
queue_t *temp = malloc(sizeof(queue_t));
if (temp == NULL){
return NULL;
}
printf("The value of num_items: %d\n", temp->num_items);
The above example may print any garbage value. Since, C language doesn't have constructors to initialize newly created variables, you should initialize every variable you create with some stable value.
You can also use calloc function call which also sets allocated memory to zero after allocating the memory space.
So how are temp->head = NULL; and temp->tail = NULL; valid statements?
The memory is allocated by malloc which may contain any garbage value. The data members of queue_t share that memory space. Since, memory space can have garbage data, the data members will be having any random garbage data. That's why it is a good approach to initialize data members of the struct allocated by malloc to some stable values. That's what you have done in your program.
Actually, temp's data members head and tail should point to the addresses of variables of type node. The malloc call has allocated the pointer variables. These pointer variables can point to any variable of type node (or store the address of variable of type node). But you haven't allocated any node variable yet and you don't want to create dangling pointer. Therefore, you should initialize these pointer variables with NULL.
Your program should look like this:
queue_t *temp;
temp = malloc(sizeof(queue_t));
if (temp == NULL){
return NULL;
}
temp->head = NULL;
temp->tail = NULL;
temp->num_items = 0;
//Allocate some node
node *n = malloc(sizeof(node));
int data = 1;
n->data=&data;
n->next=NULL;
temp->head=n;
temp->tail=n;
temp->num_items=1;
Feel free to edit the title if it doesn't make sense. So I've been using malloc for a while without really being completely sure of how it works. If I create an int pointer and set it equal to malloc(10*sizeof(int), I figure that it allocates a block in memory of 10 times the size of one int, then returns the starting address of this allocation, but nothing is actually initialized within that memory yet. Am I OK so far?
Now say I create a struct for linked list nodes like this:
typedef struct node_ {
int data;
struct node_ *next;
} Node;
and then have a create linked list function:
Node* LLCreate(int data) {
Node *head = malloc(sizeof(Node));
if ( head != NULL) {
head->next = NULL; // don't get how malloc created `next`
head->data = data; // or `data`
}
return head;
}
What I don't get, and can't seem to google appropriately, is, if head is just the starting location in memory of an empty block of size Node, and not an actual variable, how does it have the local variables of next and data?
Maybe this is a nonsensical question because I have some fundamental misunderstanding of what's going on here, but if anyone understands what I'm trying to ask and could clear this up for me I'd really appreciate it.
It doesnt have local variables like next and data.
next and data are just used to calculate the address relative to from where node is pointing.
When you do node->data then the address is calculated as address pointed by node + (total bytes required by any metadata for malloc) + 0 because data is the first member of node struct.
And when you do node->next then the address is calculated as address pointed by node + (total bytes required by any metadata for malloc) + sizeof(data) , because next is the second member of the node struct that comes after data member.
My question is an extension of this: Returning pointer to a local structure
I wrote the following code to create an empty list:
struct node* create_empty_list(void)
{
struct node *head = NULL;
return head;
}
I just read that returning pointers to local variables is useless, since the variable will be destroyed when the function exits. I believe the above code is returning a NULL pointer, so I don't think it's a pointer to a local variable.
Where is the memory allocated to the pointer in this case. I didn't allocate any memory on the heap, and it should be on the stack, as an automatic variable. But what happens when the code exits (to the pointer), if I try to use it in the program, by assigning this pointer some pointees / de-referencing and alike?
struct node* create_empty_list(void)
{
struct node *head = NULL;
return head;
}
is equivalent to:
struct node* create_empty_list(void)
{
return NULL;
}
which is perfectly fine.
The problem would happen if you had something like:
struct node head;
return &head; // BAD, returning a pointer to an automatic object
Here, you are returning the value of a local variable, which is OK:
struct node* create_empty_list()
{
struct node* head = NULL;
return head;
}
The value of head, which happens to be NULL (0), is copied into the stack before function create_empty_list returns. The calling function would typically copy this value into some other variable.
For example:
void some_func()
{
struct node* some_var = create_empty_list();
...
}
In each of the examples below, you would be returning the address of a local variable, which is not OK:
struct node* create_empty_list()
{
struct node head = ...;
return &head;
}
struct node** create_empty_list()
{
struct node* head = ...;
return &head;
}
The address of head, which may be a different address every time function create_empty_list is called (depending on the state of the stack at that point), is returned. This address, which is typically a 4-byte value or an 8-byte value (depending on your system's address space), is copied into the stack before the function returns. You may use this value "in any way you like", but you should not rely on the fact that it represents the memory address of a valid variable.
A few basic facts about variables, that are important for you to understand:
Every variable has an address and a value.
The address of a variable is constant (i.e., it cannot change after you declare the variable).
The value of a variable is not constant (unless you explicitly declare it as a const variable).
With the word pointer being used, it is implied that the value of the variable is by itself the address of some other variable. Nonetheless, the pointer still has its own address (which is unrelated to its value).
Please note that the description above does not apply for arrays.
As others have mentioned, you are returning value, what is perfectly fine.
However, if you had changed functions body to:
struct node head;
return &head;
you would return address (pointer to) local variable and that could be potentially dangerous as it is allocated on the stack and freed immediately after leaving function body.
If you changed your code to:
struct node * head = (struct node *) malloc( sizeof( struct node ) );;
return head;
Then you are returning value of local value, that is pointer to heap-allocated memory which will remain valid until you call free on it.
Answering
Where is the memory allocated to the pointer in this case. I didn't
allocate any memory on the heap, and it should be on the stack, as an
automatic variable. But what happens when the code exits (to the
pointer), if I try to use it in the program, by assigning this pointer
some pointees / de-referencing and alike?
There is no memory allocated to the pointer in your case. There is memory allocated to contain the pointer, which is on the stack, but since it is pointing to NULL it doesn't point to any usable memory. Also, you shouldn't worry about that your pointer is on the stack, because returning it would create a copy of the pointer.
(As others mentioned) memory is allocated on the stack implicitly when you declare objects in a function body. As you probably know (judging by your question), memory is allocated on the heap by explicitly requesting so (using malloc in C).
If you try to dereference your pointer you are going to get a segmentation fault. You can assign to it, as this would just overwrite the NULL value. To make sure you don't get a segmentation fault, you need to check that the list that you are using is not the NULL pointer. For example here is an append function:
struct node
{
int elem;
struct node* next;
};
struct node* append(struct node* list, int el) {
// save the head of the list, as we would be modifying the "list" var
struct node* res = list;
// create a single element (could be a separate function)
struct node* nn = (struct node*)malloc(sizeof(struct node));
nn->elem = el;
nn->next = NULL;
// if the given list is not empty
if (NULL != list) {
// find the end of the list
while (NULL != list->next) list = list->next;
// append the new element
list->next = nn;
} else {
// if the given list is empty, just return the new element
res = nn;
}
return res;
}
The crucial part is the if (NULL != list) check. Without it, you would try to dereference list, and thus get a segmentation fault.
I'm making a Binary Tree as a part of my homework.
This is the given struct:
typedef struct TreeNode {
int data;
struct TreeNode* left;
struct TreeNode* right;
}
My build_tree function is recursive, and this is the prototype:
void build_tree(TreeNode** root, const int elements[], const int count);
The homework is meant to partly test dynamically allocated memory. So my problem keeps happening when I try to assign a value to one of the pointers inside the struct. I have seen questions similar to this, but it never seems to be this question exactly, but still involves structs and pointers. If I misunderstood, I apologize for duplicating questions.
The build_tree method has to be done recursively
This is my code for when an element should be inserted to the right of the root:
if(elements[0] > (*root)->data){
TreeNode newnode = {elements[0], NULL, NULL}; //make a node to add
*((*root)->right) = newnode; //dereference the root.right pointer, and set to newnode (GIVES COMPILE ERROR HERE)
struct TreeNode **rightptrptr = malloc(sizeof((*root)->right)); //allocate a pointer to a pointer
*rightptrptr = (*root)->right; //dereference pointer to a pointer, assign to root.right pointer
build_tree(rightptrptr, new_elems, count - 1);
}
If it's important, the root node has been initialized to {an integer, NULL, NULL}.
My understanding of pointers isn't all that sophisticated, so please forgive me if this code is horrendous.
There are many issues here, I'll try to point them out:
TreeNode newnode = {elements[0], NULL, NULL};, this allocates the struct on the stack, which means that the address of newnode (&newnode) won't be valid anymore when exiting the scope of the function.
if you need to build the tree by dynamically allocating the nodes TreeNode newnode = {elements[0], NULL, NULL} is not what you are looking for. This is not a dynamically allocated object, it's on the stack and the only thing you can do it with it to copy the content to an allocated node. You need always to allocate TreeNode* node = calloc(sizeof(TreeNode)) in your situation
((*root)->right) = newnode, here you dereference the pointer to assign newnode to it. It could work but only if right points to allocated memory, which is not the case since your root initializes it to NULL. You should instead allocate directly the pointer, eg root->right = calloc(sizeof(TreeNode))
struct TreeNode **rightptrptr = malloc(sizeof((*root)->right)), here you allocate a pointer to a pointer to a TreeNode because the recursive function expects this but your approach is wrong. You should pass the pointer to an existing subtree, not allocating one with no purpose, you can do it by doing, for example &root->right.