c pointer to pointer how to iterate through it - c

struct hashLink
{
KeyType key; /*the key is what you use to look up a hashLink*/
ValueType value; /*the value stored with the hashLink, an int in our case*/
struct hashLink *next; /*notice how these are like linked list nodes*/
};
struct hashMap
{
hashLink ** table; /*array of pointers to hashLinks*/
int tableSize; /*number of buckets in the table*/
int count; /*number of hashLinks in the table*/
};
I'm trying to iterate through a hashMap with hashLinks. Is this the correct approach? The hashLinks are in an array and may have more hashLinks linked to them in a linked list. I just do not understand how to work pointers to pointers. tableSize is the amount of elements in the array. At each array position there may be more hashLinks linked to the first there.
for(int i = 0; i < ht->tableSize; i ++)
{
hashLink *current;
if (ht->table[i] != 0)
{
current = ht->table[i];
while(current->next !=0)
{
hashLink *next;
next = current->next;
free(current->key);
free(current);
current = next;
}
free(current->key);
free(current);
}
else
{
continue;
}
counter++;
}
}

Yes, this does work, but you end up with a hashtable that contains dangling pointers. Also, as Joachim noted, it works as long as you assume that the values contained in the structs are sane, i.e., tableSize contains the number of entries in table and the hashLinks have been correctly allocated.
Your iteration through the links is fine and correclty frees all the hashLinks in the table. However, consider the state of ht after the iteration. You do not change the values of ht->table[i] at all, so after you leave the loop, the pointers will still be stored in the table. If you want to reuse the table, you should set the pointers to 0 when you do not need them anymore, i.e., add ht->table[i] = 0 somewhere after current = ht->table[i];.
If this method is part of the "destructor" of the table (i.e., some method like hashmap_delete(...)), then you can simply free the hashmap after you finished your iteration, i.e., add free(ht); after the for-loop.

Simplified:
for(int i=0; i < ht->tableSize; i++)
{
hashLink *current;
while (ht->table[i] != NULL) {
current = ht->table[i];
ht->table[i] = current->next;
free(current->key);
free(current);
}
}
It can be further simplified to only one loop, but that is left as an exercise to the reader ...
Note: as a side effect, this will set all the pointers in ht->table[] to NULL; which is good, since after freeing the linked lists they have become stale anyway.

Related

listing doubly linked list items C

I'm new to C.I am trying to create a doubly linked list where the data field is a structure. But when I output the elements, only the first field of the structure is correctly displayed.
struct n
{
int a;
int b;
};
typedef struct _Node {
struct n *value;
struct _Node *next;
struct _Node *prev;
} Node;
typedef struct _DblLinkedList {
size_t size;
Node *head;
Node *tail;
} DblLinkedList;
DblLinkedList* createDblLinkedList() {
DblLinkedList *tmp = (DblLinkedList*) malloc(sizeof(DblLinkedList));
tmp->size = 0;
tmp->head = tmp->tail = NULL;
return tmp;
}
void pushBack(DblLinkedList *list, struct n *value) {
Node *tmp = (Node*) malloc(sizeof(Node));
if (tmp == NULL) {
exit(3);
}
tmp->value = value;
tmp->next = NULL;
tmp->prev = list->tail;
if (list->tail) {
list->tail->next = tmp;
}
list->tail = tmp;
if (list->head == NULL) {
list->head = tmp;
}
list->size++;
}
void printInt(struct n *value) {
printf("%d, %d", value->a, value->b);
}
void printDblLinkedList(DblLinkedList *list, void (*fun)(struct n*)) {
Node *tmp = list->head;
while (tmp) {
fun(tmp->value);
tmp = tmp->next;
printf("\n");
}
}
So, I have a few questions. Did I declare the node value field correctly? Am I inserting the node at the end of the list correctly? Am I doing the output of doubly linked list items correctly? And where is my mistake and how to fix it?
Did I declare the node value field correctly?
That depends on what your intention was. In terms of storing a pointer to a struct n: yes.
Am I inserting the node at the end of the list correctly?
Yes.
Am I doing the output of doubly linked list items correctly?
Yes.
And where is my mistake and how to fix it?
The code works from my point-of-view but what can be misleading is how pushBack operates. pushBack takes the struct n pointer as-is and stores it in Node. You did not post the pushBack caller code but the current implementation can caused problems if the caller assumes that the struct n gets copied.
To illustrate that, consider the following:
struct n value;
value.a = 1;
value.b = 2;
pushBack(list, &value);
value.a = 3;
value.b = 4;
pushBack(list, &value);
By reusing the value, two linked list nodes will effectively contain the same values. Also, the inserted struct n pointer must remain valid throughout the lifetime of the list. So, inserting stack-allocated values (that will be deallocated later by leaving their scope) or freeing dynamically-allocated values too early might lead to incorrect values. As long as the caller knows that, this is not necessarily a problem.
There are usually 3 ways to handle memory ownership:
Data structure owns values (just like it owns nodes) and is responsible for freeing them
Data structure copies values and is responsible for freeing them
Caller owns values and is responsible for freeing them
For a linked list, there's lots of merit in the strategy #3, because a linked list can be created from existing values without any copying or ownership transfer which would most certainly require changes to existing code. That's basically what your code is doing at the moment.

Pointer seg faulting although I malloc-ed right

I don't understand why my program seg faults at this line: if ((**table->table).link == NULL){ I seem to have malloc-ed memory for it, and I tried looking at it with gdb. *table->table was accessible and not NULL, but **table->table was not accessible.
Definition of hash_t:
struct table_s {
struct node_s **table;
size_t bins;
size_t size;
};
typedef struct table_s *hash_t;
void set(hash_t table, char *key, int value){
unsigned int hashnum = hash(key)%table->bins;
printf("%d \n", hashnum);
unsigned int i;
for (i = 0; i<hashnum; i++){
(table->table)++;
}
if (*(table->table) == NULL){
struct node_s n = {key, value, NULL};
struct node_s *np = &n;
*(table->table) = malloc(sizeof(struct node_s));
*(table->table) = np;
}else{
while ( *(table->table) != NULL){
if ((**table->table).link == NULL){
struct node_s n = {key, value, NULL};
struct node_s *np = &n;
(**table->table).link = malloc(sizeof(struct node_s));
(**table->table).link = np;
break;
}else if (strcmp((**table->table).key, key) == 0){
break;
}
*table->table = (**(table->table)).link;
}
if (table->size/table->bins > 1){
rehash(table);
}
}
}
I'm calling set from here:
for (int i = 0; i < trials; i++) {
int sample = rand() % max_num;
sprintf(key, "%d", sample);
set(table, key, sample);
}
Your hashtable works like this: You have bins bins and each bin is a linked list of key / value pairs. All items in a bin share the same hash code modulo the number of bins.
You have probably created the table of bins when you created or initialised the hash table, something like this:
table->table = malloc(table->bins * sizeof(*table->table);
for (size_t i = 0; i < table->bins; i++) table->table[i] = NULL;
Now why does the member table have two stars?
The "inner" star means that the table stores pointers to nodes, not the nodes themselves.
The "outer" start is a handle to allocated memory. If your hash table were of a fixed size, for example always with 256 bins, you could define it as:
struct node_s *table[256];
If you passed this array around, it would become (or "decay into") a pointer to its first element, a struct node_s **, just as the array you got from malloc.
You access the contents of the lĀ“bins via the linked lists and the head of linked list i is table->table[i].
You code has other problems:
What did you want to achieve with (table->table)++? This will make the handle to the allocated memory point not to the first element but tho the next one. After doing that hashnum times, *table->table will now be at the right node, but you will have lost the original handle, which you must retain, because you must pass it to free later when you clean up your hash table. Don't lose the handle to allocated memory! Use another local pointer instead.
You create a local node n and then make a link in your linked list with a pointer to that node. But the node n will be gone after you leave the function and the link will be "stale": It will point to invalid memory. You must also create memory for the node with malloc.
A simple implementation of your has table might be:
void set(hash_t table, char *key, int value)
{
unsigned int hashnum = hash(key) % table->bins;
// create (uninitialised) new node
struct node_s *nnew = malloc(sizeof(*nnew));
// initialise new node, point it to old head
nnew->key = strdup(key);
nnew->value = value;
nnew->link = table->table[hashnum];
// make the new node the new head
table->table[hashnum] = nnew;
}
This makes the new node the head of the linked list. This is not ideal, because if you overwrite items, the new ones will be found (which is good), but the old ones will still be in the table (which isn't good). But that, as they say, is left as an exercise to the reader.
(The strdup function isn't standard, but widely available. It also creates new memory, which you must free later, but it ensures, that the string "lives" (is still valid) after you have ceated the hash table.)
Please not how few stars there are in the code. If there is one star too few, it is in hash_t, where you have typecasted away the pointer nature.

Double free error with doubly linked list

So I'm trying to do a method to clear a doubly linked list for school where the doubly linked list and nodes are defined as:
struct word_entry
{
char *unique_word ;
int word_count ;
} ;
struct node
{
struct word_entry one_word ;
struct node *p_previous ;
struct node *p_next ;
} ;
struct linked_list
{
struct node *p_head ;
struct node *p_tail ;
struct node *p_current ;
} ;
I have a method to clear a linked list by doing
int clear_linked_list( struct linked_list *p_list ) //return how many nodes were cleared
{
if (p_list->p_head == NULL) {
return 0;
}
else {
int count = 0;
struct node *curr = p_list->p_head;
while (curr != NULL) {
struct node *next = curr->p_next;
free(curr->one_word.unique_word);
free(curr);
curr = next;
count++;
}
return count;
}
}
I do a free() on curr->one_word.unique_word because it's a malloc'd char array. I was taught to free when I use malloc, so that's there.
The issue I run into is I get a "bogus pointer (double free?)" and a core dump when I run the test file provided by my professor. I've worked on this for a few hours and can't seem to find out where (or how) I'm calling free twice.
When you loop through the list, you should constantly change the position of the head, so that even if you repeat clear_linked_list, you will not get an error.
int clear_linked_list(struct linked_list* p_list) // return how many nodes were cleared
{
if (p_list->p_head == NULL) {
return 0;
} else {
int count = 0;
while (p_list->p_head != NULL) {
struct node* curr = p_list->p_head;
p_list->p_head = p_list->p_head->p_next;
free(curr->one_word.unique_word);
free(curr);
count++;
}
return count;
}
}
When freeing memory it is a good practice to set NULL to pointers that were freed to avoid this kind of problems.
So you should do:
free(curr->one_word.unique_word);
curr->one_word.unique_word=NULL;
//if that one_word.unique_word was shared between multiples nodes that free could cause problems if you dont set it to NULL afterwards
free(curr);
curr=NULL; //or curr=next...
Also. Check that when you create the nodes that:
*p_next is NULL on the last node of the double linked list
*p_previous is NULL on the first node of the list
You don't null out p_head before you leave the clear function.
So, if you called it twice, you'd have problems (i.e. p_head would point to an already freed node). Likewise for p_tail.
Also, if you tried to add to the list again, you'd have similar problems.
Otherwise, your clear code is just fine.
So, can you prove that the list is constructed correctly (e.g. before you free, add a printf that prints out all the node's pointers before you free anything).

Bubble-sorting doubly linked list

I have a problem with my bubble-sorting function for the doubly linked list.
It is working when I'm sorting the nodes in the singly linked way (only with ->next), but I can't make it work with ->prev pointers.
Here is the code I'm using:
void sort(int count)
{
struct data *tmp,*current,*nextone;
int i,j;
for(i=0;i<count;i++)
{
current = first;
for(j=0;j<count-1-i;j++ )
{
if(current->number > current->next->number)
{
nextone = current->next;
current->next = nextone->next;
nextone->next = current;
if(current == first)
{
first = nextone;
current = nextone;
}
else
{
current = nextone;
tmp->next = nextone;
}
}
tmp = current;
current = current->next;
}
}
}
And this is the structure I'm using (with the global variables for the first and last element of the list):
struct data
{
int id;
char name[20];
int number;
struct data *next;
struct data *prev;
};
struct data *first = NULL;
struct data *last = NULL;
Below logic would work.
I would follow similar algorithm... If you want to move the entire nodes...
struct data *before, *after;
if(current->number > current->next->number)
{
before = current->prev;
after = current->next;
if(before != NULL){
before->next = after;
}
current->next = after->next;
current->prev = after;
after->next = current;
after->previous = before;
}
Alternatively, you can simply swap the numbers in the nodes without bothering to move entire nodes, if sorting of the data is the purpose. You can expand the below logic to include swapping of both char array and id as well.
if(current->number > current->next->number)
{
int tempNum = current->number;
current->number = current->next->number;
current->next->number = tempNum;
}
You just need to sit down and think about it for a bit.
Assigning first? Well the previous must be null.
Assigning last? Next must be null.
Anything else you need to do a little swap-dance with previous & next of the elements you are swapping.
A much easier way (that will quickly become faster too for larger array sizes) is to allocate an array of pointers, fill those with pointers to the elements, qsort the array then do another pass to re-wire the pointers (and delete the temp array).
One easier way is just to copy the data of the node, you can swap the data but the pointer to the node.
if(current->number > current->next->number){
swap(current->id,current->next->id);
swap(current->name,current->next->name);
swap(current->number,current->next->number);
}
With your method, you never set the previous pointer at all. You can sort the double linked list as single list at first, and then traversal the list again to set all the prev pointer.
Of course, you can sort the double linked list at a time, but it's a little complex to implement. You should consider 4 nodes each step, i.e current, current->next, as well as current->prev and current->next->next. When you want to swap current and current->next,
you should set current->prev->next=current->next, current->next=current->next->next so on and so forth.

Manually sort a linked list in the C programming language

How do you sort a linked list by name in a function in C?
struct rec{
char name[20];
int nr;
struct rec *nextRec;
};
typedef struct rec Rec; /* synonym for struct rec */
typedef Rec *RecPtr; /* synonym for pointer */
void SortLinkedList(RecPtr *sPtr, int p_size);/* prototype */
int main() {
RecPtr startPtr = NULL;
/* filling upp the linked list... size = nr of nodes in list */
SortLinkedList(&startPtr, size);
}
void SortLinkedList(RecPtr *sPtr, int p_size){
int i, j;
RecPtr tempPtr;
RecPtr currentPtr;
RecPtr nextPtr;
currentPtr = *sPtr;
nextPtr = currentPtr->nextRec;
for( j = 0; j <= p_size; j++) { /* loop for nr of nodes */
for(i = 0; i <= p_size -1 ; i++) { /* loop for one less than nr of nodes */
if(strcmp(currentPtr->name, nextPtr->name) < 0) { /* it less than ...*/
tempPtr = currentPtr;/* ...swap with temp */
currentPtr = nextPtr; /* but this sorting doesn'nt work */
nextPtr = tempPtr;
}
currentPtr = nextPtr;
currentPtr = currentPtr->nextRec;
}
}
}
The problem seems to be that you are just manipulating the pointers and not the objects themselves. When you sort a linked list, you necessary have to break links and re-make them.
For example, in your case there are three links and they have to be modified as specified in the brackets.
prevPtr->nextRec (This needs to be changed to point to nextPtr instead of currentPtr)
currentPtr->nextRec (This needs to be changed to point to nextPtr->nextRec instead of nextPtr)
nextPtr->nextRec (This needs to be changed to point to currentPtr)
You necessarily need to have a prevPtr and keep track of it in your program.
nextRec nextRec nextRec
prevPtr -------------->currentPtr ------------------------->nextPtr---------------------------> (nextPtr->nextRec)
Needs to be changed to
nextRec nextRec nextRec
prevPtr ----------------------->nextPtr ------------------> currentPtr-------------------> (nextPtr->nextRec)
Your problem is that your are only manipulating the RecPtr variables, which has no permanent effect, when you should manipulate the nextRec fields of the structs in the list instead.
Not to answer your question, but a couple of observations:
using typedefs that hide the fact that something is a pointer is generally considered bad style
if you need sorting, then a linked list is not the best structure to use - you may be better of with an array
WARNING: The following is most likely overkill for what you're wanting to do. But I'm frustrated trying to track down a subtle but nasty bug and need a distraction.
If I may offer some suggestions...
First of all, IMO it's easier to insert items into a list in order than to sort an unordered list; what I would do is create an insertInOrder function that takes your list head, the new element to be added, and a predicate (a pointer to a function) that compares records, and returns the new head of the list:
Rec *insertInOrder(Rec *head, Rec *new, int (*cmp)(Rec *, Rec *))
{
if (head == NULL)
{
return new;
}
else if (cmp(new, head) < 0) // new is "less than" head
{
new->nextRec = head;
return new; // new becomes the new head of the list
}
else
{
Rec *tmp = head;
/**
* Find the first element in the list that is not "less than" new
*/
while (tmp->nextRec != NULL && cmp(new, tmp->nextRec) > 0)
{
tmp = tmp->nextRec;
}
if (tmp->nextRec == NULL)
{
// insert new at end of list
tmp->nextRec = new;
new->nextRec = NULL;
}
else
{
// insert new before tmp->nextRec
new->nextRec = tmp->nextRec;
tmp->nextRec = new;
}
// keep the current list head
return head;
}
}
Now you can order the list based on different criteria. The cmp argument points to a function that takes two record pointers and compares them, returning -1 if the first argument is "less than" the second, 1 if the first argument is "greater than" the second, and 0 if they compare equal. For example, if you want to sort by names, define a function like
int compareNames(Rec *e1, Rec *e2)
{
int r = strcmp(e1->name, e2->name);
if (r < 0) return -1;
if (r > 0) return 1;
return 0;
}
and call insertInOrder as
head = insertInOrder(head, new, compareNames);
To sort the list, given a particular predicate: starting at the head, remove one element at a time from the existing list and add it to a new list, using the indicated predicate:
Rec *sortLinkedList(Rec *head, int (*cmp)(Rec *, Rec *))
{
Rec *newList = NULL;
while (head)
{
Rec *tmp = head;
head = head->nextRec;
tmp->nextRec = NULL;
newList = insertInOrder(newList, tmp, cmp);
}
return newList;
}
...
head = sortLinkedList(head, compareNr);
...
head = sortLinkdeList(head, compareNames);
...
head = sortLinkedList(head, compareSomethingElse);
Like Neil, I'm not terribly fond of typedefs for pointer types; experience has shown me that they cause more problems than they're worth.
If you are going to changed the order of a linked list then at some stage you are going to have to write to the nextRec pointers on the link list. As the only assignments that you make are to local pointer variables you are not making any changes to the list.
You don't do any resetting between the i loop and the j loop so it's hard to see how your algorithm guarantees not to go beyond the end of the list, although often it's not going to move very far.
If the strcmp test doesn't trigger for two iterations then it will always leave nextPtr alone and always assign currentPtr to nextPtr->nextRec so neither nextPtr nor currentPtr will change.
currentPtr = nextPtr;
currentPtr = currentPtr->nextRec;
Do you really mean to use i++ in the loop body as well as the increment part of the for loop?
What is your sorting algorithm? Note that if you need to swap two elements position in a singly linked list then you will need to retain the previous nodes to the elements being swapped so that you can adjust the previous nodes' "next" pointer.
you can sort a linked list with bubble sort easily(iterating through it, keeping track of the i-1th element in case of swapping). You could also do a quicksort rather easily(in fact, I've heard some people suggest that quicksort makes more sense on a linked list than on an array).
I would suggest merge-sort on the list as it is O(N Log N) while bubble-sort or insertion-sort are both O(N^2).
Here's good example of a simple singly linked list merge sort.
You can sort the list either through selection sort or bubble sort. Instead of data you need to swap the pointer. Below I am giving both sort examples which may helpful to your issue.
struct link
{
int data;
struct link *next;
};
selection(struct link **first)
{
struct link *p,*q,*pp,*qq,*temp;
pp=p=*first;
while(p->next!=NULL)
{
q=p->next;
while(q!=NULL)
{
if(p->data > q->data)
{
if(p==*first)
{
if(q==p->next)
{
p->next=q->next;
q->next=p;
}
else
{
temp=q->next;
q->next=p->next;
p->next=temp;
qq->next=p;
}
*first=q;
pp=*first;
}
else
{
if(q==p->next)
{
p->next=q->next;
q->next=p;
pp->next=q;
}
else
{
temp=q->next;
q->next=p->next;
p->next=temp;
qq->next=p;
pp->next=q;
}
}
temp=p;
p=q;
q=temp;
}
qq=q;
q=q->next;
}
pp=p;
p=p->next;
}
return 0;
}
bable(struct link **first)
{
struct link *p,*q,*lt,*pp,*temp;
lt=NULL;
p=*first;
while((*first)->next!=lt)
{
pp=p=*first;
while(p->next!=lt)
{
q=p->next;
if((p->data)>(q->data))
{
if(p==*first)
{
p->next=q->next;
q->next=p;
*first=q;
pp=*first;
}
else
{
p->next=q->next;
q->next=p;
pp->next=q;
}
temp=p;
p=q;
q=temp;
}
pp=p;
p=p->next;
}
lt=p;
}
return 0;
}

Resources