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.
Related
i am fairly new to c and struggling to properly use the C stdlib qsort() function.
This is relevant to education and as such i am only allowed to use C99 and standard libraries if this is important.
I have a list of items taken from a HashTable and put into a HashItem **array but then when sorting this i am struggling with the compare function, i cannot get the correct value out of the struct. I have looked around and seen a few solutions but they all seem to lead to a
[Error] dereferencing pointer to incomplete type
Here is the struct :
typedef struct {
char *word;
int occurences;
} HashItem;
And i am interested in comparing and sorting by the occurences value.
Here is the bit of code which calls the qsort:
int n = array->number_of_values;
HashItem **standard_array = array_getarray(array);
qsort(standard_array, n, sizeof(HashItem*), compare_func);
Here is the compare function:
int compare_func(const void *a, const void *b){
const struct HashItem* aa = (HashItem*)a;
const struct HashItem* bb = (HashItem*)b;
int val_1 = aa->occurencies;
int val_2 = bb->occurencies;
if(val_1 == val_2){
return 0;
}else if(val_1 > val_2){
return 1;
}else{
return -1;
}
}
Sorry for the formatting, i am new to asking questions here.
I hope you can help thankyou.
Array code :
/*DynArray is a dynamically resizing array that is used to hold values and retain size data throughout*/
typedef struct{
int number_of_values;
int capacity;
HashItem **items;
}DynArray;
/*Method to create a new dynamic array and return it */
DynArray* array_new(int file_size){
DynArray *array = malloc(sizeof(DynArray));
array->number_of_values = 0;
array->capacity = file_size / 10;
printf("capacity is %d " , array->capacity);
array->items = malloc(sizeof(HashItem*)* array->capacity);
}
/*Method used to increase the size of the array and reallocate memory*/
void array_increase_if_full(DynArray *array){
if (array->number_of_values >= array->capacity){
array->capacity *= 1.25;
array->items = realloc(array->items, sizeof(HashItem)*array->capacity);
}
}
/*Method to add a string to the dynamic array specified */
void array_append(DynArray *array, HashItem *item){
array_increase_if_full(array);
array->items[array->number_of_values] = item;
//printf("item %s added \n at position %d ", array->items[array->number_of_values]->word, array->number_of_values);
array->number_of_values++;
}
/*Method used to get value at specified position for given array*/
HashItem *array_get(DynArray *array, int position){
if(position >= array->number_of_values || position <0){
printf("Index specified out of range");
exit(1);
}
//printf("item %s at position %d retrieved", array->items[position]->word, position);
return array->items[position];
}
HashItem **array_getarray(DynArray *array){
HashItem **toreturn[array->number_of_values];
int i;
for(i = 0; i < array->number_of_values; i++){
toreturn[i] = array_get(array, i);
}
return toreturn;
}
Printing the array from the main gives the correct unsorted values of word:occurences
Edit:
Thanks to everyone that took their time to help, it is now in a working state with Michaels suggestion, i no longer use the array_getarray() method and instead use:
int n = array->number_of_values;
int i;
HashItem **standard_array = malloc(n*sizeof(HashItem*));
for(i = 0; i < n; i++){
standard_array[i] = array_get(array, i);
printf("%s : %d \n" , standard_array[i]->word, standard_array[i]->occurences);
}
You structure declaration:
typedef struct {
char *word;
int occurences;
} HashItem;
declares a typedef name for an anonymous struct. There is a HashItem type that's a structure, but there is no struct HashItem type.
So when your compare_func() has the following declarations:
const struct HashItem* aa = (HashItem*)a;
const struct HashItem* bb = (HashItem*)b;
those struct HashItem* variables are pointers to a forward declared struct HashItem that has nothign to do with the HashItem strucuture above.
Just change those variable declarations to:
const HashItem* aa = (HashItem*)a;
const HashItem* bb = (HashItem*)b;
and/or change the declaration of the structure to:
typedef struct HashItem {
char *word;
int occurences;
} HashItem;
However, there's another issue (as mentioned in other answers): you are apparently sorting an array of pointers to HashItem objects, but your compare_function() is being written as if you're sorting an array of the objects (not pointers).
To address this:
int compare_func(const void *a, const void *b)
{
// get HashItem*'s from the HashItem**'s
const HashItem* aa = *((HashItem**)a);
const HashItem* bb = *((HashItem**)b);
int val_1 = aa->occurencies;
int val_2 = bb->occurencies;
if (val_1 == val_2) {
return 0;
} else if (val_1 > val_2) {
return 1;
} else {
return -1;
}
}
Finally (for now anyway), this function is returning the address to a local array, so the data it points to is no longer valid:
HashItem **array_getarray(DynArray *array){
HashItem **toreturn[array->number_of_values];
int i;
for(i = 0; i < array->number_of_values; i++){
toreturn[i] = array_get(array, i);
}
return toreturn;
}
I think you'll need to allocate the array you're retuning using malloc() or calloc() or something. But what I really think you need to do is step back and create some drawing of your data structures and think about the lifetime of the various objects contained in them and how those lifetimes can be tracked an managed so that you don't have leaks, double frees, or pointer dereferences to no longer valid objects.
Change qsort(standard_array, n, sizeof(HashItem), compare_func); to
qsort(standard_array, n, sizeof(HashItem*), compare_func);
In function void qsort (void* base, size_t num, size_t size, int (*compar)(const void*,const void*));
the third parameter size_t size stands for:
Size in bytes of each element in the array.
It now looks to me like your problems are all springing from the first definition.
/*DynArray is a dynamically resizing array that is used to hold values and retain size data throughout*/
typedef struct{
int number_of_values;
int capacity;
HashItem **items;
}DynArray;
I see no reason for items to be a double-pointer. The comment says it should contain values, but a double-pointer pointing to an array would contain pointers, not the ultimate values. I think this initial misstep is causing you to trip everywhere else. Change it to
...
HashItem *items;
...
and the rest should flow more naturally.
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.
I'm currently attempting to use the built-in quicksort provided by C in order to sort an array of pointers to structs. I want to sort each element based on a name element within the struct.
Although my debug output of the entire array each time through the comparison function shows me that the function is indeed shifting elements, the end result is not the correct sorted order. Is there something I'm just not seeing here?
typedef struct // The custom data type.
{
char *name;
} Person;
----------------------------
Person **people; // A dynamically allocated array of Person pointers.
int numPeople; // The logical index of people.
int maxPeople; // The current maximum capacity of people.
int compare(const void *a, const void *b) // The comparison function for determining
{ // alphabetic ordering.
const Person *const *p1 = a;
const Person *const *p2 = b;
return strcmp((*p1)->name, (*p2)->name); // Compare alphabetically, return result.
}
void SomeFunction(void)
{
qsort(people, numPeople, sizeof(Person *), compare); // Perform the sort.
}
Thanks for help with this.
I have tested your code and it looks working OK. Here is the code I compiled with gcc 4.5.2:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct // The custom data type.
{
char *name;
} Person;
Person **people; // A dynamically allocated array of Person pointers.
int numPeople; // The logical index of people.
int maxPeople; // The current maximum capacity of people.
int compare(const void *a, const void *b) // The comparison function for determining
{ // alphabetic ordering.
const Person *const *p1 = a;
const Person *const *p2 = b;
return strcmp((*p1)->name, (*p2)->name); // Compare alphabetically, return result.
}
void SomeFunction(void)
{
qsort(people, numPeople, sizeof(Person *), compare); // Perform the sort.
}
int main()
{
int iCnt;
maxPeople = 4;
numPeople = 4;
people = calloc(1, sizeof(Person *) * maxPeople);
people[0] = calloc(1, sizeof(Person));
people[1] = calloc(1, sizeof(Person));
people[2] = calloc(1, sizeof(Person));
people[3] = calloc(1, sizeof(Person));
people[0]->name = strdup("Tanya");
people[1]->name = strdup("Alfred");
people[2]->name = strdup("Harry");
people[3]->name = strdup("Oakley");
for(iCnt = 0; iCnt < numPeople; iCnt ++)
printf("[%d] %s\n", iCnt, people[iCnt]->name);
SomeFunction();
for(iCnt = 0; iCnt < numPeople; iCnt ++)
printf("[%d] %s\n", iCnt, people[iCnt]->name);
return 0;
}
The code looks legit and I'm not sure what's wrong. Could you try compiling the code I tested and see if it works?
Can you try with this
int compare(const void *a, const void *b) // The comparison function for determining
{ // alphabetic ordering.
const Person *p1 = *(const Person**)a;
const Person *p2 = *(const Person**)b;
return strcmp((p1)->name, (p2)->name); // Compare alphabetically, return result.
}
So I have implemented a generic stack in Plain C. It should copy different type of data, inclusive structures. And by structures I have the problem.
So here's the structure of the stack:
/*
* Definite genStack as a structure.
* Pointer elems points to the objects lying on the stack
* The variable elemSize spiecifies the size of an element
* The variable logLength specifies the number of actually
* lying on the stack objects
* The variable allocLenght specifies the allocated size
*/
typedef struct{
void* elems;
int elemSize;
int logLength;
int allocLength;
}genStack;
Push and pop functions:
void GenStackPush(genStack *s, const void *elemAddr)
{
/* if stack is full - allocates more memory */
if (GenStackFull(s))
{
GenStackAlloc(s, s->elemSize);
}
memcpy((char*) (s->elems)+(s->logLength), elemAddr, sizeof(*elemAddr));
s->logLength++;
}
void GenStackPop(genStack *s, void *elemAddr)
{
if(GenStackEmpty(s))
{
fprintf(stderr, "Can't pop element from stack: stack is empty.\n");
} else
{
s->logLength--;
memcpy((void*) elemAddr, (s->elems)+(s->logLength), sizeof(s->elems[s->logLength]));
}
}
Simple structures test:
gentest.h:
#ifndef GENTEST1_H
#define GENTEST1_H
typedef struct {
char* name;
int age;
char gender;
}person;
#endif
gentest.c:
#include <stdio.h>
#include <stdlib.h>
#include "gentest1.h"
#include "genstacklib.h"
int main(int argc, char* argv[])
{
genStack StructStack;
person testPerson[5];
person* newPerson;
person* test;
int i;
newPerson = (void*) malloc (sizeof(person));
testPerson[0].name = "Alex";
testPerson[0].age = 21;
testPerson[0].gender = 'm';
testPerson[1].name = "Vanja";
testPerson[1].age = 20;
testPerson[1].gender = 'm';
testPerson[2].name = "sjrgsde";
testPerson[2].age = 11;
testPerson[2].gender = 'w';
testPerson[3].name = "wergsggsd";
testPerson[3].age = 99;
testPerson[3].gender = 'y';
testPerson[4].name = "adaasxx";
testPerson[4].age = 13;
testPerson[4].gender = 'g';
GenStackNew(&StructStack, sizeof(person));
printf("sizeof(person) = %lu\n", sizeof(person));
for (i = 0; i < 5; i++) {
newPerson = &testPerson[i];
GenStackPush(&StructStack, newPerson);
printf("Pushed: %s, %d, %c\n", newPerson->name, newPerson->age, newPerson->gender);
}
test = (void*) malloc (sizeof(person));
test->name = "test";
test->age = 0;
test->gender = 't';
while(!GenStackEmpty(&StructStack))
{
GenStackPop(&StructStack, test);
printf("Popped: %s, %d, %c\n", test->name, test->age, test->gender);
}
GenStackDispose(&StructStack);
return 0;
}
And here's the output I get:
./gentest1
elemSize = 16 GenStackInitialAlocationSize = 4
sizeof(person) = 16
Pushed: Alex, 21, m
Pushed: Vanja, 20, m
Pushed: sjrgsde, 11, w
Pushed: wergsggsd, 99, y
New size of alloc = 8
Pushed: adaasxx, 13, g
Popped: adaasxx, 0, t
Popped: wergsggsd, 0, t
Popped: sjrgsde, 0, t
Popped: Vanja, 0, t
Popped: Alex, 0, t
As you can see, I can receive names, but no age or gender. I've tried a lot of options, but still getting Segmentation Fault or the output from above. For moment, the output above is the finest output I get, but still not what I want.
The question is - how can I get the output I need?
Thanks in advance.
To avoid some questions:
sizeof(person) = s->elemSize
It is defined by creating the stack:
genstacklib.c:
void GenStackNew(genStack *s, int elemSize)
{
void* newElems;
/* Allocate a new array to hold the contents. */
newElems = (void*) malloc(elemSize * GenStackInitialAlocationSize);
printf("elemSize = %d\tGenStackInitialAlocationSize = %d\n",
elemSize, GenStackInitialAlocationSize);
if (newElems == NULL)
{
fprintf(stderr, "Error with allocating the stack.\n");
exit(1); /* Exit, returning error code. */
}
s->elems = newElems;
s->elemSize = elemSize;
s->allocLength = GenStackInitialAlocationSize;
s->logLength = 0; /*is empty*/
}
gentest.c:
GenStackNew(&StructStack, sizeof(person));
printf("sizeof(person) = %lu\n", sizeof(person));
your push function is copying sizeof(*elemAddr) and that is a void *, so it has the size of a pointer not the inteded size of a person struct. So you are probably copying only the first 4 bytes
As stated above the push is copying the wrong size of data. It should be elemSize.
The memcpy is also overwriting its own data. Something like this should work.
memcpy((char*) (s->elems)+(s->logLength)*elemSize, elemAddr, elemSize);
s->logLength++;
You're not using elemSize in all the relevant places...
void GenStackPush(genStack *s, const void *elemAddr)
{
...
memcpy((char*) (s->elems)+(s->logLength), elemAddr, sizeof(*elemAddr));
^^^^^^^^^^^^^^^^^
This is very wrong; the type of the expression *elemAddr is void, which is a constraint violation (sizeof may not be called on an expression of incomplete type, and void is an incomplete type). You will want to turn up the warning level on your compiler. I wrote a test program to compute sizeof on expressions of type void * and void, and I get a warning with gcc -pedantic. If I drop the -pedantic I don't get a warning, but the result I get for sizeof (void) is 1, which I'm pretty certain is not the size of a person. Why aren't you using s->elemSize here?
Secondly, why are you casting s->elems to char *?
EDIT
If I may offer some advice, I've womped up a few generic containers in the past, and here are the lessons I've come away with:
First, delegate all type-aware operations (allocation, deallocation, copy, compare, display, etc.) to separate functions, which are called via function pointers passed as parameters to the generic container's functions; i.e., a push would be defined like
GenStackPush(genStack *stack, const void *data, void *(*copy)(const void *))
{
stack->elems[++stack->logLength] = copy(data);
}
...
void *myIntCopyFunc(const void *data)
{
const int *inputData = (const int *) data;
int *copy = malloc(sizeof *copy);
if (copy)
*copy = *inputData;
return copy;
}
...
GenStackPush(&myIntStack, &intVal, myIntCopyFunc);
One issue you have with your person type is that you're not doing a deep copy of the name member; you're just copying a pointer value to the stack. In this case it's not a big deal since you're working with string literals, but if you were using, say, a local char [], you'd have problems. By writing a separate copy function for each type, you can deal with those sorts of issues, instead of trying to do a one-size-fits-all allocation in the container function itself.
Secondly, don't call your generic container functions directly; put a type-aware interface between you and the container (basically, the poor man's version of function overloading):
void pushInt(GenStack *stack, int intVal)
{
GenStackPush(stack, &intVal, myIntCopyFunc);
}
...
genStack myIntStack;
...
pushInt(&myIntStack, 5);
This gives you two benefits; first, it allows you to pass literal values as parameters (which you can't do with parameters of type void *). Secondly, it gives you a way to enforce type safety on your container. You can't accidentally push a value of the wrong type this way.
Is this a lot of extra work? Oh my yes. There's a lot of magic that has to happen under the hood for generic container types to work properly. If you're trying to replicate the same kind of functionality that you get with the C++ std::stack container type, you're going to be writing a lot of code.
How does one malloc an array of structs correctly if each struct contains an array of strings which vary in size?
So each struct might have a different size and would make it impossible to
realloc(numberOfStructs * sizeof(structName))
after
malloc(initialSize * sizeof(structName)
How does one allocate memory for this and keep track of what is going on?
If your structure has a char *, it takes up the size of one pointer. If it has a char[200], it takes up two hundred bytes.
I am making some guesses here, based on the information you have provided. The only reason I can see for wanting to realloc an array of structs is if you want to add more structs to that array. That's cool. There are plenty of reasons to want that kind of dynamic storage. The best way to handle it, especially if the structures are themselves dynamic, is to keep an array of pointers to these structures. Example:
1. Data structure:
typedef struct {
int numberOfStrings;
char ** strings;
}
stringHolder;
typedef struct {
int numberOfStructs;
stringHolder ** structs;
}
structList;
2. Managing dynamic arrays of strings:
void createNewStringHolder(stringHolder ** holder) {
(*holder) = malloc(sizeof(stringHolder));
(*holder)->numberOfStrings = 0;
(*holder)->strings = NULL;
}
void destroyStringHolder(stringHolder ** holder) {
// first, free each individual string
int stringIndex;
for (stringIndex = 0; stringIndex < (*holder)->numberOfStrings; stringIndex++)
{ free((*holder)->strings[stringIndex]); }
// next, free the strings[] array
free((*holder)->strings);
// finally, free the holder itself
free((*holder));
}
void addStringToHolder(stringHolder * holder, const char * string) {
int newStringCount = holder->numberOfStrings + 1;
char ** newStrings = realloc(holder->strings, newStringCount * sizeof(char *));
if (newStrings != NULL) {
holder->numberOfStrings = newStringCount;
holder->strings = newStrings;
newStrings[newStringCount - 1] = malloc((strlen(string) + 1) * sizeof(char));
strcpy(newStrings[newStringCount - 1], string);
}
}
3. Managing a dynamic array of structures:
void createNewStructList(structList ** list, int initialSize) {
// create a new list
(*list) = malloc(sizeof(structList));
// create a new list of struct pointers
(*list)->numberOfStructs = initialSize;
(*list)->structs = malloc(initialSize * sizeof(stringHolder *));
// initialize new structs
int structIndex;
for (structIndex = 0; structIndex < initialSize; structIndex++)
{ createNewStringHolder(&((*list)->structs[structIndex])); }
}
void destroyStructList(structList ** list) {
// destroy each struct in the list
int structIndex;
for (structIndex = 0; structIndex < (*list)->numberOfStructs; structIndex++)
{ destroyStringHolder(&((*list)->structs[structIndex])); }
// destroy the list itself
free((*list));
}
stringHolder * addNewStructToList(structList * list) {
int newStructCount = list->numberOfStructs + 1;
size_t newSize = newStructCount * sizeof(stringHolder *);
stringHolder ** newList = realloc(list->structs, newSize);
if (newList != NULL) {
list->numberOfStructs = newStructCount;
list->structs = newList;
createNewStringHolder(&(newList[newStructCount - 1]));
return newList[newStructCount - 1];
}
return NULL;
}
4. Main program:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main (int argc, char * argv[]) {
structList * allHolders;
createNewStructList(&allHolders, 10);
addStringToHolder(allHolders->structs[4], "The wind took it");
addStringToHolder(allHolders->structs[4], "Am I not merciful?");
addStringToHolder(allHolders->structs[7], "Aziz, Light!");
printf("%s\n", allHolders->structs[4]->strings[0]); // The wind took it
printf("%s\n", allHolders->structs[4]->strings[1]); // Am I not merciful?
printf("%s\n", allHolders->structs[7]->strings[0]); // Aziz, Light!
stringHolder * newHolder = addNewStructToList(allHolders);
addStringToHolder(newHolder, "You shall not pass!");
printf("%s\n", newHolder->strings[0]); // You shall not pass!
printf("%s\n", allHolders->structs[10]->strings[0]); // You shall not pass!
destroyStructList(&allHolders);
return 0;
}
You don't, generally. There are two reasons you might want to do this:
So that a single free() will release the entire block of memory.
To avoid internal memory fragmentation.
But unless you have an exceptional situation, neither are very compelling, because there is crippling drawback to this approach:
If you do this, then block[i] is meaningless. You have not allocated an array. There is no way to tell where your next struct starts without either examining the struct or having outside information about the size/position of your structs in the block.
It is not so clear how your struct type is declared. C99 has a special construct for such things, called flexible array member of a struct:
As a special case, the last element of
a structure with more than one named
member may have an incomplete array
type; this is called a flexible array
member.
You could do something like
typedef struct myString myString;
struct myString { size_t len; char c[]; };
You may then allocate such a beast with
size_t x = 35;
myString* s = malloc(sizeof(myString) + x);
s->len = x;
and reallocate it with
size_t y = 350;
{
myString* tmp = realloc(s, sizeof(myString) + y);
if (!tmp) abort(); // or whatever
tmp->len = y;
}
s = tmp;
To use this more comfortably you'd probably better wrap this into macros or inline functions.