C: passing address of pointer vs passing address of array - c

This was an remote coding test I got a few days ago, and I've already submitted the answers so I'm not cheating here.
The question is fairly simple: implement a function to delete an item from a linked list. But with one catch: the linked list is in the form of an array.
typedef struct _Node{
int val;
struct _Node* next;
} Node;
Test input provided by the interviewer, which we can modify slightly
void printLL(Node* root){
Node* current = root;
while (current){
printf("%d\n", current->val);
current = current->next;
}
}
int main(){
Node list[6];
list[0].value =1; list[0].next = list+1;
list[1].value =2; list[1].next = list+2;
list[2].value =3; list[2].next = list+3;
list[3].value =4; list[3].next = list+4;
list[4].value =5; list[4].next = list+5;
list[5].value =6; list[5].next = 0;
delete(&list, 3) // this gives segmentation error with my implementation;
printLL(list);
return 0;
}
My answer, which is standard for linked list deletion:
void delete(Node** root, int val){
Node* current = *root;
Node* prev = NULL;
if ((*root)->val == val){
*root = (*root)->next;
//free(current); // test case not dynamically allocated
return;
}
while (current && (current->val != val)){
prev = current;
current = current->next;
}
if (!current){
printf("value not found\n");
return ;
}
else{
prev->next = current->next;
//free(current);
}
}
However, if I use pointers instead, then the function works
Node* p = list;
delete(&p, 3);
I think I understand the difference between &list and &p as function argument:
**Variable name | Variable value | Variable address**
list | address of first elem | address of first elem
p | address of first elem | some random address of the pointer
But since in the delete function we are operating with *list and *p respectively, their value should also be the same.
My guess now is because of in
*root = (*root)->next;
if *root is an array name, then it's illegal as we cannot reassign it. But if *root is a pointer, then we are freely to reassign them. Am I correct on this?
Thank you for reading through this long and messy post.

Related

When adding first node to linked list in hashmap, why must the new node be assigned directly to the indexed pointer?

Here is my implementation of a hashmap in c and its initialization and insert code.
In the hashmap_t structure, I use an array of pointers (table) to nodes which contain the key/value pairs. In hashmap_init, I allocate the desired amount of nodes and loop through the array setting each pointer to NULL.
What I'm confused about is in the hashmap_put function. I find the index of which list the key should be inserted in and that first pointer is referenced by hm->table[i]. For clarity, I want to make sure it's obvious that hm->table[i] is the start of the list so I assign it to hashnode_t *head.
So when inserting the first node (head == NULL), I originally used head = new_node, but none of my inserts worked. It only works when I use hm->table[i] = new_node.
I don't understand why that's the case. head points to the same thing so why does setting head equal to the new_node not work? I'm also confused later in the function when last->next = new_node does work. Last is a pointer just like head but it works there.
Thanks for any clarification.
typedef struct hashnode {
char key[128];
char val[128];
struct hashnode *next;
} hashnode_t;
typedef struct {
int item_count;
int table_size;
hashnode_t **table;
} hashmap_t;
void hashmap_init(hashmap_t *hm, int table_size) {
hm->table_size = table_size;
hm->item_count = 0;
hm->table = malloc(table_size * sizeof(hashnode_t));
for (int i = 0; i < table_size; i++) { // loop through array of pointers to nodes
hm->table[i] = NULL;
}
}
int hashmap_put(hashmap_t *hm, char key[], char val[]) {
hashnode_t *new_node = malloc(sizeof(hashnode_t)); // allocate new node
strcpy(new_node->key, key);
strcpy(new_node->val, val);
new_node->next = NULL;
int i = hashcode(key) % hm->table_size; // index of list hashed to
hashnode_t *head = hm->table[i];
hashnode_t *cur = head;
hashnode_t *last;
if (!head) { // list is empty
new_node->next = head;
hm->table[i] = new_node;
//why does head = new_node not work?
hm->item_count += 1;
return 1;
}
while (cur) { // loop through nodes
if (strcmp(cur->key, key) == 0) {
strcpy(cur->val, val);
free(new_node);
return 0;
}
last = cur; // save pointer to node that points to NULL
cur = cur->next;
}
last->next = new_node;
//why does it work here?
hm->item_count += 1;
return 1;
}
'head' is pointing to a hashnode_t and so is 'hm->table[i]'. So, they are both pointing to the same object. Changing 'head' just makes 'head' point elsewhere. You have not actually assigned a pointer in the hashmap_t to the 'new_node'.
The reason that 'last' works is that you are changing a member variable to a new value. And, since 'last' is pointing to an object already in the hashmap_t, the assignment updates the object pointed to in the hastmap_t. So, an update to 'last->next = new_node' is the same as 'hm->table[x]->next = new_node' ('x' is some arbitrary index).

strcpy() to copy string from struct member to char array is failing. Why?

I have a structure node which is used to create a binary search tree. Inside each node I am storing a integer KEY and a corresponding string value. I am performing some searches within the tree and wish to return arrays containing key value pairs of only specific nodes.
TO do so I am passing arrays by reference and saving the integer KEY to that array. This works fine, however when I try to the the same with the string I am getting poor results.
In the below code I am trying to copy the string inside root[count].value; to p_value_arr[*p_unique_count] which is a char array.
Struct definition:
typedef struct node {
int KEY;
char *value;
int node_count;
struct node *left, *right;
int unique_count;
} node;
Function to traverse graph and copy unique key value pairs. KEY is being copied correctly to an array while value is not.
void unique_key(node *root, int *p_unique_count, int p_unique_arr[], char *p_value_arr[]) {
int count = 0;
//unique *temp = (unique *)malloc(n * sizeof(unique));
if (root != NULL)
{
unique_key(root->left, p_unique_count, p_unique_arr, p_value_arr);
if (root->node_count == 1) {
root[count].unique_count = *p_unique_count;
p_unique_arr[*p_unique_count] = root[count].KEY;
printf("%s\n", root[count].value);
//"warning: assignment makes integer from pointer without a cast"
strcpy(p_value_arr[*p_unique_count],root[count].value);
printf("%d(%d) -> %s %d\n", root->KEY, root->node_count, root->value, root->unique_count);
(*p_unique_count)++;
count++;
}
unique_key(root->right, p_unique_count, p_unique_arr, p_value_arr);
}
}
A utility function to insert a new node with given key in BST
node* insert_node(node* node, int key, char *value)
{
/* If the tree is empty, return a new node */
if (node == NULL)
return newNode(key,value);
// If key already exists in BST, icnrement count and return
if (key == node->KEY)
{
(node->node_count)++;
// return node;
}
/* Otherwise, recur down the tree */
if (key < node->KEY)
node->left = insert_node(node->left, key, value);
else
node->right = insert_node(node->right, key, value);
/* return the (unchanged) node pointer */
return node;
}
node *newNode(int KEY, char *value)
{
struct node *temp = (struct node *)malloc(sizeof(struct node));
temp->KEY = KEY;
strcpy(temp->value, value);
temp->left = temp->right = NULL;
temp->node_count = 1;
return temp;
}
Main driver code
int main() {
int unique_count = 0;
int in_count = 0;
int unique_arr[10]; /
char *value_arr[10]; // an array of pointers
/* Let us create following BST. Passing values along with key */
node *root = NULL;
//this is for storing commands
root = insert_node(root, 2, "Hello");
root = insert_node(root, 3, "Thanks");
printf("\nkeys of the given tree \n");
unique_key(root, &unique_count, unique_arr, *value_arr);
for(int i = 0; i < 10; i++) {
printf("%d %s\n", unique_arr[i], value_arr[i]); //Mismatching the argument type "char" and conversion specifier "s" and nothing prints here
}
}
Output:
Hello
keys of the given tree
Segmentation fault
Any suggestions on how I can effectively copy a string inside a struct member to an array of chars?
EDIT:
Full code: https://pastebin.com/CB4Gp0gY
Since char *value_arr[10]; is an array of pointers I followed chapter 5.6 of K&R The C programming language to pass the array of pointers to the function. I get no warnings now, but the seg fault persists.
I also have more warnings set on my NetBeans 8.2.
Output from debugger:
/cygdrive/C/Users/****/AppData/Roaming/NetBeans/8.2/bin/nativeexecution/dorun.sh: line 71: 16516 Segmentation fault (core dumped) sh "${SHFILE}"
Gonna follow up for Lundin here
node *newNode(int KEY, char *value)
{
// Allocate a new overall structure
struct node *temp = (struct node *)malloc(sizeof(struct node));
//Copy the integer key
temp->KEY = KEY;
// uh oh - copy the given string into a random location in memory and segfault.
// Hint - you need to allocate enough memory to hold the incoming string.
// Advanced hint - If you don't want to make a copy of the string, you can
// just store its pointer, but it would want to be marked constant at the least...
strcpy(temp->value, value);
// Set tree stuff and count, but we are already dead...
temp->left = temp->right = NULL;
temp->node_count = 1;
return temp;
}
Also,
printf("%d %s\n", unique_arr[i], value_arr[i]); //Mismatching the argument type "char" and conversion specifier "s" and nothing prints here
Will fail because value_arr[i] is not a string, it is a char *. For this to work, it would have to point at a valid C string, or would need to point to memory that has a properly '\0' terminated string.
Take a look at his given link as you need a deeper understanding of how C strings work.
char *value_arr[10]; // the problem is here
That initializes an array of pointers but does not assign memory to those pointers before using them for stuff like strcpy(). As per K&R chapter 5.6 one should allocate memory using alloc() for the pointer array inside the function.
If you want a pointer to point to some memory for storing a string, then you have to create such an area of memory and set the pointer to point to it.
char *p;
alloc(strlen(root[count].value) +1);
strcpy(p, root[count].value);
p_value_arr[*p_unique_count] = p;
struct node_t*curr = head;
struct node_t*node = (struct node_t*)malloc(sizeof(struct node_t));
strcpy(node -> str,str);
node -> prev = NULL;
node -> next = NULL;
if(curr == NULL)
{
head = node;
tail = node;
return
}
int value = strcmp(curr -> str,str);
if(value>0)
{
head = node;
node -> next = curr;
curr -> prev = node;
return;
}
struct node_t* prev = curr;
curr = prev -> next;
while(curr != NULL)
{
value=strcmp(prev -> str,str);
if(value < 0)
{
int value1 = strcmp(curr -> str,str)
if(value1>0)
{
node -> prev = prev;
node -> next = curr;
node -> next = node;
node -> prev = node;
}
else if(value1 == 0)
{
if(curr -> next == NULL)
tail=prev;
prev -> next = curr -> next;
curr -> prev = NULL
return;
}
}
prev = curr;
curr = prev -> next;
}
prev -> next = node;
node -> prev = prev;
tail = node;

Deleting a linked list node in a C function doesn't transfer to the calling function

I have this C function which is supposed to find an element in the linked list which has a specific "pos" value, delete it, and return the deleted value to the calling function. It does delete the item, but the change isn't saved in the calling function, the list just doesn't get updated with the new changes.
My list is structured like this:
struct list{
int value;
int pos;
struct list * next_ptr;
};
And my C function is this:
bool findDeleteElement(struct list **ptr, int position, int *value){
struct list** temp = ptr;
if(*ptr!=NULL){
while((*ptr)->pos!=position) ptr=&(*ptr)->next_ptr; //Gets to desired node
temp=ptr;
value=&(*ptr)->value; //saves the value
temp=&(*temp)->next_ptr; //Goes to next node
ptr=temp; //Makes ptr point to next node
return 1;
}
else return 0;
}
I just can't see what I'm missing.
I'm a beginner so I probably made a simple mistake.
Change to:
*value = (*ptr)->value; //saves the value
You only set value, the local copy of your external variable's address. This does not change your external variable in the calling function.
Some question:
What happens when position has the wrong value, such that no node is found?
What's the purpose of temp = ptr;, because temp is overwritten by temp = &(*temp)->next_ptr; without having been used.
Disclaimer: I've not further checked this function.
I kindly advise you to take on other code formatting rules that add more air and make things more readable. Here's an example:
bool findDeleteElement(struct list **ptr, int position, int *value)
{
struct list** temp = ptr;
if (*ptr != NULL)
{
// Gets to desired node
while((*ptr)->pos != position)
{
ptr = &(*ptr)->next_ptr;
}
temp = ptr;
*value = (*ptr)->value; // Saves the value
temp = &(*temp)->next_ptr; // Goes to next node
ptr = temp; // Makes ptr point to next node
return 1;
}
else
{
return 0;
}
}
You are confused about pointers and dereferencing and what & and * actually do. This is a normal state of affairs for a beginner.
To start with, ptr and value when used without * preceding them are function arguments and like automatic (local) variables they disappear when the function scope exits. So this statement:
value=&(*ptr)->value;
Merely changes the value of value i.e. what it points to and has no visible effect to the caller. What you need to change is the thing that value points to. i.e. the statement should look like this:
*value = (*ptr)->value;
The difference is that instead of setting value to the address of (*ptr)->value it sets what valuepoints to to (*ptr)->value.
You have a similar problem with ptr. But your problems are more subtle there because you are also trying to use it as a loop variable. It's better to separate the two uses. I'd write the function something like this:
bool findDeleteElement(struct list **head, int position, int *value)
{
struct list* temp = *head;
struct list* prev = NULL;
while(temp != NULL && temp->pos != position)
{
prev = temp;
temp = temp->next;
}
if (temp == NULL) // position not found
{
return false;
}
else
{
*value = temp->value;
// Now need to delete the node.
if (prev != NULL)
{
// If prev has been set, we are not at the head
prev->next = temp->next; // Unlink the node from the list
}
else // We found the node at the head of the list
{
*head = temp->next;
}
free(temp); // Assumes the node was malloced.
return true;
}
}
The above is not tested or even compiled. I leave that as an exercise for you.
int delete(struct llist **pp, int pos, int *result)
{
struct llist *tmp;
while ( (tmp = *pp)) {
if (tmp->pos != pos) { pp = &tmp->next; continue; }
*result = val;
*pp = tmp->next;
free(tmp);
return 1;
}
return 0;
}

Pointers to pointers - linked list mess

I'm writing a simple C program to manage a linked list defined as follow:
typedef struct node {
int value;
struct node *next;
} *List;
I reviewed the code and it seems okay but when printing results something is not working well.
My main, with problems on comments:
int main(void) {
List n = list_create(1);
insert(n, 2);
insert(n, 3);
insert(n, 5);
insert(n, 4);
//something here does not work properly. It produces the following output:
//Value: 1
//Value: 2
//Value: 3
//Value: 4
//where is value 5?
print_list(n);
delete(n, 3);
print_list(n);
return 0;
}
I don't know where am I destroying list structure. These are my functions, to debug, if you are too kind.
List list_create(int value) {
List new = malloc(sizeof(struct node));
new->value = value;
new->next = NULL;
return new;
}
List new_node(int value, List next_node) {
List new = malloc(sizeof(struct node));
new->value = value;
new->next = next_node;
return new;
}
void print_list(List l) {
List *aux;
for (aux = &l; (*aux) != NULL; aux = &((*aux)->next))
printf("Valor: %d\n", (*aux)->value);
}
void insert(List l, int value) {
List *p;
for (p = &l; (*p) != NULL; p = &((*p)->next))
if ((*p)->value > value) {
List tmp = *p;
List new = new_node(value, tmp);
*p = new;
break;
}
*p = new_node(value, NULL);
}
void delete(List l, int value) {
List *p;
for (p = &l; (*p) != NULL; p = &((*p)->next))
if ((*p)->value == value) {
List del = (*p);
(*p) = ((*p)->next);
free(del);
break;
}
}
This code has (at least) two bugs:
The line
if ((*p)->value > value){
means that if you start the list with 1 as the first value and then try to insert 2,3,4..., the body of the 'if' statement never runs, so nothing ever gets inserted.
If you insert a value below the starting value, you have to modify the list pointer itself. However, as #EOF alluded, you are trying to modify a value passed to a function by taking its address. This won't work. &l does not give you the address of the List you passed, it gives you the address of the local copy on insert()'s stack. You are better off modifying the values of first element of the list 'in place'. If you really want to make the List parameter mutable, you'll need to pass it as a List *, and call the function with the address of the list (e.g. insert(&n,2); ) Your delete() function suffers from the same problem - try deleting the first element of the list.
Try this for your insert function:
void insert(List l, int value)
{
List p;
// Find end of list or highest item less than value
for(p = l; p->next != NULL && p->next->value < value; p = p->next);
if (p->value >= value) {
// Over-write p with new value, and insert p as a new one after.
// This saves having to modify l itself.
int tmpval = p->value;
p->value = value;
p->next = new_node(tmpval, p->next);
} else {
// Insert new item after p
p->next = new_node(value, p->next);
}
}
A comment: it is possible the way you are using pointers is not helping the debugging process.
For example, your print_list() could be re-written like this:
void print_list(List l){
List aux;
for(aux = l; aux != NULL; aux = aux->next)
printf("Valor: %d\n", aux->value);
}
and still behave the same. It is generally good practice not to 'hide' the pointer-like nature of a pointer by including a '*' in the typedef.
For example, if you define your list like this:
typedef struct node{
int value;
struct node *next;
} List
And pass it to functions like this:
my_func(List *l, ...)
then it'll make some of these issues more apparent. Hope this helps.
There are many problems in your code:
Hiding pointers behind typedefs is a bad idea, it leads to confusion for both the programmer and the reader.
You must decide whether the initial node is a dummy node or if the empty list is simply a NULL pointer. The latter is much simpler to handle but you must pass the address of the head node to insert and delete so they can change the head node.
printlist does not need an indirect pointer, especially starting from the address of the pointer passed as an argument. Simplify by using the Node pointer directly.
in insert you correctly insert the new node before the next higher node but you should then return from the function. Instead, you break out of the switch and the code for appending is executed, replacing the inserted node with a new node with the same value and a NULL next pointer. This is the reason 5 gets removed and lost when you insert 4. Furthermore, you should pass the address of the head node so a node can be inserted before the first.
delete starts from the address of the argument. It cannot delete the head node because the pointer in the caller space does not get updated. You should pass the address of the head node.
You should avoid using C++ keywords such as new and delete in C code: while not illegal, it confuses readers used to C++, confuses the syntax highlighter and prevents compilation by C++ compilers.
Here is a simplified and corrected version:
#include <stdio.h>
#include <stdlib.h>
typedef struct Node {
int value;
struct Node *next;
} Node;
Node *new_node(int value, Node *next_node) {
Node *node = malloc(sizeof(*node));
if (node != NULL) {
node->value = value;
node->next = next_node;
}
return node;
}
void print_list(Node *list) {
for (; list != NULL; list = list->next)
printf("Valor: %d\n", list->value);
}
void insert_node(Node **p, int value) {
while ((*p) != NULL && (*p)->value < value)
p = &(*p)->next;
*p = new_node(value, *p);
}
void delete_node(Node **p, int value) {
while (*p != NULL) {
if ((*p)->value == value) {
Node *found = *p;
*p = (*p)->next;
free(found);
// return unless delete() is supposed to remove all occurrences
return;
} else {
p = &(*p)->next;
}
}
}
int main(void) {
Node *n = NULL;
insert_node(&n, 2);
insert_node(&n, 3);
insert_node(&n, 5);
insert_node(&n, 4);
insert_node(&n, 1);
print_list(n);
delete_node(&n, 3);
print_list(n);
delete_node(&n, 1);
print_list(n);
return 0;
}

doublepointed list C

I wanted to make a list using double pointer and using void as return.
#include<stdio.h>
#include<stdlib.h>
typedef struct list{
int value;
struct list *next;
}*list;
void addnode(struct list **List, int number) {
if(*List == NULL) {
*List = (struct list*)malloc(sizeof(struct list*));
(*List)->value = number;
(*List)->next = NULL;
} else {
while((*List)->next != NULL) {
(*List) = (*List)->next;
}
*List = (struct list*)malloc(sizeof(struct list*));
(*List)->value = number;
(*List)->next = NULL;
}
}
int main() {
list List1 = NULL;
addnode(&List1, 20);
printf("%d \n", List1->value);
addnode(&List1, 30);
printf("%d \n", List1->value);
printf("%d \n", List1->next->value);
return 0;
}
The first if in addnode is always executed but i want to append the list if its not empty but it seems like it never work. Ill also get segmenation fault because in the last printf it tries to take the next element in the list but its never initialized like i want.
If everthing worked as i wanted i should have printed out
printf("%d\n", List1->value)
20
printf("%d\n", List1->value)
20
printf("%d\n", List1->next->value)
30
The size you are passing to malloc is wrong.
You are allocating a struct list, not a struct list *.
If you are trying to append a new list item, remember (*List)->next will already be NULL on the second call. The malloc following that uses the pointer before the NULL list item (*List) when it should be assigned to the next list item, the one that is NULL, to make it non-NULL ((*List)->next=malloc(struct list);).
Also, your malloc should be using sizeof(struct list), without the *. If you add the *, you're allocating a struct list **. A rule you can use is use one * fewer than the destination type as the sizeof operand. Since your destination is *List, which is of type struct list *, use sizeof(struct list). Alternatively, because your destination is *List, use sizeof **List (use one more * than the destination variable has). This avoids you needing to know the type. It won't matter if List or *List is NULL because the sizeof operation is executed first; pointer dereferencing never occurs since sizeof works on the type of the variable.
Modify your program like this
int addNode(struct list **List, int number)
{
struct list *new, *tmp; // new = create new node, tmp = navigate to last
new = malloc(sizeof(struct list));
if(!new) { //always validate "malloc"
perror("malloc");
exit(1);
}
new -> value = value; // assigning values to new node
new -> next = NULL;
if(!(*list)) { //Check if list is empty or not, plz initialize *list#main() with NULL as like your program. or write seperate function to initialize
*list = new;
return 0; //no need write else condition, bcoz its the first node. and u can directly return
}
tmp = *list;
while(tmp -> next) // To navigate to last node
tmp = tmp -> next;
tmp -> next = new; //creating link to new node
return 0;
}
It's better to write print function seperatly.
int print(struct list **list)
{
struct *current; //current is your current node position
current = *list;
while(current) { //loop till current node addr == NULL
printf("%d\t", current -> value);
current = current -> next;
}
printf("\n");
return 0;
}

Resources