I'm currently studying Computer Science and we started working with Pointers. I had the feeling that I started to understand pointers but I ran into a problem and can't figure out what went wrong.
We defined a Tree like this:
typedef struct node *tree;
struct node {int key; tree left, right;};
Now we should write a function that creates nodes with three parameters, the key of the node, the left node and the right node that should be "below" the node. I did it like this and it seemed to work:
tree createNode(int n, tree l, tree r){
tree node = (tree) malloc(sizeof(tree));
node->key = n;
node->left = l;
node->right = r;
return node;
}
Finally we should write a function that multiplies all leaves of the Tree and i thought the easiest way would be to start at the root and search for the leaves through a recursion and then multiply them. But when i call the function the program seem to crash in the middle of the function. My function looks like this:
int leafprod(tree t){
printf("%d\n", t->key);
if (t->left == NULL){
if (t->right == NULL){
printf("$1\n\n");
return t->key;
}
printf("$2\n\n");
return leafprod(t->right);
}
if (t->right == NULL){
printf("$3\n\n");
return leafprod(t->left);
}
printf("$4\n\n");
return leafprod(t->left) * leafprod(t->right);
}
and i call the function in the main function like this:
int main(){
tree a = createNode(1, NULL, NULL);
tree b = createNode(2, NULL, NULL);
tree c = createNode(3, a, NULL);
tree d = createNode(4, b, c);
int n = leafprod(d);
printf("end: %d", n);
free(a);
free(b);
free(c);
free(d);
return 0;
}
I used the print statements to follow the programm and try to locate the error, but in most cases it prints nothing. Then sometimes it prints:
4
$4
2
$2
And only two times the program went through the whole code. I believe maybe I am using the malloc function wrong but I cannot tell.
The problem is in this line:
tree node = (tree) malloc(sizeof(tree));
tree is typedef for a pointer to struct node.
Therefore sizeof(tree) is just the size of the pointer.
You should one of the following instead:
tree node = malloc(sizeof(*l));
Or:
tree node = malloc(sizeof(*r));
*l and *r are of type struct node (not a pointer) and this is the element you are trying to create.
Another option as #IanAbbott commented is:
tree node = malloc(sizeof(*node));
Note that node here is the name of the variable, not the type (which in C requires to be prefixes with struct, i.e. struct node).
The advantage of this approach is that the statement is not dependent on other variables.
Side notes:
You shouldn't cast the result of malloc. See here:Do I cast the result of malloc?.
It's not a good practice to hide pointer types with typedefs. You can consider to avoid it (you can use typedef struct node Node if you want to save the need to use the struct keyword everywhere).
Don't use the typedef for hiding pointer types.
With typedef struct node *tree; you have no idea what tree is in your code hence the confusion.
Typically here tree node = (tree) malloc(sizeof(tree)) you did it wrong, and the main reason was probably because you didn't know anymore what tree actually was.
...
struct node
{
int key;
struct node* left;
struct node *right;
};
struct node* createNode(int n, struct node*l, struct node*r) {
struct node* node = malloc(sizeof(*node)); // don't use the cast, it's useless
...
}
int leafprod(struct node* t) {
...
Related
All the implementations I have seen online use pointer to declare nodes and then will use malloc to create space for them like this:
struct Node
{
int data;
struct Node *next;
};
int main()
{
struct Node* head = NULL;
struct Node* second = NULL;
struct Node* third = NULL;
head = (struct Node*)malloc(sizeof(struct Node));
second = (struct Node*)malloc(sizeof(struct Node));
third = (struct Node*)malloc(sizeof(struct Node));
...
But I can also create the same without pointers and malloc like this:
struct node {
int id;
struct node* next;
};
struct node head = {0, NULL};
struct node one = {1, NULL};
struct node two = {2, NULL};
struct node tail = {3, NULL};
int main(){
head.next = &one;
one.next = &two;
two.next = &tail;
...
My question is, why the 1st method is mostly the one used, why do we need to declare each node as pointer and why do we need malloc?
(Just to point out I know why struct Node *next; is declared as pointer int the struct declaration).
You should do this with local variables, not global ones, but the general idea would be the same. You should also steer towards having arrays and not heaps of otherwise unrelated variables:
struct node {
int id;
struct node* next;
};
int main(){
struct node nodes[4];
for (int i = 0; i < 4; ++i) {
nodes[i].id = (3 - i);
if (i != 0) {
nodes[i].next = &nodes[i-1];
}
}
return 0;
}
Where something like that assembles them in reverse order for convenience, but they're all grouped together in terms of memory initially.
malloc is used because you often don't know how many you're going to have, or they get added and removed unpredictably. A general-purpose solution would allocate them as necessary. A specialized implementation might allocate them as a single block, but that's highly situational.
Here the lifespan of nodes is within that function alone, so as soon as that function ends the data goes away. In other words:
struct node* allocateNodes() {
struct node nodes[10];
return &nodes; // Returns a pointer to an out-of-scope variable, undefined behaviour
}
That won't work. You need a longer lived allocation which is precisely what malloc provides:
struct node* allocateNodes() {
struct node *nodes = calloc(10, sizeof(struct node));
return nodes; // Returns a pointer to an allocated structure, which is fine
}
The problem is if you malloc you are responsible for calling free to release the memory, so it becomes more work.
You'll see both styles used in code depending on the required lifespan of the variables in question.
If you know ahead of time exactly how many items will be in your list, then you're probably better off using an array rather than a list. The whole point of a linked list is to be able to grow to an unknown size at runtime, and that requires dynamic memory allocation (i.e., malloc).
I've been asked by my teachers to write a function in C that mirrors a binary tree, and by that I mean that inverts the tree.
I've been struggling with this question because the solution doesn't make any sense to me.
The data struct we use is the following:
typedef struct nodo {
int valor;
struct nodo *esq, *dir;
} *ABin;
and the solution is:
void mirror (ABin *a) {
ABin c = *a;
if (c == NULL);
else {
ABin e = c -> esq;
ABin d = c -> dir;
c -> esq = d;
c -> dir = e;
mirror (&(c -> esq));
mirror (&(c -> dir));
}
}
My biggest concern here is the use of pointers or not. I don't understand why, when we call the function recursively, we have to use & when esq and dir are already pointers to the struct nodo type?
The higher level answer to your question is that by recursively swapping every node's left and right subtrees, you reverse the tree's ordering invariant.
Typically, a binary search tree maintains the invariant that every left descendant orders earlier (or equal to) than the current node, while every right descendant orders later (or equal to) the current node according to the ordering function of the tree. By swapping the left and right subtrees at every node in the tree, you reverse the ordering invariant of the tree.
As to your question about the level of pointer indirection, there is no good reason for the mirror function to take an ABIN pointer. It should just take an ABIN as that is a pointer to a tree node by typedef. Even better, you (or your teachers) wouldn't make a typedef for a pointer to a struct in the first place without good reason.
You only need to swap the pointers (even if one of them is NULL)
struct node {
struct node *prev;
struct node *next;
int key;
char payload[123];
};
void mirror(struct node *ptr)
{
struct node *tmp;
if (!ptr) return;
tmp = node->prev;
node->prev = node->next;
node->next= tmp;
mirror(node->prev);
mirror(node->next);
}
You are making a typedef for a pointer.
typedef struct nodo* ABin;
This is asking for confusion! In fact, it seemed to have confused you because
void mirror(ABin*)
is the same as
void mirror(struct nodo**)
and there's no reason for that extra layer of indirection. You could change that to
void mirror(ABin)
but it's best to avoid making typedefs for pointers in the first place.
Cleaned up:
typedef struct Nodo {
int valor;
struct Nodo* esq;
struct Nodo* dir;
} Nodo;
void mirror(Nodo* nodo) {
if (nodo == NULL)
return;
Nodo* tmp = nodo->esq;
nodo->esq = nodo->dir;
nodo->dir = tmp;
mirror(nodo->esq);
mirror(nodo->dir);
}
As you can see esq and dir are pointers to a struct nodo.
Using the typedef struct nodo{} *ABin makes ABin type also a pointer to a struct nodo.
so
ABin is equivalent to struct nodo * (types).
So, for your function void mirror (ABin *a), a would be a pointer to a pointer.
parameter a is struct nodo** // pointer to a pointer
Your function must modify the pointer itself so you must pass its address; that's why you call the function with &.
I am using the following free function for my tree:
void freeTree(struct node *tree) {
if (tree == NULL) return;
freeTree(tree->left);
freeTree(tree->right);
free(tree);
}
And I am creating a tree/operating on it like this:
struct node *root = NULL;
root = createTree(testNodes);
inOrderPrint(root);
freeTree(root);
Definition of node struct:
struct node {
int val;
int color;
struct node *parent;
struct node *left;
struct node *right;
};
I have noticed after running this enough (many hours), memory starts to build up (very little amount) in my IDE (I am using CLion). I am posting to ask if my freeTree function is implemented correctly to rule that out and to see if it could be something else causing this slow memory build up.
Supposing that your tree is in fact constructed in the manner implied by the names of the members of struct node, and that nodes' left and right pointers are set to NULL for nodes that do not have a left or right child, respectively, your freeTree() function will free all memory associated with the (sub) tree whose root node its argument points to. If you have a leak, it is elsewhere.
In the below code, I am trying to insert a node after a particular node. In the function, I will be giving as input the address of the previous node after which I want to insert the new node. The problem is in the 10th line of function insertAfter() - it says I cannot access *prev_ref->next.
#include<stdio.h>
#include<stdlib.h>
struct node
{
int data;
struct node* next;
};
void push(struct node **head_ref, int data)
{
struct node* newNode = (struct node*)malloc(sizeof(struct node)) ;
newNode->next= *head_ref;
newNode->data= data;
*head_ref= newNode;
}
void insertAfter(struct node **prev_ref, int data)
{
if(*prev_ref==NULL)
{
printf("prev ref cant be null");
return;
}
struct node * newNode;
newNode = (struct node*)malloc(sizeof(struct node)) ;
newNode->next= *prev_ref->next;
newNode->data= data;
*prev_ref->next= newNode;
}
void printList(struct node *node)
{
while (node != NULL)
{
printf(" %d ", node->data);
node = node->next;
}
}
main()
{
struct node* head = NULL;
push(&head, 7);
push(&head, 1);
insertAfter(&head, 8);
printf("\n Created Linked list is: ");
printList(head);
getchar();
return 0;
}
Do you know (*p).s is equivalent to p->s ? I would suggest you to try something like (*prev_ref)->next or (**prev_ref).next
You seem to dereference prev_ref three-levels deep instead of two.
pointer->field is dereference of a pointer, equivalent to (*pointer).field
So, **prev_ref->next; is in fact (***prev_ref).next;
Either drop one asterisk or use . instead of ->.
EDIT:
You seem to have skipped the parentheses we included in our answers.
-> has higher precedence than *.
The effect is:
(*prev_ref)->next
first uses '*' and finds the memory pointed to by prev_ref (let's call it memory location A),
then uses '->' to find memory pointed to by A, let's call it B,
then location of the the next field of the structure, offset by a set distance from B, let's call it C
and finally accesses (reads/writes) the value stored at C.
Now for *prev_ref->next
first, uses -> and finds the memory pointed to by prev_ref (A), just the same
then the location of the the next field of the structure, offset by a set distance from A, which happens to be an entirely random location in memory (because A stored a pointer to the structure, not the structure itself); let's call that location D.
Then it tries to find the memory location at wherever D pointed to, which is entirely random.
Now, the system won't let you do that, because it sees A is not where a structure lies, but where a pointer to a structure lies, hence the error message
And the fundamental reason of your problems is that you use pointers-to-pointers for no good reason. Nothing of this would have happened if you always used plain pointers. void push(struct node *head_ref, int data) , void insertAfter(struct node *prev_ref, int data), prev_ref->next etc. Managing pointers to pointers is tricky, error-prone (as you've experienced) and in 99% cases completely unnecessary.
Okay so I have a Binary Search Tree built using only C structs and pointers because I'm insane and didn't want to use C++. Anyways, I've got some serious memory leaks since I'm assuming free(tree), tree being an instance of the struct below, doesn't free all the children of that tree.
Here is my node:
struct node{
struct node* parent;
struct node* left;
struct node* right;
int key; //the value of the node
};
and here is my bst:
struct bst{
struct node* root;
int elements; //number of nodes in the bst
};
So my question, is there any better way of doing this than recursively calling a delete function? for instance (writing this on the spot):
void delete_tree(struct node* n){
if(n == NULL) return;
struct node* left = n->left;
struct node* right = n->right;
free(n);
delete_tree(left);
delete_tree(right);
}
I see absolutely nothing wrong with a recursive delete. You could use an iterative approach, but it wouldn't have any discernible benefits and would be harder to write.
By the way, you can simplify the code a little and remove the two local variables as so:
void delete_tree(struct node* n){
if(n == NULL) return;
delete_tree(n->left);
delete_tree(n->right);
free(n);
}
You are making the recursive calls but never actually call free. You probably need to verify if a node is a leaf node (maybe asking if both children are null) and call free on that node.