Accessing struct pointer within another struct in C - c

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;

Related

Understanding how freeing a head allocated pointer in structures work in C

So I have this NODE structure
typedef struct tNODE {
struct tNODE* parent;
char* tag;
}NODE;
and I have a free function that goes through all the nodes and frees them.
Also I have a new node function that simply mallocs the amount of a Node structure and sets everything to NULL except parent.
NODE* new_node(NODE* parent){
NODE* result = malloc(sizeof(NODE));
result->inner_text = NULL;
result->parent = parent;
}
My question is that if for example I have the following code
NODE* temp = new_node(parent);
temp->tag = "x"
and
NODE* temp = new_node(parent);
temp->tag = strdup("x");
I can free the strdup("x") version with free(temp->tag) but not the temp->tag = "x". so I have 2 Questions.
1-:Why is this happening ? isnt there a memmory allocated when the new_node function is called ?
2- If I have a code with the first version(that means temp->tag = "x") and I want to free all the nodes, Does that mean that I have to only call free(temp) and the tag will also get free'd ? Thank you
the abbreviated answer is that you cannot free the memory in case of the first example, the detailed reason is that:
in case of the first version when you typed:
NODE* temp = new_node(parent);
temp->tag = "x";
this created a null-terminated string stored in the read-only memory, not the heap memory, to be more specific in the .rodata section of the memory which is just a section of the memory where you can only read but not modify strings, so if you write a code like:
NODE* temp = new_node(parent);
temp->tag = "x";
temp->tag[0] = 's';
the line temp->tag[0] = 's'; will throw a segmentation error as you are trying to modify a memory that can't be modified, that's why they say that the strings are immutable, which means that in case of the first example code, you cannot free the memory pointed by the pointer because the "x" will always be there in the read-only memory at any time of the execution of the program.
in the second case when you wrote:
NODE* temp = new_node(parent);
temp->tag = strdup("x");
you can refer to strdup() manual where they said:
The strdup() function returns a pointer to a new string which is a
duplicate of the string s. Memory for the new string is obtained
with malloc(3), and can be freed with free(3).
which means that the "x" is stored in the heap memory which is modifiable and readable memory, so you can modify it and read it and free it at any time.

How to return a pointer struct array from a function in C

Consider you have a function and the function output will be the Head and Tail address of a Linked List (a function for copy a Linked List in another address):
struct path *copyPath(struct path *head) {
struct path *temp = malloc(sizeof (struct path));
// array of newHead and newTail
struct path *resultPath[2];
struct path *newHead = NULL;
struct path *newTail = NULL;
while (head != NULL) {
temp = malloc(sizeof (struct path));
if (newHead == NULL) {
// Add a new node to the first of list and save newHead as the beginning of the list
} else {
// Add the other nodes and save the last node address as newTail
}
head = head -> next;
}
resultPath[0] = newHead;
resultPath[1] = newTail;
return resultPath;
}
I defined an array of struct path and I return it. In the main function an array has been defined:
struct path *newPath;
newPath = copyPath(path_head, nextNode);
When I run it, I do not have anything in newPath and it is empty.
So, what is the best way to return these addresses and why I do not have it in newPath?
Also, when I use:
struct path *newPath[2];
newPath = copyPath(path_head, nextNode);
I have an error: error: assignment to expression with array type
How to can I pass these two values to the main function?
In addition to the comments, your funciton has two primary problems, 1) you leak memory by overwriting the address of the first allocated block of memory; and 2) you attempt to return returnPath which has automatic storage duration declared local to copyPath.
Memory Leak
You leak memory in your function by overwriting the pointer temp before it has been assigned, e.g.
struct path *temp = malloc(sizeof (struct path));
...
while (head != NULL) {
temp = malloc(sizeof (struct path));
By allocating a second time for temp before you have assigned the original pointer to another variable, you lose the original pointer in temp pointing to the first block of memory allocated. That memory can never be freed by your program from that point forward.
Returning Array with Automatic Storage Declared Local to Function
When I run it, I do not have anything in newPath and it is empty.
struct path *resultPath[2]; declares an array-of-pointers to struct path (two of them). Automatic storage for resultPath is declared local to copyPath within it function stack frame. When copyPath returns, all local variables with automatic storage duration are destroyed (function stack frame memory is released for reuse). This is expressly explained by C11 Standard - ยง6.2.4 Storage durations of objects
1) An object has a storage duration that determines its lifetime. There are four storage durations: static, thread, automatic, and allocated. Allocated storage is described in 7.22.3.
2) The lifetime of an object is the portion of program execution during which storage is guaranteed to be reserved for it. An object exists, has a constant address,33) and retains its last-stored value throughout its lifetime.34) If an object is referred to outside of its lifetime, the behavior is undefined. The value of a pointer becomes indeterminate when the object it points to (or just past) reaches the end of its lifetime.
6) For such an object that does not have a variable length array type, its lifetime extends from entry into the block with which it is associated until execution of that block ends in any way.
(emphasis ours)
So, what is the best way to return these addresses and why I do not have it in newPath?
In order to return resultPath, you need to allocate storage for it dynamically and return a pointer to it. Objects with allocated storage duration extend for the lifetime of the program or until they are freed.
The easiest way to declare and allocate for an object capable of storing two pointers to struct path is to declare an pointer-to-pointer to struct path, (effectively a dynamic array of pointers), and allocate storage for two pointers, e.g.
/* allocate & validate 2 pointers to struct path */
struct path **resultPath = malloc (sizeof *resultPath * 2);
if (!resultPath) { /* always validate all memory allocations */
perror ("malloc resultPath failed.");
return NULL;
}
...
resultPath[0] = newHead; /* newHead & newTail must point to allocated memory */
resultPath[1] = newTail;
return resultPath;
You can now safely return resultPath and storage for resultPath will survive the return eliminating your access of memory that is no longer available. You then access newHead and newTail through newPath[0] and newPath[1], respectively, in the caller.
You will also need to change the return type for copyPath to stuct path **, e.g.
struct path **copyPath(struct path *head)
and update the type in the caller.
Whenever you allocate a dynamic memory(using malloc, calloc, realloc), appropriately typecast the return address to the proper data type. Because these functions return void pointer. In the below line you are assigning the void pointer type address to the struct path type pointer.
temp = malloc(sizeof (struct path));
It can be written as:
temp = (struct path *)malloc(sizeof (struct path));
Second thing, you are returning a array of pointer of type "struct path", but you are collecting this return array of pointer value in a regular pointer of type "struct path" in main which is against the C standard. Correct this mistakes and try to run the code.
In Main use array of pointer to collect the return value:
struct path *newPath[2];
newPath = copyPath(path_head, nextNode)

Dynamically allocated pointer to a structure not returning from C function

I am trying to write a concordance program using a linked list in C. To do this, I am returning a pointer to a dynamically allocated structure from a function. However, the address of the pointer in the function doesn't match the address of the returned value. The relevant code is below.
int main(int argc, char *argv[]){
...
struct wordlist *a = NULL;
...
a = firstNode(word);
printf("%p\n",(void*)&a);
...
}
struct wordlist * firstNode(char *fileWord){
struct wordlist *newNode = malloc(sizeof *newNode);
newNode->word = fileWord;
newNode->next = NULL;
newNode->previous = NULL;
printf("%s\n",newNode->word);
printf("%p\n",(void*)&newNode);
return newNode;
}
When I print the address of newNode, it is always different than when I print the value of a in main.
Are you printing what you return?
No.
You are printing the wrong thing.
printf("%p\n",newNode);
Now you will see the value you return and value you print will match. Previously you were returning on thing and printing another.
Explanation
Earlier you were allocating memory dynamically and the returned value of malloc is stored in local variable newNode. Now you are printing newNode's address. Not what newNode contains which is returned from the function.
Also check the return value of malloc. De-allocate dynamically allocated memory.

insertion in linked list -

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.

Returning local pointer

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.

Resources