Given the desire to abstract the structure of a circular buffer from its content, and starting from the following code segments (courtesy of this wikipedia entry):
typedef struct
{
int value;
} ElemType;
typedef struct
{
int size; /* total number of elements */
int start; /* index of oldest element */
int count; /* index at which to write new element */
ElemType *elements; /* vector of elements */
} CircularBuffer;
void cbInit(CircularBuffer *cb, int size) {
cb->size = size;
cb->start = 0;
cb->count = 0;
cb->elements = (ElemType *)calloc(cb->size, sizeof(ElemType));
}
How does one abstract the element type so that it is specified when an instance of the CircularBuffer is defined? My attempt thus far is as follows:
CircularBuffer *cbInit(uint16 size, void *element)
{
CircularBuffer *buffer;
buffer = malloc(sizeof(*buffer));
if (buffer != NULL)
{
buffer->size = size;
buffer->start = 0;
buffer->count = 0;
buffer->elements = (void *)calloc(size, sizeof(???));
if (buffer->elements == NULL)
{
free(buffer);
buffer = NULL;
}
}
return buffer;
}
But I cannot figure out how to determine the size of an unknown type, which may be an int, a struct, or anything in between. Is what I am attempting to do even possible?
As you've found out, you can't automatically tell the size of an unknown piece of data. You'll need either a fixed element type (void* would be a good generic choice), or have the user pass in the size of each element:
CircularBuffer *cbInit(uint16 size, int elementSize)
{
...
buffer->elementSize = elementSize;
buffer->elements = calloc(size, elementSize);
}
Related
I'm a little confused about it.
The exercise is very long so I hope I wrote everything that's relevant for my question.
I have a given header file (part of it):
typedef void *(*copy_element)(const void *);
typedef void *(*free_element)(void **);
typedef struct group {
size_t group_size;
void **data;
copy_element copy_element_func;
free_element free_element_func;
} group;
group *group_alloc(copy_element copy_element_func, free_element free_element_func);
void group_free(group **p);
int add(group *group, const void *value);
I need to implement group.c as a generic struct.
My question is how can I implement add and alloc functions for **data?
With a known type I would use malloc and realloc with the size of the type, but here I'm not sure what to do.
group *group_alloc() {
group *p = malloc(sizeof(group))
if(p == NULL) {
//
}
p->group_size = 0;
void **ptr = malloc(sizeof(void*));
p->data = ptr;
return p;
}
In the exercise, Group should contain a dynamic array of values.
Thanks!
In fact, you should not care for the size nor the type of the elements, because the caller shall provide 2 functions that deal with copy and deallocation of those elements, so you can at the group functions level handle them as fully opaque pointers.
Here is a possible implementation. This code also contains a small demo showing how to handle null terminated strings:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef void *(*copy_element)(const void *);
typedef void *(*free_element)(void **);
typedef struct group {
size_t group_size;
void **data;
copy_element copy_element_func;
free_element free_element_func;
} group;
group *group_alloc(copy_element copy_element_func, free_element free_element_func);
void group_free(group **p);
int add(group *group, const void *value);
/***
Allocate a new group that will use the 2 provided functions.
The group will be initially empty
*/
group *group_alloc(copy_element copy_element_func, free_element free_element_func) {
group * g = malloc(sizeof(*g));
g->group_size = 0;
g->data = NULL;
g->copy_element_func = copy_element_func;
g->free_element_func = free_element_func;
return g;
}
/*********
* Add a new element to a group.
* Will use the copy_element_func member to build a copy of the element
* This implementation returns the number of elements in the group
*/
int add(group *group, const void *value) {
size_t sz = group->group_size + 1; // do not change anything on alloc error
void **data = realloc(group->data, sz * sizeof(void *));
if (data == NULL) { // allocation error
return 0;
}
// use copy_element_func to build a copy of the element
data[sz - 1] = group->copy_element_func(value);
group->group_size = sz;
group->data = data;
return (int) sz;
}
/******************
* Free a group.
* First free all elements of the group (using the free_element_func member)
* and then the group itself
*/
void group_free(group **p) {
group *g = *p;
if (g != NULL) {
for (int i = 0; i < g->group_size; i++) {
// again use free_element_func that should be able to free an element
g->free_element_func(g->data + i);
}
free(g);
}
*p = NULL;
}
// Example functions for null terminated strings
void * copy_string(const void *input) {
return strdup(input);
}
void * free_string(void **str) {
free(*str);
*str = NULL;
return *str;
}
// demo code
int main() {
group *g = group_alloc(©_string, &free_string);
int i = add(g, "foo");
printf("%d\n", i); // should display 1
i = add(g, "bar");
printf("%d\n", i); // should display 2
for (i = 0; i < g->group_size; i++) {
printf("%s\n", ((char **)g->data)[i]); // should display foo then bar
}
group_free(&g);
printf("%p\n", g); // should display a NULL pointer
return 0;
}
Disclaimer: this code blindly assumes the availability of the strdup function, while it is optional and does not test for allocation errors...
Based on your description, i think you are supposed to do the following:
group_size member holds the number of data entries in the group.
data member is an array of pointers that point to the objects that were added to the group.
With this combination of struct and function definitions, you can't do much other than adding pointers to objects to the data array in the group.
In the group_alloc function, you just allocate memory for the group object itself and initialize its members:
group *group_alloc(copy_element copy_element_func, free_element free_element_func){
group *ret = malloc(sizeof(*ret));
if(ret == NULL){
return(NULL);
}
ret->group_size = 0; //initially the group holds no pointers to objects
ret->data = NULL;
ret->free_element_func = free_element_func;
ret->copy_element_func = copy_element_func;
return(ret);
}
When an object is to be added, the add function is called.
The caller passes the group in which the object should be stored and a pointer to the object. You have to make space for another pointer in your data array:
int add(group *group, const void *value){
void **newData = realloc(group->data, (group->data_size + 1) * sizeof(*group->data)); //Grow the data array by one to store an additional pointer.
//realloc may return NULL, in that case an error occured, but you don't want to overwrite your existing data pointer
if(newData == NULL){
return(-1); //return -1 to indicate an error occured
}
group->data = newData;
group->data[group->data_size] = value; //store the pointer to the object in the last array field
group->data_size++; //increment data_size
return(0); //return 0 to indicate success
}
I am kind of questioning the usefulness of this construct since you can add pointers to arbitrary data to the group, zero information about what kind of data and wether or not it has to be free'd later is stored. You could add a bunch of pointers to static objects and a bunch of pointers to dynamically allocated objects and you would have no idea which of the objects have to be freed.
In this thread I was suggested to use max_align_t in order to get an address properly aligned for any type, I end up creating this implementation of a dynamic array:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stddef.h>
struct vector {
size_t capacity;
size_t typesize;
size_t size;
max_align_t data[];
};
#define VECTOR(v) ((struct vector *)((unsigned char *)v - offsetof(struct vector, data)))
static void *valloc(size_t typesize, size_t size)
{
struct vector *vector;
vector = calloc(1, sizeof(*vector) + typesize * size);
if (vector == NULL) {
return NULL;
}
vector->typesize = typesize;
vector->capacity = size;
vector->size = 0;
return vector->data;
}
static void vfree(void *data, void (*func)(void *))
{
struct vector *vector = VECTOR(data);
if (func != NULL) {
for (size_t iter = 0; iter < vector->size; iter++) {
func((unsigned char *)vector->data + vector->typesize * iter);
}
}
free(vector);
}
static void *vadd(void *data)
{
struct vector *vector = VECTOR(data);
struct vector *new;
size_t capacity;
if (vector->size >= vector->capacity) {
capacity = vector->capacity * 2;
new = realloc(vector, sizeof(*vector) + vector->typesize * capacity);
if (new == NULL) {
return NULL;
}
new->capacity = capacity;
new->size++;
return new->data;
}
vector->size++;
return vector->data;
}
static size_t vsize(void *data)
{
return VECTOR(data)->size;
}
static void vsort(void *data, int (*comp)(const void *, const void *))
{
struct vector *vector = VECTOR(data);
if (vector->size > 1) {
qsort(vector->data, vector->size, vector->typesize, comp);
}
}
static char *vgetline(FILE *file)
{
char *data = valloc(sizeof(char), 32);
size_t i = 0;
int c;
while (((c = fgetc(file)) != '\n') && (c != EOF)) {
data = vadd(data);
data[i++] = (char)c;
}
data = vadd(data);
data[i] = '\0';
return data;
}
struct data {
int key;
char *value;
};
static int comp_data(const void *pa, const void *pb)
{
const struct data *a = pa;
const struct data *b = pb;
return strcmp(a->value, b->value);
}
static void free_data(void *ptr)
{
struct data *data = ptr;
vfree(data->value, NULL);
}
int main(void)
{
struct data *data;
data = valloc(sizeof(struct data), 1);
if (data == NULL) {
perror("valloc");
exit(EXIT_FAILURE);
}
for (size_t i = 0; i < 5; i++) {
data = vadd(data);
if (data == NULL) {
perror("vadd");
exit(EXIT_FAILURE);
}
data[i].value = vgetline(stdin);
data[i].key = (int)vsize(data[i].value);
}
vsort(data, comp_data);
for (size_t i = 0; i < vsize(data); i++) {
printf("%d %s\n", data[i].key, data[i].value);
}
vfree(data, free_data);
return 0;
}
But I'm not sure if I can use max_align_t to store a chunk of bytes:
struct vector {
size_t capacity;
size_t typesize;
size_t size;
max_align_t data[]; // Used to store any array,
// for example an array of 127 chars
};
Does it break the one past the last element of an array rule?
Does it break the one past the last element of an array rule?
No.
Using max_align_t to store a chunk of bytes
OP's issue is not special because it uses a flexible array member.
As a special case, the last element of a structure ... have an incomplete array type; this is called a flexible array member. ... However, when a . (or ->) operator has a left operand that is (a pointer to) a structure with a flexible array member and the right operand names that member, it behaves as if that member were replaced with the longest array (with the same element type) ...
It is the same issue as accessing any allocated memory or array of one type as if it was another type.
The conversion from max_align_t * to char * to void * is well defined when alignment is done right.
A pointer to an object type may be converted to a pointer to a different object type. If the resulting pointer is not correctly aligned for the referenced type, the behavior is undefined. C11dr ยง6.3.2.3 7
All reviewed accessing in code do not attempt to access outside the "as if" array.
I have to create a generic array that can contain generic data structures.
How can i put a generic structure into an empty slot of my void array?
This is my code.
struct CircularBuffer {
int E;
int S;
int length; // total number of item allowable in the buffer
int sizeOfType; // size of each element in the buffer
void *buffer;
};
struct CircularBuffer* circularBufferInit(int length, int sizeOfType) {
struct CircularBuffer *cb = malloc(sizeof(struct CircularBuffer));
cb->E = 0;
cb->S = 0;
cb->length = length;
cb->sizeOfType = sizeOfType;
cb->buffer = malloc(sizeOfType *length);
return cb;
}
int circularBufferIsEmpty(struct CircularBuffer* cb) {
if (cb->S == cb->E)
return 1; //empty
else
return 0;
}
int circularBufferIsFull(struct CircularBuffer *cb) {
int nE = (cb->E + 1) % (cb->length);
if (nE == cb->S)
return 1; //full
else
return 0;
}
void circularBufferAdd(struct CircularBuffer *cb, void* obj) {
memcpy(cb->buffer + cb->E, obj, cb->sizeOfType);
}
[...]
memcpy is the problem...
It seems that essentially you're trying to figure out how to offset a void * to the address of an array element of known size but unknown type so you can pass it to memcpy().
It looks as though, in circularBufferAdd(), cb->E gives the index of the element you want to copy to, cb->buffer is the void * to the array, obj is the item to be copied, and cb->sizeOfType is the size each array element (and obj). In that case you can change:
memcpy(cb->buffer + cb->E, obj, cb->sizeOfType);
to:
memcpy((char *)cb->buffer + (cb->E * cb->sizeOfType), obj, cb->sizeOfType);
Since you can't use pointer arithmetic with a void *, you'd cast it to char *. Then, you can multiply the element index by the element size to get the offset of the element in bytes, and use that to get the address of the element you need.
I have the following pointer to structure
struct ALIST
{
short sPeriod;
long lDate;
}*list_ptr;
list_ptr = malloc(sizeof(*list_ptr));
Now if I have a global variable sIndex which I initialize to zero, is it possible to do this?
(list_ptr + sIndex)->sPeriod = period_variable;
(list_ptr + sIndex)->lDate = date_variable;
sIndex++
Is there a more efficient method?
This looks like you want to allocate a dynamic array. Make a size variable and set it to your starting size for the array.
Something like:
size_t list_size = 10;
struct ALIST list_ptr = 0;
size_t i;
list_ptr = malloc(sizeof(*list_ptr) * list_size);
for(i=0; i<list_size; ++i) {
list_ptr[i].sPeriod = period;
list_ptr[i].lDate = date;
}
Now, if you don't know the size of the array then what you want ends up looking a lot like a C++ std::vector.
I'd build a C version that wraps the necessary information in a struct. Use realloc to resize it.
It might look like (NOTE THAT THIS IS COMPLETELY UNTESTED):
struct dynamic_ALIST {
struct ALIST *array;
size_t size;
size_t capacity;
};
void dynamic_ALIST_construct(struct dynamic_ALIST *x, size_t initial_size)
{
x->array = 0;
x->size = 0;
x->capacity = 0;
dynamic_ALIST_reserve(x, initial_size);
}
void dynamic_ALIST_reserve(struct dynamic_ALIST *x, size_t size)
{
struct ALIST *tmp = realloc(x->array, sizeof(*tmp) * size);
if(!tmp) abort();
x->array = tmp;
x->capacity = size;
}
struct ALIST* dynamic_ALIST_get(struct dynamic_ALIST *x, size_t offset)
{
if(offset < x->size) {
return x->array + offset;
}
if(offset < x->capacity) {
x->size = offset + 1;
return x->array + offset;
}
dynamic_ALIST_reserve(x, offset+1);
return dynamic_ALIST_get(x, offset);
}
Then you could use it like:
void f()
{
size_t item_index = 0;
struct dynamic_ALIST list;
FILE *fp = fopen("filename");
dynamic_ALIST_construct(list, 0);
while( read_item(fp, dynamic_ALIST_get(list,item_index)) ) {
item_index++;
}
fclose(fp);
}
You can make all kinds of changes to that. The get function might return an error instead of automatically creating new entries. You might make another function that increases the size. You might want to have a function that sets all the values to zero before returning new memory.
If you have a lot of different structs to wrap up you can put ALL of the above dynamic_ALIST struct, and construct, reserve, get functions into a macro. If you do it right then you just say:
NEW_DYNAMIC_STRUCT(ALIST);
And the preprocessor spits out a whole new copy with all the names changed.
I'll answer point by point:
Do those pointer manipulations only if you know what you are doing.
Assuming sIndex to be an int, with sIndex=0;, it is no problem but if you increment sIndex, you don't have that space allocated to use becuase you have malloc'd just one block.
You need to first do your allocation appropriately if you need to access multiple such blocks then:
list_ptr = malloc(sizeof(struct ALIST)*N); //replace N with the number of blocks you want
I'm a beginner in C and programming. I would like to ask some questions on dynamic array and pointer in C.
I am trying to create a dynamic array and increase its capacity, but I can't get my code working. I believe something is wrong in my setCapacityDynArr function.
Can someone give me some help?
Thanks!
struct DynArr {
TYPE *data; /* pointer to the data array */
int size; /* Number of elements in the array */
int capacity; /* capacity ofthe array */
};
void initDynArr(struct DynArr *v, int capacity) {
v->data = malloc(sizeof(TYPE) * capacity);
assert(v->data != 0);
v->size = 0;
v->capacity = capacity;
}
void freeDynArr(struct DynArr *v) {
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;
}
int sizeDynArr(struct DynArr *v) {
return v->size;
}
void addDynArr(struct DynArr *v, TYPE val) {
/* Check to see if a resize is necessary */
if (v->size >= v->capacity) {
_setCapacityDynArr(v, 2 * v->capacity);
}
v->data[v->size] = val;
v->size++;
}
void _setCapacityDynArr(struct DynArr *v, int newCap) {
//create a new array
struct DynArr *new_v;
assert(newCap > 0);
new_v = malloc(newCap * sizeof(struct DynArr));
assert(new_v != 0);
initDynArr(new_v, newCap);
//copy old values into the new array
for (int i = 0; i < new_v->capacity; i++) {
new_v->data[i] = v->data[i];
}
//free the old memory
freeDynArr(v);
//pointer is changed to reference the new array
v = new_v;
}
int main(int argc, const char * argv[]) {
//Initialize an array
struct DynArr myArray;
initDynArr(&myArray, 5);
printf("size = 0, return: %d\n", myArray.size);
printf("capacity = 5, return: %d\n", myArray.capacity);
//Add value to the array
addDynArr(&myArray, 10);
addDynArr(&myArray, 11);
addDynArr(&myArray, 12);
addDynArr(&myArray, 13);
addDynArr(&myArray, 14);
addDynArr(&myArray, 15);
for (int i = 0; i < myArray.size; i++) {
printf("myArray value - return: %d\n", myArray.data[i]);
}
return 0;
}
//pointer is changed to reference the new array
v = new_v;
This is your problem, a classic mistake in C. In fact the function changes its own copy of the pointer, the caller never sees the change. The problem is amply described by this C FAQ.
I suggest a different approach. There's no reason to make a new v: you simply want more storage associated with it. So instead of actually changing v, you'll probably want to just call realloc on the storage: v->DATA.
You might get away with something like:
tmp = realloc(v->data, newCap * sizeof *v->data);
if (!tmp)
error;
v->data = tmp;
And this way you don't need to copy the elements either: realloc takes care of that.
//pointer is changed to reference the new array
v = new_v;
Your original pointer outside the function is not changed, since you passed the value of the pointer not the address of it here:
void _setCapacityDynArr(struct DynArr *v, int newCap)
{
Yes it's an error in _setCapacityDynArr. It's an error because you declare an DynArr structure on the stack, then you try to free it and assign a new pointer to it. That will not work, as items allocated on the stack can't be freed.
What you want to do is to reallocate only the actual data, not the whole structure. For this you should use the realloc function.
There are other problems with the function as well, like you assigning to the pointer. This pointer is a local variable so when the function returns all changes to it will be lost.