Need some help understanding pointers and memory in C - c

I'm writing a bit of code for a class, but since I have no experience in C I'm a bit unsure of what the code I've written actually does. Particularly what the memory looks like. Here's the relevant bits:
typedef struct listnode *Node;
typedef struct listnode {
void *data;
Node next;
Node previous;
} Listnode;
typedef struct listhead *LIST;
typedef struct listhead {
int size;
Node first;
Node last;
Node current;
} Listhead;
#define HALLOCSIZE 50
static LIST hallocbuf[HALLOCSIZE];
static LIST *hallocp = hallocbuf;
LIST *CreateList()
{
if(hallocbuf + HALLOCSIZE - hallocp >= 1)
{
LIST temp;
temp->size = 0;
temp->first = NULL;
temp->last = NULL;
temp->current = NULL;
*hallocp = temp;
return hallocp;
}else
return NULL;
}
So my question is, in the CreateList function, how is the program allocating memory for temp? And does the code *hallocp = temp copy the temp LIST into the hallocbuf array? I am trying to have all my LIST structs sit in the allocated memory for hallocbuf. Is this what I'm doing? I'm a bit uncertain of how the typedef, structs and pointers play together.
Thanks!

So my question is, in the CreateList function, how is the program allocating memory for temp?
It isn't, which is a problem. It should do something like this:
temp = malloc(sizeof(Listhead));
And does the code *hallocp = temp copy the temp LIST into the hallocbuf array?
It copies the pointer that was saved in temp into the first element of hallocbuf (assuming that hallocp hasn't been changed anywhere and still has the value that it has been initialized to, pointing to hallocbuf[0]).
Generally it's not usually a good idea to hide the fact that LIST and Node are pointers behind typedefs. It's much clearer where memory needs to be allocated of freed if it's obvious which variables are pointer, and having an explicit * in the variable declaration makes that clear.

temp is allocated in the space used for objects with "automatic storage duration" - this is usually on a runtime stack, but you don't really need to know the details. The space is deallocated when the block in which it was allocated is exited (in your case, when you hit the return).
The line *hallocp = temp; does indeed copy the value of temp into the memory that hallocp is pointing at, which is hallocbuf[0].
The problem is that temp is just a pointer itself - and it's not pointing at anything. This is called a "dangling pointer". This means that when you try to access what it's pointing at, you have an error. That happens in these lines:
temp->size = 0;
temp->first = NULL;
temp->last = NULL;
temp->current = NULL;
You can't have your structs sit in the memory allocated for hallocbuf, because it doesn't have room for structs - it's just an array of pointers, not an array of structs.

If LIST were
typedef struct listhead LIST;
and you accessed temp
temp.size = 0;
...
then
*hallocp++ = temp;
would use hallocp as a pointer into the hallocbuf buffer and place the newly initialized element there. Not the best way to do it, though. You could replace temp with
hallocp->size = 0;
...
++hallocp;

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.

C destroying what I have behind when adding to list

I'm writing a program that needs to execute a list of commands so i'm using this struct to save them
typedef struct clist {
struct clist *prox;
char comando[];
} *CommandList;
And this function to add to make the last node point to a new node
void addToList (CommandList list, CommandList x){
CommandList temp = list;
while (temp->prox != NULL)
temp = temp->prox;
temp->prox = x;
}
However when I call the adding function I seem to lose everything that I had behind and I seriously can't understand why this is happening, this is the piece of code where that
aux = (CommandList) malloc(sizeof(struct clist));
strcpy(aux->comando,cropString(string));
aux->prox = NULL;
addToList(list,aux);
free(aux);
print_list(list);
When I call the print_list function the only node that is printed is the one I just added, and later on in the code I get a segfault, can someone help please?
This happens because you are misusing flexible struct member: when you define an array member like this
char comando[];
it means that you will allocate space for it manually; C compiler cannot do it for you.
You fix it by allocating additional space in malloc for the string that you save in comando, like this:
const char* cropped = cropString(string);
aux = malloc(sizeof(struct clist)+strlen(cropped)+1);
strcpy(aux->comando, cropped);
You are allocating "aux", adding it to your list, and then freeing aux. So you now have a list that points to a deallocated chunk of memory. When you allocate memory, you have to not deallocate it until you are done using it.

Accessing struct pointer within another struct in 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;

Delete struct from stack memory

I have a linked list struct, i want to pass one node (another struct) pointer to a function (the node is part of the linked list, but i'm passing the node seperately to a deleter function
I want it to copy the next node data into itself (overriding its data), and to delete the next node, thus deleting itself (this part is working)..
I made it check whether the passed node is the last node in the list, and if so, to delete itself.
I don't know how to delete structs from the stack (i know i can malloc() and free() it using the heap memory).
#include <stdlib.h>
#include <stdio.h>
typedef struct {
int data;
struct node * next;
}node;
typedef struct {
struct node * head;
}linked_list;
void print_list(linked_list * list) {
node *current = list->head;
while (current) {
printf("Current node has %d\n",current->data);
current = current->next;
}
}
void delete_node(node * n) {
node * next = n->next;
if (next) {
n->data = next->data;
n->next = next->next;
}
else {
*n = NULL; /*This of course won't compile because assigning void* (null) to node variable
but if i make n point to NULL, nothing will happen because i'm inside a function
and the pointer is duplicated (the passed pointer will still work) */
}
}
void main(){
node first;
node second;
first.data = 1;
first.next = &second;
second.data = 2;
second.next = NULL;
linked_list l;
l.head = &first;
print_list(&l);
delete_node(&second);
print_list(&l);
}
As others have said, you can't.
If you want to be able to store both allocated (by malloc) and non-allocated (static or automatic) storage objects in your list and have a "delete" function that removes objects from the list and frees them, you need to store as part of each list member a flag indicating whether it's in allocated storage or not, and only free the ones which are.
Also note that you'll be in big trouble if the lifetime of the structure with automatic storage ends before you remove it from the list! If dealing with this is confusing at all for you, then you would probably be better-off just using allocated storage (malloc) for all list members.
You can't :)
On most computer architectures, local variables are either allocated directly on a CPU register or on the stack. For local variables allocated on the stack, the top of the stack (the same stack that is used to hold the return addresses of function calls) is manipulated to make space for them when a function enters, and it is restored to "release" the memory when the function exits. All this stack management is handled automatically by the compiler.
You can use the 'free' operator for freeing / deleting a malloc assigned object in the memory. For that, in your code you can write :
free(n);

Linked List in C Trouble with Strings

I'm trying to create a linked list with a head and a tail node in C. Each node needs to hold an int and a string. My issue is that when I create a new node, assign it the correct values, and add it to the end of the list. All previous nodes obtain the string that I assigned the newest node. The int values stay correct, but its like their char pointer gets reassigned. I'm guessing I'm doing somethign wrong with pointers and have looked at tons of examples online and can't see where I'm going wrong. I included my node structure and add function. Thanks for all the help!!
// Linked list (queue) for requests
typedef struct request {
struct request *next;
int request_id;
char *command;
} request_t;
// Global variables
request_t *head = NULL;
request_t *tail = NULL;
void add(int r_id, char *c) {
request_t *node = NULL;
node = (request_t *)malloc(sizeof(request_t));
node->request_id = r_id;
node->command = c;
node->next = NULL;
if(head == NULL) {
head = node;
tail = node;
} else {
tail->next = node;
tail = node;
}
}
You need to create a duplicate of the string.
i.e. in the add function you require the line
node->command = strdup(c);
In addition you will have to free this string to prevent memory leaks when you free the node.
node->command = c;
Should be something like:
// Allocate enough memory for a copy of the string pointed to by c
node->command = malloc(strlen(c) + 1);
// Copy the string pointed to by c, to the newly allocated memory
strcpy(node->command, c);
Or since you have to call strlen on c you can use the more efficient:
size_t num_bytes = strlen(c) + 1;
// Allocate enough memory for a copy of the string pointed to by c
node->command = malloc(num_bytes);
// Copy num_bytes bytes from the memory pointed to by c, to the newly allocated memory
memcpy(node->command, c, num_bytes);
So that you make a copy of the string. Your delete function should be modified to free the allocated memory as well:
free(node->command);
So what was wrong with your code?
Without copying the string you just copy the pointer, so if you call add(10, somestr); Then somestr is a char * (a pointer). If you modify the string at that memory location it will also be modified in your linked list since there is really only one string with two pointers to it (command and somestr).
I never knew one could assign a string to another using a '=' operator :P This may work fine with structure variables but not with strings. Try using strcpy(node->command, c) after dynamically allocating size to node->command. This should work! :)

Resources