I have made a Java like ArrayList class in C for educational purposes however currently it is only good for integers. I want to make it Generic so it can take any type of data. How do I go about it. I read somewhere about creating a typedef void pointer. Any thoughts ?
........................................................................................................
Here is My code
#ifndef ARRAYLIST_H
#define ARRAYLIST_H
typedef struct ArrayList ArrayList;
typedef int bool;
#define false 0
#define true 1
struct ArrayList {
int *con;
int numElements;
int conSize;
};
ArrayList *createArrayList();
void freeArrayList(ArrayList *);
void add(ArrayList *, int);
void printList(ArrayList *);
void resize(ArrayList *);
int remove(ArrayList *, int);
bool isEmpty(ArrayList*);
int getNumElements(ArrayList*);
int getConSize(ArrayList*);
#endif
_____________________________________
#include<stdio.h>
#include<stdlib.h>
#include"Consts.h"
#include "ArrayList.h"
#define CAPACITY 5
ArrayList *createArrayList() {
ArrayList *arrayList = malloc(sizeof(ArrayList));
arrayList->conSize = CAPACITY;
arrayList->numElements = 0;
arrayList->con = malloc(sizeof(int) * CAPACITY);
return arrayList;
}
void freeArrayList(ArrayList * arrayList) {
if (arrayList == NULL) {
return;
}else {
free(arrayList->con);
free(arrayList);
}
}
void add(ArrayList *arrayList, int input) {
//printf("Num elements in add method before adding %d \n", arrayList->numElements);
if (arrayList->numElements >= arrayList->conSize) {
resize(arrayList);
printf("resized\n");
}
int size = arrayList->numElements;
//add element to the last
arrayList->con[size] = input;
arrayList->numElements = arrayList->numElements + 1
}
void resize(ArrayList *arrayList) {
int num = arrayList->numElements;
int oldSize = arrayList->conSize;
int newSize = oldSize + 50;
int *temp = realloc(arrayList->con, sizeof(type) * newSize);
if (temp != NULL) {
arrayList->con = temp;
arrayList->conSize = newSize;
}
}
int remove(ArrayList * arrayList, int val) {
int i = 0;
while (arrayList->con[i] != val) {
i++;
}
//remove this index
if (i == arrayList->conSize) {
return -1;
}
else {
int removedVal = arrayList->con[i]; int j;
for (j = i; j < arrayList->numElements ; j++) {
arrayList->con[j] = arrayList->con[j + 1];
}
arrayList->con[j + 1] = NULL;
arrayList->numElements = arrayList->numElements - 1;
return removedVal;
}
}
If you want the array list be able to store any type of data, you need to
1) make con a void pointer. void *con;
2) store additional metadata in the struct about the memory alignment of the type
3) add one more parameter to the constructor of array list, which is the additional metadata mentioned in 2)
4) when allocating memory, use the stored metadata instead of sizeof(whatever), like temp=malloc(stored_metadata_about_type*50);
Also notice that it is not usually a good idea to hardcode 50, and better declare it as a constant like buffer_size.
Related
As a learning exercise, I'm trying to implement my own dictionary in C. My dictionary type definition is:
#define MAXCAP 24
static size_t primes[MAXCAP + 2] = {
1,
101, 251, 509, 1021, 2039, 4093, 8191,
16381, 32749, 65521, 131071, 262139, 524287, 1048573,
2097143, 4194301, 8388593, 16777213, 33554393, 67108859, 134217689,
268435399, 536870909, 10737441789, 2147483647};
typedef struct item_tag item;
struct item_tag {
void *key;
void *val;
item *next;
};
typedef struct dict_tag {
size_t cap; // capacity of the dict, which is used as an index for primes[]
size_t size; // number of slots taken up out of the capacity of the dict
item **items;
int (*eq) (const void *, const void *);
int (*hash) (const void *, size_t n);
} dict;
My function for inserting a new entry to the dict is:
int dict_put(void *key, void *val, dict *d) {
int i;
item *kv;
if (!dict_get(key, d)) {
kv = malloc(sizeof(item));
kv->key = key;
kv->val = val;
kv->next = d->items[(i = d->hash(key, primes[d->cap]))];
d->items[i] = kv;
if (!kv->next)
d->size++;
if (d->size >= primes[d->cap] / 2)
expand(d);
return 1;
}
return 0;
}
Insertion works fine if I do not try to resize the dict using expand function which is defined as:
static void expand(dict *d) {
int i;
item *kv;
dict *tmp;
if (d->cap < MAXCAP) {
tmp = malloc(sizeof(dict));
init(d->cap + 1, d->hash, d->eq, tmp);
for (i = 0; i < d->cap; i++) {
for (kv = d->items[i]; kv; kv = kv->next)
dict_put(kv->key, kv->val, tmp);
}
destroy_items(0, d);
d->cap = tmp->cap;
d->size = tmp->size;
d->items = tmp->items; // looks like there are no items in dict after this step
free(tmp);
} else
fprintf(stderr, "dict: max size reached.\n");
}
In the above function, I'm trying to create a new temporary larger dict and then copy the pointer to the new list of items to the old dict. The init function is:
static void init(size_t n, const int (*hash) (const void *, size_t n), const int (*eq) (const void *, const void *),
dict *d) {
d->cap = n;
d->size = 0;
d->eq = eq;
d->hash = hash;
d->items = calloc(primes[d->cap], sizeof(item *));
}
Your problem is on this line in the expand function
for (i = 0; i < d->cap; i++) {
should be changed to
for (i = 0; i < primes[d->cap]; i++) {
This question already has an answer here:
Making a generic ArrayLIst in C
(1 answer)
Closed 2 years ago.
I am trying to implement a java-like arraylist in C and now I am trying to make it generic. I am new to C and pointer arithmetic but I tried using void* pointer for container, then element size(for size of data type), capacity and size. I am trying to debug my code and it is giving a random value on the screen.
I cant seem to find what is wrong with my add method ? and how should I go about the printlist method as we are not sure about the data type. I think my printLIst is wrong.
I am new to C and playing around for educational purposes and any help is appreciated. Thank you
____________________________________________________________________
#ifndef ARRAYLIST_H
#define ARRAYLIST_H
typedef struct ArrayList ArrayList;
typedef int bool;
#define false 0
#define true 1
struct ArrayList {
void *con;
int elementSize;
int numElements;
int conSize;
};
ArrayList *createArrayList(int );
int remove(ArrayList *, int);
void freeArrayList(ArrayList *);
int add(ArrayList *, void*);
void *getAtIndex(ArrayList *, int);
void printList(ArrayList *);
void resize(ArrayList *);
bool isEmpty(ArrayList*);
int getNumElements(ArrayList*);
int getConSize(ArrayList*);
#endif
_______________________________________________________________________
#include<stdio.h>
#include<stdlib.h>
#include"Consts.h"
#include<memory.h>
#include "ArrayList.h"
#define CAPACITY 5
#define EXTRA 100
ArrayList *createArrayList(int elementSize) {
ArrayList *arrayList = malloc(sizeof(ArrayList));
arrayList->elementSize = elementSize;
arrayList->conSize = CAPACITY;
arrayList->numElements = 0;
arrayList->con = malloc(elementSize * CAPACITY);
return arrayList;
}
void freeArrayList(ArrayList * arrayList) {
if (arrayList == NULL) {
return;
}else {
free(arrayList->con);
free(arrayList);
}
}
int add(ArrayList *list, void *input) {
if (list != NULL && input != NULL) {
if (list->numElements >= list->conSize) {
resize(list);
printf("resized\n");
}
list->con = input;
memcpy((char*)list->con + (list->numElements*list->elementSize), input, list->elementSize);
list->numElements++;
return 1;
}
return -1;
}
void resize(ArrayList *list) {
void *temp = realloc(list->con, (list->conSize + EXTRA) * list->elementSize);
if (temp != NULL) {
list->conSize += 100;
list->con = temp;
}
}
int remove(ArrayList * list, int i) {
if (list != NULL) {
//find index of value to remove
int elementSize = list->elementSize;
int lenToMove = elementSize * (list->numElements - (i + 1));
memmove((char *)list->con + i*elementSize, (i+1)*elementSize, lenToMove);
list->numElements--;
return 1;
}
return -1;
}
void printList(ArrayList *list) {
if (list != NULL) {
char *p = list->con;
for (int i = 0; i < list->numElements; i++) {
void* val = getAtIndex(list, i);
printf("%d \n", val);
}
}
}
void *getAtIndex(ArrayList *listptr, int index) {
return (char*)listptr->con + index * (listptr->elementSize);
}
int getNumElements(ArrayList * list) {
return list->numElements;
}
int getConSize(ArrayList * list) {
return list->conSize;
}
bool isEmpty(ArrayList * list) {
return list->numElements == 0;
}
___________________________________________________
#include<stdio.h>
#include<stdlib.h>
#include"ArrayList.h"
#include "Consts.h"
#pragma warning(disable : 4996)
int main() {
ArrayList * list = createArrayList(sizeof(int));
int x = 5;
add(list, &x);
printf("%d",(int *)getAtIndex(list, 0));
freeArrayList(list);
system("pause");
return 0;
}
Pay attention to the comments above. I know you're new to the site, but it's worth while getting to know how it works.
Anyway the problem is in this line of code:
printf("%d",(int *)getAtIndex(list, 0));
getAtIndex() returns a pointer so you need to dereference it to get the value. It should be:
printf("%d",*(int *)getAtIndex(list, 0));
I've been trying to get my code to work. It compiles but when I run it I get a segfault and gdb and valgrind pointed to this line in particular and my problem is that I don't really know how to fix it:
if (!pCrawl->cvec[index]) {
which is in addword().
Essentially I'm supposed to implement functions in the head file for a trie data structure: makedictionary, add, search, and delete.
Here's the school supplied header file:
#define VECSIZE ('z'-'a' + 1)
typedef char *word;
enum __bool__ { FALSE, TRUE };
typedef enum __bool__ bool;
typedef struct __tnode__ *Dict, TNode;
struct __tnode__ {
Dict cvec[VECSIZE];
bool eow;
};
void newdict(Dict *dp);
void addword (const Dict r, const word w);
bool checkword (const Dict r, const word w);
void delword (const Dict r, const word w);
void barf(char *s);
Also not that since I can't change the header file, and bool is a typedef I can't use stdbool.h.
And this is my C code which I'm writing up:
#define CHAR_TO_INDEX(c) ((int)c - (int)'a')
void newdict (Dict *dp) {
*dp = NULL;
dp = (Dict *)malloc(sizeof(Dict));
if (dp) {
int i;
(*dp)->eow = FALSE;
for (i = 0; i < VECSIZE; i++) {
(*dp)->cvec[i] = NULL;
}
}
}
void addword (const Dict r, const word w) {
int level;
int length = strlen(w);
int index;
Dict pCrawl = r;
printf("line 1\n");
for (level = 0; level < length; level++) {
index = CHAR_TO_INDEX(w[level]);
if (!pCrawl->cvec[index]) {
newdict(&(pCrawl->cvec[index]));
}
pCrawl = pCrawl->cvec[index];
}
pCrawl->eow = TRUE;
}
bool checkword (const Dict r, const word w) {
int level;
int length = strlen(w);
int index;
Dict pCrawl = r;
for (level = 0; level < length; level++) {
index = CHAR_TO_INDEX(w[level]);
if (!pCrawl->cvec[index]) {
return FALSE;
}
pCrawl = pCrawl->cvec[index];
}
if (pCrawl != NULL && pCrawl->eow) {
return TRUE;
} else {
return FALSE;
}
}
I'm a bit new to C so any tips would be greatly appreciated. Thanks in advance.
I'm guessing that the confusion is in understanding
typedef struct _tnode__ *Dict, TNode;
This means that
Dict lookup;
is the same as
TNode* lookup;
So when you are creating the dictionary
void newdict(Dict* dp)
Is the same as
void newdict(TNode** dp)
So when allocating, allocating sizeof(Dict) is the same as sizeof(TNode*) i.e. sizeof a pointer. What is really required is a TNode. Note - in C there is no need to cast mallocs. It is only required for C++.
*dp = malloc (sizeof(TNode));
Try that and see if it fixes your problem.
I need to initialized the hash table with the size i get, i have a problem here t->arr_table[i]->key = NULL;
#include <stdio.h>
typedef struct element{
char * key;
char * value;
}element;
typedef struct HashTable{
int size; // size of the arr
element **arr_table; //arr of elements
}HashTable;
void init_hash(int size, HashTable * t)
{
if (size < 1)
return;
t->size = size;
t->arr_table = (element **)malloc(sizeof(element*)*size);
if (t->arr_table == NULL) // out memory
return;
int i;
for (i = 0; i < size; i++)
{ // initial list
t->arr_table[i]->key = NULL;
t->arr_table[i]->value = NULL;
}
}
void main()
{
HashTable *ht = (HashTable*)malloc(1*sizeof(HashTable));
int size_ht = 9;
init_hash(size_ht, ht);
printf("...\n");
return;
}
What you've made is an array of pointers to elements. However, the init_hash function seems to expect an array of elements. To create an array of elements the code should be as shown below. I've added some comments to highlight some of the changes.
typedef struct element{
char *key;
char *value;
}element;
typedef struct HashTable{
int size;
element *arr_table; // <-- only one '*', not two, to declare a pointer to an array of elements
}HashTable;
void init_hash(int size, HashTable *t)
{
if (size < 1)
return;
t->size = size;
t->arr_table = malloc(sizeof(element) * size); // <-- allocate memory for the elements, note 'sizeof(element)' not 'sizeof(element *)'
if (t->arr_table == NULL)
return;
int i;
for (i = 0; i < size; i++)
{
t->arr_table[i].key = NULL; // <-- table[i] is a structure, use dot notation
t->arr_table[i].value = NULL;
}
}
int main( void ) // <-- declare main with the correct signature
{
HashTable *ht = malloc(sizeof(HashTable)); // <-- don't cast the return value from malloc
int size_ht = 9;
init_hash(size_ht, ht);
printf("...\n");
}
Would someone please help me with thhis c pointers issue. It seems to me like it should work but there must be something im not understanding as the code doesnt compile. On the line where is says this: ERROR<<<<>>>>>>>>. I think the problem is with function getDynArr which is at the tag: GETDYNARR<<<<<<<<<<<<<<<<<<<<>>>>>>>>>>>>>>>>. I think getDynArr(da, i) may be returning int for some reason?
/* dynArr.c: Dynamic Array implementation. */
#include <assert.h>
#include <stdlib.h>
#include <stdio.h>
#ifndef KEYTYPE
#define KEYTYPE char *
#endif
#ifndef VALUETYPE
#define VALUETYPE double
#endif
#ifndef TYPE
#define TYPE struct association
//# define TYPE int
#endif
struct association
{
KEYTYPE key;
VALUETYPE value;
};
struct DynArr
{
TYPE *data; /* pointer to the data array */
// struct association *data;
int size; /* Number of elements in the array */
int capacity; /* capacity ofthe array */
};
///////////////////dictionary Section
void dyArrayDictionaryPut(struct DynArr *vec, KEYTYPE key, VALUETYPE val)
{
struct association *ap;
if (dyArrayDictionaryContainsKey(vec,key))
dyArrayDictionaryRemove(vec, key);
ap = malloc(sizeof(struct association));
assert(ap != 0);
ap->key = key;
ap->value = val;
//dyArrayAdd(vec, ap);
addDynArr(vec, ap);
}
VALUETYPE dyArrayDictionaryGet(struct DynArr *da, KEYTYPE key, VALUETYPE *valptr)
{
struct association *ap;
int i = 0;
if(dyArrayDictionaryContainsKey(da, key))
{
for ( i = 0; i < da->size; i++)
{
//if(da->data[i]->key == key)
if(da->data[i].key == key)
{
//return da->data->value;
struct association temp3;
temp3 = getDynArr(da, i); //ERROR<<<<<ERROR>>>>>>>>>incompatible types when assigning to type 'struct association' from type 'int'
//ap = getDynArr(da, i);
//*valptr = ap->value;
}
}
}
}
int dyArrayDictionaryContainsKey(struct DynArr *da, KEYTYPE key)
{
int i = 0;
for (i = 0; i < da->size; i++)
{
if(da->data[i].key == key)
{
return 1;
}
}
return 0;
}
void dyArrayDictionaryRemove(struct DynArr *da, KEYTYPE key)
{
for (int i = 0; i < da->size; i++)
{
if(da->data[i].key == key)
{
removeAtDynArr(da, i);
return;
}
}
}
////
void _initDynArr(struct DynArr *v, int capacity)
{
assert(capacity > 0);
assert(v!= 0);
v->data = malloc(sizeof(TYPE) * capacity);
assert(v->data != 0);
v->size = 0;
v->capacity = capacity;
}
struct DynArr* createDynArr(int cap)
{
struct DynArr *r;
assert(cap > 0);
r = malloc(sizeof( struct DynArr));
assert(r != 0);
_initDynArr(r,cap);
return r;
}
void freeDynArr(struct DynArr *v)
{
assert(v!=0);
if(v->data != 0)
{
free(v->data); /* free the space on the heap */
v->data = 0; /* make it point to null */
}
v->size = 0;
v->capacity = 0;
}
void deleteDynArr(struct DynArr *v)
{
assert (v!= 0);
freeDynArr(v);
free(v);
}
void _dynArrSetCapacity(struct DynArr *v, int newCap)
{
int i;
TYPE *oldData;
int oldSize = v->size;
oldData = v->data;
printf("========Resizing========\n");
/* Create a new dyn array with larger underlying array */
_initDynArr(v, newCap);
for(i = 0; i < oldSize; i++){
v->data[i] = oldData[i];
}
v->size = oldSize;
/* Remember, init did not free the original data */
free(oldData);
#ifdef ALTERNATIVE
int i;
/* Create a new underlying array*/
TYPE *newData = (TYPE*)malloc(sizeof(TYPE)*newCap);
assert(newData != 0);
/* copy elements to it */
for(i = 0; i < v->size; i++)
{
newData[i] = v->data[i];
}
/* Delete the oldunderlying array*/
free(v->data);
/* update capacity and size and data*/
v->data = newData;
v->capacity = newCap;
#endif
}
int sizeDynArr(struct DynArr *v)
{
assert(v!=0);
return v->size;
}
void addDynArr(struct DynArr *v, TYPE val)
{
assert(v!=0);
/* Check to see if a resize is necessary */
if(v->size >= v->capacity)
_dynArrSetCapacity(v, 2 * v->capacity);
v->data[v->size] = val;
v->size++;
}
//GETDYNARR<<<<<<<<<<<<<<<<<<<<<GETDYNARR>>>>>>>>>>>>>>>>>
//type is struct association
//TYPE getDynArr(struct DynArr *v, int pos )
struct association getDynArr(struct DynArr *v, int pos)
{
assert(v!=0);
assert(pos < v->size);
assert(pos >= 0);
struct association returned = v->data[pos];
return returned;
}
void putDynArr(struct DynArr *v, int pos, TYPE val)
{
assert(v!=0);
assert(pos < v->size);
assert(pos >= 0);
v->data[pos] = val;
}
void swapDynArr(struct DynArr *v, int i, int j)
{
TYPE temp;
assert(v!=0);
assert(i < v->size);
assert(j < v->size);
assert(i >= 0);
assert(j >= 0);
temp = v->data[i];
v->data[i] = v->data[j];
v->data[j] = temp;
}
void removeAtDynArr(struct DynArr *v, int idx){
int i;
assert(v!= 0);
assert(idx < v->size);
assert(idx >= 0);
//Move all elements up
for(i = idx; i < v->size-1; i++){
v->data[i] = v->data[i+1];
}
v->size--;
}
int main()
{
// struct DynArr *dictionary = createDynArr(2);
// dyArrayDictionaryPut(dictionary, "key1", 2);
//double result = dyArrayDictionaryGet(dictionary, "key1");
//dyArrayDictionaryContainsKey(struct DynArr *da, KEYTYPE key)
//dyArrayDictionaryRemove(struct DynArr *da, KEYTYPE key)
//printf("%f\n",result);
}
getDynArr is not declared before it is used, the declaration is further down in the file. The compiler should warn you about that.
Since the getDynArr is undeclared, the compiler doesn't know what parameters it takes or what it returns. The compiler then assumes some defaults, like a return type int, which leads to the error you're seeing.
To fix the problem, declare the function before you try to use it.
I'm not going to discuss the implementation as you haven't asked for it.
In order to compile you'll need to fix the following:
Missing function declarations for: getDynArr, dyArrayDictionaryRemove, dyArrayDictionaryContainsKey, addDynArr, removeAtDynArr; So you need to add the function signature just below the struct declarations, like:
struct association
{
KEYTYPE key;
VALUETYPE value;
};
struct DynArr
{
TYPE *data; /* pointer to the data array */
// struct association *data;
int size; /* Number of elements in the array */
int capacity; /* capacity ofthe array */
};
///////////////////dictionary Section
struct association getDynArr(struct DynArr *v, int pos);
void dyArrayDictionaryRemove(struct DynArr *da, KEYTYPE key);
int dyArrayDictionaryContainsKey(struct DynArr *da, KEYTYPE key);
void addDynArr(struct DynArr *v, TYPE val);
void removeAtDynArr(struct DynArr *v, int idx);
...
inside the function dyArrayDictionaryPut you need to call addDynArr passing the struct by value according to the function signature, so the call at line 54 would be:
addDynArr(vec, *ap);
You need to compile it as c99 code, otherwise you need to remove the variable initialization on every for loop (the gcc option is -std=c99 for instance);
Then let me give you a couple of suggestions you may find useful:
You may want to consider using typedef instead of define to create an alias for the struct association;
typedef struct
{
KEYTYPE key;
VALUETYPE value;
} association;
Then at this point you can use "association" as a type without the needs to mention it is a struct anymore (I guess you can find a better name for it as well);
Depending on the performance requirements, you may need to avoid returning or passing the struct by value around, using a pointer is much faster.