I'm trying to figure out how to initialize an array of pointers to linked lists that has a dynamic size based on user input.
I've got a struct as follows:
struct HashTable {
int tableSize;
int (*getKey)(void *);
char * (*toString)(void *);
void (*freeHashObject)(void *);
Boolean (*compare)(void *, void *);
ListPtr table;
};
The table variable is supposed to contain a dynamic number of linked lists depending on user input. You can assume that n is a valid integer and that createHashObject works as it is supposed to.
HashTablePtr table;
HashObjectPtr job;
table = createHashTable(n, getKey, toString, freeHashObject, compare);
for (i=0; i<n; i++)
{
job = createHashObject(firstInput);
HashInsert(table, job);
}
I believe the problem lies in createHashTable which is as follows.
HashTablePtr createHashTable(int size, int (*getKey)(void *),
char * (*toString)(void *), void (*freeHashObject)(void *),
Boolean (*compare)(void *, void *))
{
HashTablePtr h = (HashTablePtr)malloc(sizeof(HashTable));
//dont worry that i dont do things with the function pointers
h -> table = (ListPtr)malloc(sizeof(List)*size);
int i;
for (i = 0; i < size; i++)
{
h -> table[i] = createList(getKey, toString, freeHashObject);
}
}
The above code doesn't seem to correctly initialize all the linked lists. Here is the code for the createList function and the List struct.
ListPtr createList(int(*getKey)(void *),
char * (*toString)(void *),
void (*freeHashObject)(void *))
{
ListPtr list;
list = (ListPtr) malloc(sizeof(List));
list->size = 0;
list->head = NULL;
list->tail = NULL;
list->getKey = getKey;
list->toString = toString;
list->freeObject = freeHashObject;
return list;
}
struct list {
int size;
NodePtr head;
NodePtr tail;
int (*getKey)(void *);
char * (*toString)(void *);
void (*freeObject)(void *);
};
I am willing to wager that the issue lies in how I am defining the dynamically sized array of pointers to the linked lists and then initializing it. Can anyone offer me some help with that?
Thank you.
In your HashTable structure change the table definition to declare it as pointer, as below
struct HashTable {
...
ListPtr *table;
};
Then you can allocate as many you want which you have done correctly.
Related
I'm working on a project that strictly requires to realize two set of functions in C with same signature that can be used from a sigle .c test file. one set is for a data structure, the other one for a different and incompatible data structure.
Since in C there is no polymorphism is not possible to call a function that has two implementation with same signature in two different headers (.h) files and taking for granted that the call will be referred to the right implementation of the function that is actually capable of managing the right data structure.
Ok I know it seems impossible and contradictory but..that is it...
I have to merge two generic items that can be list or dynamic array
Update:
on List.h (dynamicArray is in another .h)
typedef struct Node{
void *data;
struct Node *next, *prevNode;
} Node;
//typedef struct declaration List
typedef struct List {
struct Node *top, *bot, *prev;
int size;
} List;
//in the dynamicarray.h file:
typedef struct dynamicArray{
void **array;
size_t size;
size_t capacity;
}dynArray;
//in the dynamicarray.h file:
void* merge(void *element1,void *element2, int parameters){
void * returner;
if (parameters==ARRAY) {
returner= Array_Merge(element1,element2); // expected to receive two arrays
}
else {
returner= List_Merge(element1,element2); // expected to reveice two lists
}
return returner;
}
Do you have any suggestion to accomplish this request?
Thanks.
You need to pass both, a pointer to your function and some handler function to the test, along with argument(s). In 'c' void * can be use in place of any pointer. Something like the following might work for you:
int mytest(void*(*function)(void *), int(*handler)(void *), void *arg) {
if (handler(function(arg)))
return OK;
return FAIL;
}
So, you just need to have separate handler functions for arrays and lists and pass them to the test function along with other params.
Answering your last comment
I can imagine some scheme as the following.
List list1;
dyArray array1;
MergedList outList;
MergedArray outArray;
...
void *getNextArrayElement(dynArray *array){...}
void *getNextListElement(List *list){...}
int mergeAsList(void* el, void *list){
if (el == NULL)
return 0;
ListMember *mmb = malloc(sizeof(ListMember));
mmb->el = el;
mmb->next = ((MergeList*)list)->head;
(MergeList*)mergeList->head = mmb;
return 1;
}
int mergeAsArray(void *el, void *array) {
if (el == NULL)
return 0;
if (((MergeArray *)array)->index) >= MAX)
return 0;
((MergeArray *)array)[((MergeArray *)array)->index++] = el;
return 1;
}
int mergeAsSortedArray(void *el, void *array){...}
...
test(getNextArrayEelement, mergeAsList, &arraty1, &outList);
test(getNextListEelement, mergeAsList, &list1, &outArray);
...
int test (void *(get*)(void*),
int (merge*)(void *m1, void *result),
void *in,
void *out) {
void *el = get(in);
int res = merge(el, out);
return res;
}
Function pointers are the means in which you accomplish this.
Function pointers are what you would use if, for example, you wanted to pass a function to a sort function that told the sort function how to compare two adjacent members. Such a comparison function allows you to provide a generalized sort function that will work on a collection of any struct, since you can change out the comparison function to accommodate any struct.
Consider the following sort code:
typedef struct node{
void* item;
struct node* next;
} Node;
// Just an ordinary bubble sort
void sort(Node *start, bool greaterThan(void* a, void* b))
{
int swapped, i;
Node *ptr1;
Node *lptr = NULL;
/* Checking for empty list */
if (start == NULL)
return;
do
{
swapped = 0;
ptr1 = start;
while (ptr1->next != lptr)
{
if (greaterThan(ptr1->item, ptr1->next->item))
{
swap(ptr1, ptr1->next);
swapped = 1;
}
ptr1 = ptr1->next;
}
lptr = ptr1;
}
while (swapped);
}
// Swap function used above
void swap(Node *a, Node *b)
{
void* temp = a->item;
a->item = b->item;
b->item = temp;
}
To use it, we just need to define a payload to put into Node* item and a sort function to tell it how to order the items:
typedef struct {
int book_id;
char title[50];
char author[50];
char subject[100];
char ISBN[13];
} Book;
// Comparison function.
bool bookGreaterThan(void* left, void* right)
{
Book* a = (Book*)left;
Book* b = (Book*)right;
return strcmp(a->title, b->title) > 0;
}
Finally, you would sort your list like so:
// Pass a pointer to the first node in your list, and a function pointer to your comparer.
sort(pointerToMyList, bookGreaterThan);
A complete example can be found here.
See also Is it possible to achieve runtime polymorphism in C?
I'm trying to add values to a binary search tree so for that I've written two functions one that create a new node and another which insert these values to the tree
So this is the code
typedef struct Node {
void *data;
struct Node *left;
struct Node *right;
} Node;
Node *createNode(void *data,size_t s){
Node *newNode = (Node *)malloc(s * sizeof(Node));
newNode->data = data;
newNode->left = NULL;
newNode->right = NULL;
return newNode;
}
void addValueBt(Node ** node, void *data, size_t s,int (* compar)(const void *, const void *)){
if (*node == NULL)
*node = createNode(data,s);
else if (compar((*node)->data,data) > 0)
addValueBt(&(*node)->left, data, s, compar);
else
addValueBt(&(*node)->right, data, s, compar);
}
When I call addValueBt in the main with two different ways (but normally seems to be the same thing) I don't get the same result
1st way:
int main(){
Node *root = NULL;
for(int i = 0; i < 10; i++)
addValueBt(&root, &i, 1, myCompar);
printBt(root,print);//print the whole tree
}
The display gives this:
10
10
10
10
10
10
10
10
10
10
2nd way:
int main(){
int a = 8, b = 9, c = 5, d = 1;
addValueBt(&root, &a, 1, myCompar);
addValueBt(&root, &b, 1, myCompar);
addValueBt(&root, &c, 1, myCompar);
addValueBt(&root, &d, 1, myCompar);
printBt(root,print);
}
The display:
1
5
8
9
"myCompar" compares between two integers
My question is: why it doesn't work when we use the function 'addValueBt' in the loop ?
(normally it should give the same result as for the other one )
Primary problem
As user3386109 correctly pointed out in a comment, the primary problem is that you're storing a pointer to the data in your tree, not a copy of the data. All the nodes end up pointing at the same location, so when that location changes, the value pointed at by the tree changes in all nodes at once.
Secondary issues
In a collection of comments (some of them responses to comment questions by Yasmine, the OP), I noted:
This is tangential to your main problem, but the parameter s is initially confusing. It's always 1 because you only allocate one node at a time. If it was larger, you'd waste the extra space that createNode() does allocate but does not initialize. You could stop using s (assume 1) without causing problems in the visible code.
I mean that in Node *createNode(void *data, size_t s) { Node *newNode = (Node *)malloc(s * sizeof(Node));, you allocate an array of Node structures of the size given by s. However, you initialize only the first structure allocated, and you don't record the size of the array anywhere. This doesn't matter because if you track the calls from main() to addValueBt() to createNode(), the value of s is always 1. But because it is always 1, you really have no need to pass it, so you can simplify the calls all the way down the calling chain.
Note that #user3386109: has identified the problem. You store the same pointer in each element of the tree, so when the pointed at value changes, all the elements of the tree change at the same time. And if the pointed at value goes out of scope, you get undefined behaviour. You either need to make a copy of the data in the createNode() function, which means that you need to know how big the data is so that you can allocate new space to store it, or you need to ensure that a different pointer is passed to the code each time, and those pointers remain valid until the tree is freed.
You can see with your working code, you pass a different pointer each time (a pointer to a different variable). When you repeatedly pass a pointer to the same variable, the same address is stored, so the current value stored at that address is used by all the nodes; they all have the same value.
Part of the problem comes from using void *data; instead of a simple int data;. For the immediate purposes, life would be much, much simpler if your data structure used int data;. You'd have:
Node *createNode(int key)
{
Node *newNode = (Node *)malloc(sizeof(Node));
newNode->data = data; newNode->left = NULL;
newNode->right = NULL;
return newNode;
}
If you store void *, you really need to know how long the data you're storing pointers to is, so that you can make copies, etc. Your comparator can make assumptions about the length (and you'll be OK as long as those assumptions are correct). So, you'd need:
Node *createNode(void *data, size_t len)
{
void *copy = malloc(len);
memmove(copy, data, len);
Node *newNode = (Node *)malloc(s * sizeof(Node));
newNode->data = copy;
newNode->left = NULL;
newNode->right = NULL;
return newNode;
}
Yasmine commented:
I know but I have to do that so that it can be used with other types like float or char.
At that point, I started to produce the code below.
Working code
Here's an offering dealing with the points made in my comments.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
typedef struct Node
{
void *data;
size_t datalen;
struct Node *left;
struct Node *right;
} Node;
static int cmp_dbl(const void *p1, const void *p2);
static int cmp_int(const void *p1, const void *p2);
static void freeBt(Node *node);
static void printBt(Node *node, void (*print)(const void *));
static void print_dbl(const void *p);
static void print_int(const void *p);
extern Node *createNode(void *data, size_t len);
extern void addValueBt(Node **node, void *data, size_t len, int (*compar)(const void *, const void *));
Node *createNode(void *data, size_t len)
{
void *copy = malloc(len);
// Error check allocation!
memmove(copy, data, len);
Node *newNode = (Node *)malloc(sizeof(*newNode));
// Error check allocation!
newNode->data = copy;
newNode->left = NULL;
newNode->right = NULL;
newNode->datalen = len;
return newNode;
}
void addValueBt(Node **node, void *data, size_t len, int (*compar)(const void *, const void *))
{
if (*node == NULL)
*node = createNode(data, len);
else if (compar((*node)->data, data) > 0)
addValueBt(&(*node)->left, data, len, compar);
else
addValueBt(&(*node)->right, data, len, compar);
}
int main(int argc, char **argv)
{
unsigned seed = time(0);
if (argc == 2)
seed = atoi(argv[1]);
srand(seed);
printf("Seed: %u\n", seed);
Node *root = NULL;
for (int i = 0; i < 10; i++)
addValueBt(&root, &i, sizeof(i), cmp_int);
printBt(root, print_int);
freeBt(root);
root = NULL;
for (int i = 0; i < 10; i++)
{
double d = (double)rand() / RAND_MAX * 1000.0 + (double)rand() / RAND_MAX;
addValueBt(&root, &d, sizeof(d), cmp_dbl);
}
printBt(root, print_dbl);
freeBt(root);
return 0;
}
static int cmp_int(const void *p1, const void *p2)
{
int v1 = *(int *)p1;
int v2 = *(int *)p2;
return (v1 > v2) - (v1 < v2);
}
static int cmp_dbl(const void *p1, const void *p2)
{
double v1 = *(double *)p1;
double v2 = *(double *)p2;
return (v1 > v2) - (v1 < v2);
}
static void print_int(const void *p)
{
printf("%d\n", *(int *)p);
}
static void print_dbl(const void *p)
{
printf("%8.3f\n", *(double *)p);
}
static void printBt(Node *node, void (*print)(const void *))
{
if (node != 0)
{
printBt(node->left, print);
print(node->data);
printBt(node->right, print);
}
}
static void freeBt(Node *node)
{
if (node != 0)
{
freeBt(node->left);
freeBt(node->right);
free(node);
}
}
Here's an example run:
Seed: 1511037421
0
1
2
3
4
5
6
7
8
9
87.907
118.694
140.163
170.833
343.940
412.792
422.254
530.731
557.656
936.981
What changed?
I dropped the parameter s to addValueBt() and createNode(), but added a parameter size_t len. That defines the length of the data. The length is stored in size_t datalen in the Node structure.
The code in createNode() makes a copy of the data it is passed and stores that copy in the tree. This ensures that each node is unaffected by changes to the values in the calling code.
What was added?
I added code for the int comparator (you called yours myCompar(); I called mine cmp_int() because I also wanted to work with double), and a double comparator cmp_dbl(). I added printing functions print_int() and printf_dbl() — renaming your print to print_int. I added memory release function freeBt(). I added code for printBt().
The code playing with the command line arguments allows you to specify a seed of your choosing. It's a bit sloppy; I should probably be using strtoul() instead of atoi(). But it allows you to specify a number on the command line to be used as the seed, which will give you repeatability when you need it. The seed is reported so you can reproduce results. This is helpful when playing with random numbers — sometimes you want randomness, and sometimes you want repeatability.
What I didn't do
I didn't fix the printing code so that the structure of the tree is visible. You just get a list of numbers.
The code doesn't really use the datalen member of the structure, but
it could be useful to check consistency, or for recording the length of strings.
Full error message
dll.c:89:13: error: cannot take the address of an rvalue of type 'void'
arr2[i] = &(*remove)(list);
My function is supposed to take 4 parameters, an two arrays, a length value, a remove function, and an insert function. It then takes the values of the first array and puts them in a linked list. Then it removes the values from the linked list and inserts them into the 2nd array. The part that Im having trouble with is inserting the values into the second array. I know the problem has to do with my understanding of pointers. I know the arrays are void * and my remove function returns void * so initially I tried arr2[i] = (*remove)(list) but I also got a compiler error. So I added the & just because this has seemed to work in the past ( I know not a good reason ) and now I get this error. Thanks for any help.
void transfer(char *arr1, char *arr2, int length, void (*insert)(List *, void *), void* (*remove)(List *)) {
List *list = List_create();
for (int i=0; i < length; i++) {
(*insert)(list, &arr1[i]);
}
for (int i=0; i < length; i++) {
arr2[i] = &(*remove)(list);
}
}
Header File
#include <stdlib.h>
typedef struct ListNode {
struct ListNode *next;
struct ListNode *prev;
void *value;
} ListNode;
typedef struct List {
int count;
ListNode *first;
ListNode *last;
} List;
List *List_create(void);
void add_to_back(List *list, void *value);
void *remove_from_back(List *list);
void add_to_front(List *list, void *value);
void *remove_from_front(List *list);
void transfer(void *arr1, void *arr2, int length, void (*insert)(List *, void *), void* (*remove)(List *));
The signature of your remove function is
void remove(List *);
you call it here:
arr2[i] = &(*remove)(list);
but as the return value is void it's like
// arr2[i] = &(void);
I think you meant that remove returns a pointer to the removed element, but then the signature should be like:
void* remove(List *);
as a function pointer:
void* (*remove)(List *))
I'm trying to understand how does dynamic memory allocation in C work. So I coded this:
typedef struct person{
int id;
int credit;
}person_t;
typedef struct list{
int id;
person_t * people;
}list_t;
int main(){
list_t * list;
list = malloc(sizeof(list_t));
list->people = malloc(10 * sizeof(person_t)); //list for 10 people
free(list->people);
free(list);
}
, which appears to be correct. However, when I decided to create functions for allocation\deallocation, double free or corruption error started to appear:
void init_list(list_t * listptr, int size){
listptr = malloc(sizeof(list_t));
listptr->people = malloc(size * sizeof(person_t));
}
void clear_list(list_t * listptr){
free(listptr->people);
free(listptr);
}
int main(){
list_t list;
init_list(&list, 10); //list for 10 people
clear_list(&list);
}
Output:
Error in ./list: double free or corruption (out) : 0x00007ffc1b3fba70
Why could that be? Thanks in advance.
void init_list(list_t * listptr, int size){
listptr = malloc(sizeof(list_t));
listptr->people = malloc(size * sizeof(person_t));
}
is not correct. You are modifying listptr in the function. That does not change anything of list in main. You need to remove the line fhat changes listptr in that function. Use:
// listptr is already a valid pointer.
// There is no need to allocate memory for it.
void init_list(list_t * listptr, int size){
listptr->people = malloc(size * sizeof(person_t));
}
You have a worse mistake in clear_list.
void clear_list(list_t * listptr){
free(listptr->people);
free(listptr);
}
You are calling free on a pointer that was not allocated by a call to malloc. listptr is a pointer to the object that was created in stack in main. Remove the second call to free. Use:
// listptr is a pointer to an object on the stack in main.
// Trying to call free on it is an error.
void clear_list(list_t * listptr){
free(listptr->people);
}
I am trying to have an array of pointers to generic linked lists within a generic hash table be allocated dynamically with the size being dependent on user input. Please let me show you the code.
This is the driver, the user would input the size that they want the hash table to be which correlates directly to how many linked lists there should be in the hash table.
int main(int argc, char **argv)
{
int i;
int n;
int count;
unsigned int seed=0;
HashObjectPtr job;
HashTablePtr table;
if (argc < 2) {
fprintf(stderr, "Usage: %s <table size> [<test size=table size * 10>]);
exit(1);
}
n = atoi(argv[1]);
count = n;
if (argc >= 3) {
count = atoi(argv[2]);
count *= 10;
}
if (argc == 4) {
seed = atoi(argv[3]);
}
char * firstInput = (char *)malloc(sizeof(char) *
strlen("I'm a void star made at the beginning") + 1);
firstInput = strcpy (firstInput, "I'm a void star made at the beginning");
table = createHashTable(n, getKey, toString, freeHashObject, compare);
for (i=0; i<n; i++)
{
job = createHashObject(firstInput);
HashInsert(table, job);
}
runRandomTests(count, seed, n, table);
if (DEBUG > 0)
PrintHash(table);
free(firstInput);
FreeHashTable(table);
exit(
}
Here's the struct. I have the array of linked lists defined ListPtr * table => linkedList ** table;
typedef struct HashTable HashTable;
typedef struct HashTable * HashTablePtr;
struct HashTable {
int tableSize;
int (*getKey)(void *);
char * (*toString)(void *);
void (*freeHashObject)(void *);
Boolean (*compare)(void *, void *);
ListPtr * table;
};
HashTablePtr createHashTable(int size, int (*getKey)(void *), char * (*toString)(void *), void (*freeHashObject)(void *), Boolean (compare)(void *, void *));
void HashInsert(HashTablePtr table, HashObjectPtr object);
HashObjectPtr HashSearch (HashTablePtr table, HashObjectPtr obj);
void PrintHash(HashTablePtr table);
void FreeHashTable(HashTablePtr table);
HashObjectPtr HashRemove(HashTablePtr table, HashObjectPtr obj);
int HashFunction(HashObjectPtr obj);
This is the function that is initializing the linked lists.
HashTablePtr createHashTable(int size, int (*getKey)(void *), char * (*toString)(void *), void (*freeHashObject)(void *), Boolean (*compare)(void *, void *))
{
HashTablePtr h = (HashTablePtr)malloc(sizeof(HashTable));
h -> tableSize = size;
h -> getKey = getKey;
h -> toString = toString;
h -> freeHashObject = freeHashObject;
h -> compare = compare;
h -> table = (ListPtr *)malloc(sizeof(ListPtr)*size);
int i;
for (i = 0; i < size; i++)
{
h -> table[i] = createList(getKey, toString, freeHashObject);
}
}
This is the function that creates the linked lists
ListPtr createList(int(*getKey)(void *),
char * (*toString)(void *),
void (*freeHashObject)(void *))
{
ListPtr list;
list = (ListPtr) malloc(sizeof(List));
list->size = 0;
list->head = NULL;
list->tail = NULL;
list->getKey = getKey;
list->toString = toString;
list->freeObject = freeHashObject;
return list;
}
I stepped through this in the debugger in eclipse and it's compiling and running fine but when I am clicking on the "table" variable within h during the CreateHashTable function, everything looks fine, the loop iterates through and createsLists at each index. But when I go to insert, I get a segfault.
It has to be with the way I am initializing the array of ListPointers but I couldn't think of a better way to do.
Help please?
Thank you