I am having some trouble using the malloc and realloc functions in C and saving values in them using 2 seperate functions
Note i have only recently just started using c and porbably there are multiple bugs in the code but my main focus is saving the values to the struct
i need to be able to save multiple values to a single array.
these are my functions
int ArrayCreate() {
int *pt;
pt = (ASet *)calloc(1,sizeof(ASet));
pt == NULL;
return *pt;
}
//possible 2nd *
void ArrayAdd(ASet **arrayStruct, int x) {
ASet *pt = NULL;
*arrayStruct=realloc(*arrayStruct,1*sizeof(ASet));
pt = arrayStruct;
*arrayStruct->value = x;
free(pt);
}
and in my main i have
ASet *arr_pt = NULL;
to create an array
printf("a new array is created \n");
arr_pt = ArrayCreate();
break;
to add something to an array
printf("Enter integer \n");
scanf("%d",&x);
ArrayAdd(arr_pt,x);
while (getchar() != '\n') {
continue;
}
break;
also to test i have added this but i am not sure about it
void ArrayShow(ASet *arrayStruct) {
ASet *pt = arrayStruct;
//printf("%d \n", pt[0].value);
printf("%d \n", pt[1].value);
printf("%d \n", pt[2].value);
printf("%d \n", pt[3].value);
printf("%d \n", pt[4].value);
}
i only need to know how to save correctly using realloc and calloc but for some reason the pt[0] is ignored and anything after p[1] is garbage
simple sample
#include <stdio.h>
#include <stdlib.h>
typedef int Item;
#define PRN_Item "%d"
typedef struct da {
size_t size;
Item *value;
} ASet;
ASet *ArrayCreate(void){
return calloc(1, sizeof(ASet));
}
void ArrayAdd(ASet *arrayStruct, Item x){
ASet *ap = arrayStruct;
Item *p = ap->value;
p = realloc(p, (ap->size + 1)*sizeof(Item));
if(p){
ap->value = p;
ap->value[ap->size] = x;
ap->size += 1;
} else {
perror("realloc");
exit(1);
}
}
void ArrayShow(ASet *arrayStruct) {
Item *v = arrayStruct->value;
size_t n = arrayStruct->size;
while(n--){
printf(PRN_Item " \n", *v++);
}
}
void ArrayDrop(ASet *arrayStruct){
free(arrayStruct->value);
free(arrayStruct);
}
int main(){
ASet *arr_pt = ArrayCreate();
if(!arr_pt){
perror("ArrayCreate");
exit(1);
}
printf("a new array is created \n");
while(1){
int x;
printf("Enter integer \n");
if(1!=scanf("%d", &x))
break;
ArrayAdd(arr_pt, x);
}
ArrayShow(arr_pt);
ArrayDrop(arr_pt);
return 0;
}
EDIT:
The line below is a comment so it's not executed:
//printf("%d \n", pt[0].value);
And your casting to pt with the wrong type that's why you're getting garbage
/* replace Aset with int and SIZE must be a defined size */
pt= calloc(SIZE, sizeof(int));
pt == NULL is a comparaison expression not an assingment statment:
pt = NULL // assining NULL to pt
ArrayCreate must return a pointer to the fisrt element of the array, not the value of the address pointed to:
int *ArrayCreate() {
int *pt;
pt = calloc(SIZE,sizeof(int));
pt == NULL;
return pt;
}
there are a few things wrong with your code here are some:
instead of pt == NULL; you should do pt = NULL because using the first compares between pt and NULL and doesn't assign NULL to pt
this pt = arrayStruct is illegal because pt is of type ASet while arrayStruct is of type **ASet you should replace it with pt = *arrayStruct
here *arrayStruct->value = x; it's like you're asking for the address of the member value to be changed to x you should instead do *(arrayStruct)->value = x;
Problems:
There's no visible record of how big the array is, so you can't tell what size to reallocate.
Your ArrayCreate() function should return a pointer to the structure. The pt == NULL; statement does nothing; your compiler should be warning you about a statement with no effect. Because you are returning an int and not a pointer, you are leaking memory.
Your ArrayAdd() function doesn't know how much space to allocate (point 1 again). The code should not be using free(pt); realloc() may have returned a pointer to the same location or to a different location, but either way, it has dealt with the old memory.
I did not understand the 1, 2; so you're saying pt should be *pt? What effect will that have and 3 the part about how much space?
Consider the ArrayCreate() function — as currently written:
int ArrayCreate() {
int *pt;
pt = (ASet *)calloc(1,sizeof(ASet));
pt == NULL;
return *pt;
}
First of all, the code is confused about whether it is dealing with int or ASet types. It appears to be allocating an array with 1 (zeroed) ASet element but assigning that to an int *. The next line should probably be an if statement. The code should probably read:
ASet *ArrayCreate(void)
{
ASet *pt = (ASet *)calloc(1, sizeof(ASet));
if (pt == NULL)
{
fprintf(stderr, "Memory allocation failed!\n");
exit(1);
}
return pt;
}
I don't object to the cast on the return from calloc() as long as you reassure me that you always compile with options such that if you forget to include <stdlib.h>, your code will not compile. For example, I mainly use GCC and always compile with options such as:
gcc -O3 -g -std=c11 -Wall -Wextra -Wmissing-prototypes -Wstrict-prototypes -Wold-style-definition -Werror ...
If you don't compile like that, then you're better off omitting the cast. As you'll notice, there are others who are not as flexible about the issue of casting the return from malloc() et al. (I even use those options on other people's code on SO; it is sometimes painful getting their code to compile cleanly.)
The error handling or memory allocation is about the simplest available option — report the error (on standard error, be it noted) and exit. You can be as creative as you like about how to handle the error, but failing fast has many merits in many situations.
Now, the code I've shown is more or less coherent, but the only way you know that there's one element in the array of ASet values is ... because it was just created with one element.
When you call ArrayAdd() the first time, you can assume that there was one element in the array, but when you call it another time, you have no idea how many rows are in the array, so you'll still allocate 2 elements to the array, even though that's a no-op. You need to know, somehow, how many elements are already in the array.
As currently written:
void ArrayAdd(ASet **arrayStruct, int x) {
ASet *pt = NULL;
*arrayStruct=realloc(*arrayStruct,1*sizeof(ASet));
pt = arrayStruct;
*arrayStruct->value = x;
free(pt);
}
You take the (pointer to the) array that you're passed, and resize the array to one element. The pt = arrayStruct; line should generate compilation warnings; you're assigning an ASet ** to an ASet *; they're very different types. The free(pt) is simply wrong — irredeemably wrong. You need code something like:
void ArrayAdd(ASet **arrayStruct, size_t new_size, int x)
{
ASet *pt = realloc(*arrayStruct, new_size * sizeof(ASet));
if (pt == 0)
{
fprintf(stderr, "Memory allocation failed!\n");
exit(1);
}
pt[new_size-1].value = x;
*arrayStruct = pt;
}
Again the code fails fast on memory allocation failure. It is careful not to clobber *arrayStruct before it knows that it is safe to do so. It assigns x to the value member of the last element of the array. Note that realloc() does not zero the extra memory it makes available. The code also does not know how big the array was before; it can't tell whether the array is growing or shrinking — only the calling code is privy to that information.
So, this isn't a very good bit of code, but it is better than what was there before.
Related
I'm fairly new to pointers, and void pointers is still black art to me.
In the following code I get a segfault when tmp->item = strdup(item);
I'm not sure how to fix.
int springmap_add(SpringMap *sm, char *key, void *item) {
SpringMap *tmp = sm;
.
.
.
while (tmp) {
if (!tmp->next) {
tmp->next = springmap_init();
tmp->next->key = strdup(key);
tmp->next->item = strdup(item); // Segfault here
return 1;
}
tmp = tmp->next;
}
return 0
}
int main(int argc, char* argv[]) {
char* key[ESTS] = {"alpha"};
void* ptr[ESTS] = {(void*)0xdeadbeef};
SpringMap* map = springmap_init();
for(int i = 0; i < TESTS; i++) {
int status = springmap_add(map, key[i], ptr[i]);
}
springmap_free(map);
return 0;
I'm not up to speed on void pointers.
The function name already tells: strdup composes of string duplicate, and it only is able to duplicate null-terminated C-strings (well, admittedly any data as long as it contains a null byte somewhere, though it would get cut off too early unless this null byte was the very last byte within the data).
void pointers in C have the unfortunate nature of implicitly converting to any other pointer type, happening in your code as well. However these pointers do not point to null-terminated C-strings, actually, they aren't even valid at all (most of most likely, at least)! Thus trying to read from them yields undefined behaviour.
So at first make sure that your void pointers point to valid memory. To use strdup they should point to C-strings, otherwise memcpy is the way to go, though you need to malloc storage space as target first. For both, you need the size of the object available, though. However you cannot get that back from the void pointer any more, thus you need yet another parameter.
You could write your own objdup function covering the duplication:
void* objdup(size_t size, void* object)
{
void* copy = malloc(size);
if(copy)
{
memcpy(copy, object, size);
}
return copy;
}
Still your pointers need to be valid! Some possible example might look like:
int main()
{
SomeDataType o1;
AnotherDataType o2;
AnotherDatatType* ptr = &o2; // create a valid pointer
// (could be done by malloc'ing memory, too)
void* c1 = objdup(sizeof(o1), &o1);
// ^ take the address of a REAL object!
if(c1)
{
void* c2 = objdup(sizeof(*o2), o2); // or via pointer to REAL object
if(c2)
{
// ...
free(c2);
}
free(c1);
}
return 0;
}
This question already has answers here:
Initializing a pointer in a separate function in C
(2 answers)
Closed 3 years ago.
My goal is to pass a pointer to double to a function, dynamically allocate memory inside of the function, fill resulted array with double values and return filled array. After lurking attentively everywhere in StackOverflow, I have found two related topics, namely Initializing a pointer in a separate function in C and C dynamically growing array. Accordingly, I have tried to write my own code. However, the result was not the same as it was described in aforementioned topics. This program was run using both gcc and Visual Studio.
First trial.
int main()
{
double *p;
int count = getArray(&p);
<...print content of p...>
return 0;
}
int getArray(double *p)
{
int count = 1;
while(1)
{
if(count == 1)
p = (double*)malloc(sizeof(double));
else
p = (double*)realloc(p, count*sizeof(double));
scanf("%lf", &p[count-1]);
<...some condition to break...>
count++;
{
<... print the content of p ...>
return count;
}
(Here comes the warning from compiler about incompatible argument type. Ignore it).
Input:
1.11
2.22
3.33
Output:
1.11
2.22
3.33
0.00
0.00
0.00
Second trial.
int main()
{
double *p;
int count = getArray(&p);
<...print content of p...>
return 0;
}
int getArray(double **p)
{
int count = 1;
while(1)
{
if(count == 1)
*p = (double*)malloc(sizeof(double));
else
{
double ** temp = (double*)realloc(*p, count*sizeof(double));
p = temp;
}
scanf("%lf", &(*p)[count-1]);
<...some condition to break...>
count++;
{
<... print the content of p ...>
return count;
}
Input:
1.11
2.22
Segmentation error.
I tried this method on several different *nix machines, it fails when the loop uses realloc. SURPRISINGLY, this code works perfect using Visual Studio.
My questions are: first code allows to allocate and reallocate the memory and even passes all this allocated memory to main(), however, all the values are zeroed. What is the problem? As for the second program, what is the reason of the segmentation error?
The right way of doing it is like this:
int getArray(double **p)
{
int count = 0;
while(1)
{
if(count == 0)
*p = malloc(sizeof(**p));
else
*p = realloc(*p, (count+1)*sizeof(**p));
scanf("%lf", &((*p)[count]));
<...some condition to break...>
count++;
{
<...print content of p...>
return count;
}
If you pass a pointer to a function and you want to change not only the value it is pointing at, but change the address it is pointing to you HAVE to use a double pointer. It is simply not possible otherwise.
And save yourself some trouble by using sizeof(var) instead of sizeof(type). If you write int *p; p = malloc(sizeof(int));, then you are writing the same thing (int) twice, which means that you can mess things up if they don't match, which is exactly what happened to you. This also makes it harder to change the code afterwards, because you need to change at multiple places. If you instead write int *p; p = malloc(sizeof(*p)); that risk is gone.
Plus, don't cast malloc. It's completely unnecessary.
One more thing you always should do when allocating (and reallocating) is to check if the allocation was successful. Like this:
if(count == 0)
*p = malloc(sizeof(**p));
else
*p = realloc(*p, (count+1)*sizeof(**p));
if(!p) { /* Handle error */ }
Also note that it is possible to reallocate a NULL pointer, so in this case the malloc is not necessary. Just use the realloc call only without the if statement. One thing worth mentioning is that if you want to be able to continue execution if the realloc fails, you should NOT assign p to the return value. If realloc fails, you will lose whatever you had before. Do like this instead:
int getArray(double **p)
{
int count = 0;
// If *p is not pointing to allocated memory or NULL, the behavior
// of realloc will be undefined.
*p = NULL;
while(1)
{
void *tmp = realloc(*p, (count+1)*sizeof(**p));
if(!tmp) {
fprintf(stderr, "Fail allocating");
exit(EXIT_FAILURE);
}
*p = tmp;
// I prefer fgets and sscanf. Makes it easier to avoid problems
// with remaining characters in stdin and it makes debugging easier
const size_t str_size = 100;
char str[str_size];
if(! fgets(str, str_size, stdin)) {
fprintf(stderr, "Fail reading");
exit(EXIT_FAILURE);
}
if(sscanf(str, "%lf", &((*p)[count])) != 1) {
fprintf(stderr, "Fail converting");
exit(EXIT_FAILURE);
}
count++;
// Just an arbitrary exit condition
if ((*p)[count-1] < 1) {
printf("%lf\n", (*p)[count]);
break;
}
}
return count;
}
You mentioned in comments below that you're having troubles with pointers in general. That's not unusual. It can be a bit tricky, and it takes some practice to get used to it. My best advice is to learn what * and & really means and really think things through. * is the dereference operator, so *p is the value that exists at address p. **p is the value that exists at address *p. The address operator & is kind of an inverse to *, so *&x is the same as x. Also remember that the [] operator used for indexing is just syntactic sugar. It works like this: p[5] translates to *(p+5), which has the funny effect that p[5] is the same as 5[p].
In my first version of above code, I used p = tmp instead of *p = tmp and when I constructed a complete example to find that bug, I also used *p[count] instead of (*p)[count]. Sorry about that, but it does emphasize my point. When dealing with pointers, and especially pointers to pointers, REALLY think about what you're writing. *p[count] is equivalent to *(*(p+count)) while (*p)[count] is equivalent to *((*p) + count) which is something completely different, and unfortunately, none of these mistakes was caught even though I compiled with -Wall -Wextra -std=c18 -pedantic-errors.
You mentioned in comments below that you need to cast the result of realloc. That probably means that you're using a C++ compiler, and in that case you need to cast, and it should be (double *). In that case, change to this:
double *tmp = (double*)realloc(*p, (count+1)*sizeof(**p));
if(!tmp) {
fprintf(stderr, "Fail allocating");
exit(EXIT_FAILURE);
}
*p = tmp;
Note that I also changed the type of the pointer. In C, it does not matter what type of pointer tmp is, but in C++ it either has to be a double* or you would need to do another cast: *p = (double*)tmp
int insert(struct node *temp, char *input_string, int str_len) {
char ch = *input_string;
int copied=str_len;
int i;
temp =(struct node *) temp->child_nodes[ch];
if (temp->data[0] == "NULL") { //when the node is empty and has no data in its data[]
for (i = 0; i < str_len; i++) {
temp->data[i] = *input_string;//copy the character into the array
input_string++;//increment the pointer to get next character to be inserted
}//for closed
return 1; //function job completed
}//if closed
else {
i = 0;
while (str_len - copied) {
if (temp->data[i] == ch) {//till the characters are same to be inserted.
copied++;
i++;
input_string++;
ch = *input_string;
continue;
}
else //when the data to be inserted is not same.
{
//initialise a node1 and node2 memory code here.
temp->no_of_child_nodes += 2;
temp->child_nodes[temp->data[i]] = (struct node*) malloc(sizeof(struct node));
temp->child_nodes[ch] = malloc(sizeof(struct node));
while (temp->data[i] != "NULL") {//check for the parent node data to reach NULL
node1->data[i] = temp->data[i];//copy data
i++;//increment I
}
while (str_len - copied) {
node2->data[i] = ch;
input_string++;
ch++;
}
}
}
}
}
In the Function Insert, i face a situation where I need to Add two structure nodes inside a Function. As the memory allocated will be freed upon exiting Function, How can I fix this?
To be able to access a dynamically allocated structure everywhere, you're going to need have access to its location to be able to reference it anywhere, and for this we use pointers. Let me show you an example.
Let's say you have a Point data type to represent a point in three-dimensional space.
typedef struct point_t {
double x;
double y;
double z;
} *Point;
Now you can create a "builder" function -not unlike a constructor in C++, although not quite- where you set its initial values and return its pointer. This is how you're allocating the structure dynamically and preserving a pointer to it so that you can continue to use it in other parts of your program.
Point createPoint(double a, double b, double c) {
Point newPoint = malloc(sizeof(struct point_t));
if (newPoint == NULL) { /* Handle failure here... */ }
SetPoint(&newPoint,a,b,c);
return newPoint;
}
And then you would just define SetPoint like this:
void SetPoint(Point* p, double a, double b, double c) {
if (p == NULL) { return; }
*p->x = a;
*p->y = b;
*p->z = c;
}
To actually use the structure, you're going to have to do two things. First, you're going to have to construct the structure, then you'll have to pass it to the other function that needs it. You've already seen this at work, in the SetPoint function above.
Notice that Point* p declares a double pointer to a structure of type struct point_t. We need one layer of indirection to work with the pointer itself and then one additional layer of indirection so that we can pass the pointer by reference and modify its contents in the function. If we passed in a regular Point handle, the pass by value mechanism would discard the effects of the functions.
So with this hypothetical data structure, your main function might use it like this:
int main()
{
Point origin = createPoint(0,0,0);
PrintPoint(origin);
Point p1 = createPoint(1,-1,5);
PrintPoint(p1);
printf("Distance between p1 and the origin: %lf\n", DistanceBetween(p1,origin));
return EXIT_SUCCESS;
}
You might define Print Point like this:
void PrintPoint(Point p) {
if (p == NULL) { return; }
printf("(%lf,%lf,%lf)\n", p->x, p->y, p->z);
}
Likewise, you can define the DistanceBetween function like this: (Remember that the distance between any two points in three-dimensional Euclidean space is basically the Pythagorean Theorem but with an extra dimension.
double DistanceBetween(Point a, Point b) {
return sqrt(pow((b->x - a->x),2) + pow((b->y - a->y),2) + pow((b->z - a->z),2));
}
For some general pointers, remember to stay vigilant about NULL pointers. Not only can they cause a segmentation fault or give you garbage results -sometimes this is even worse imo- but it can also lead to code vulnerabilities, like the "double free" exploit in zlib years ago. There was another one recently but I didn't read the exploit summary.
If you want to play it safe with with double free's, you can use the method Professor Reese wrote about in "Understanding and Using C Pointers." If we apply it to the hypothetical Point structure from above, it looks like this:
void SafeFree(Point* pointHandle) {
if (*pointHandle == NULL) { return; }
free(*pointHandle);
*pointHandle = NULL;
}
Notice that we're passing in the pointer we want to free by reference (so you'd have to free it like this: free(&origin), for example. This is so that we can set it to NULL once we've free'd it. This way, if you were to call free on the same memory location, even if it was being referenced by a different pointer, this NULL check would save you from double-freeing memory.
Anyways, just an added precaution, I hope all this helps, let me know if you have any more questions.
So i got a strucure:
typedef struct Achat {
char aAcheter[25];
double quantite;
double prixUnitaire;
categorie rayon;
} achat;
Two static ints:
static int physicalSize = 0;
static int logicalSize = 0;
And a function:
int ajout (achat a, achat **table){
if (physicalSize == 0){
if ((*table = (achat *) malloc (5 * sizeof(achat))) == NULL){
perror ("malloc error");
return -1;
}
physicalSize = 5;
}
if (logicalSize == physicalSize){
if ((*table = (achat *) realloc(table, (physicalSize *= 2) * sizeof(achat))) == NULL){
perror("realloc error");
return -1;
}
}
*(table)[logicalSize] = a;
logicalSize++;
return logicalSize;
}
Basically, everything works fine when I call the function the first time, the item is added in the table and both the physicalSize and the logicalSize are updated.
The problem occurs when i call the function for the second time: I get a segmentation error. My guess would be that the malloc wasn't done well, even tho I can't see what I should change :/
Thanks for your answers :)
nb: the second argument (achat **table) is a single array, passed with the address of the table.
I suspect, by the superfluous parentheses, that your error lies in *(table)[logicalSize].
This treats table as a pointer to an array of achat*, when your note states that it is a pointer to a pointer to an array of achat.
The second interpretation would be written (*table)[logicalSize].
There's another typo in here : realloc(table, ...) should be realloc(*table, ...) to be consistent with the other uses of table (thanks MikeCAT!).
On a side note, please don't cast the result of malloc : it's useless at best, and harmful at worst.
I have a C struct:
typedef struct {
Dataset *datasets;
int nDatasets;
char *group_name;
enum groupType type;
} DatasetGroup;
It has a constructor function like this:
DatasetGroup * new_DatasetGroup(char *group_name, enum groupType type, enum returnCode *ret)
{
DatasetGroup *dg;
dg = (DatasetGroup *) malloc(sizeof(DatasetGroup));
if (dg == NULL)
{
*ret = EMEMORY_ERROR;
}
// Allocate space for a few datasets
dg->datasets = malloc(sizeof(Dataset) * INCREMENT);
if (dg->datasets == NULL)
{
*ret = EMEMORY_ERROR;
}
dg->group_name= malloc(sizeof(char) * strlen(group_name));
strcpy(dg->group_name, group_name);
dg->type = type;
groupCount++;
return dg;
}
I want to dynamically create an array of these structs. Whats the best way to do this?
So far I have something like:
DatasetGroup * make_array(){
DatasetGroup *dg_array;
// Allocate space for a few groups
dg_array = (DatasetGroup *) malloc(sizeof(DatasetGroup) * INCREMENT);
return dg_array;
}
void add_group_to_array(DatasetGroup *dg_array, ...){
// Add a datasetgroup
DatasetGroup *dg = new_DatasetGroup(...);
// groupCount - 1 as the count is incremented when the group is created, so will always be one ahead of the array index we want to assign to
dg_array[groupCount - 1] = dg;
if (groupCount % INCREMENT == 0)
{
//Grow the array
dg_array = realloc(dg_array, sizeof(DatasetGroup) * (groupCount + INCREMENT));
}
}
But this doesnt seem right....
any ideas?
A few suggestions:
You have groupCount being incremented by the constructor function of the struct. This means you can only have one array of the struct that uses your array function. I would recommend having the array be responsible for managing the count.
To that affect if you want to have a managed array I would create a struct for that and have it keep both the pointer to the array,the number of objects and the size of the array (e.g. the maximum number of structs it can currently hold)
If you keep proper track of how many elements you have and the size of the array you can replace groupCount % INCREMENT == 0 with something like groupCount == arraySize which is a lot more intuitive in my opinion.
You can avoid the second malloc in the constructor all together by having the array be an array of the elements instead of an array of pointers. The constructor than then just initialize the struct members instead of allocating memory. If you are doing this a lot you will be avoiding a lot of memory fragmentation.
Finally, while this depends on your application, I usually recommend when you realloc do not increase by a constant but instead of by a multiple of the current array size. If say you double the array size you only have to do log_2 n number of reallocs with n being the final array size and you waste at most half of memory (memory is generally cheap, like I said it depends on the application). If that is wasting to much memory you can do say 1.5. If you want a more detailed explanation of this I recommend this Joel on Software article, the part about realloc is about 2/3 down.
Update:
A few others things:
dg = (DatasetGroup *) malloc(sizeof(DatasetGroup));
if (dg == NULL)
{
ret = EMEMORY_ERROR;
}
// Allocate space for a few datasets
dg->datasets = malloc(sizeof(Dataset) * INCREMENT);
As previously pointed out is very bad as you will us dg even if it is NULL. You probably want to exit right after detecting the error.
Furthermore you are setting ret but ret is passed by value so it will not be changed for the caller if the callee changes it. Instead you probably want to pass a pointer and dereference it.
Update 2: Can I give an example, sure, quick not so much ;-D.
Consider the following code (I apologize if there are any mistakes, still half asleep):
#include <stdio.h>
#include <stdlib.h>
#define LESS_MALLOCS
#define MAX_COUNT 100000000
typedef struct _foo_t
{
int bar1;
int bar2;
} foo_t;
void foo_init(foo_t *foo, int bar1, int bar2)
{
foo->bar1 = bar1;
foo->bar2 = bar2;
}
foo_t* new_foo(int bar1, int bar2)
{
foo_t *foo = malloc(sizeof(foo_t));
if(foo == NULL) {
return NULL;
}
foo->bar1 = bar1;
foo->bar2 = bar2;
return foo;
}
typedef struct _foo_array_t
{
#ifdef LESS_MALLOCS
foo_t *array;
#else
foo_t **array;
#endif
int count;
int length;
} foo_array_t;
void foo_array_init(foo_array_t* foo_array, int size) {
foo_array->count = 0;
#ifdef LESS_MALLOCS
foo_array->array = malloc(sizeof(foo_t) * size);
#else
foo_array->array = malloc(sizeof(foo_t*) * size);
#endif
foo_array->length = size;
}
int foo_array_add(foo_array_t* foo_array, int bar1, int bar2)
{
if(foo_array->count == foo_array->length) {
#ifdef LESS_MALLOCS
size_t new_size = sizeof(foo_t) * foo_array->length * 2;
#else
size_t new_size = sizeof(foo_t*) * foo_array->length * 2;
#endif
void* tmp = realloc(foo_array->array, new_size);
if(tmp == NULL) {
return -1;
}
foo_array->array = tmp;
foo_array->length *= 2;
}
#ifdef LESS_MALLOCS
foo_init(&(foo_array->array[foo_array->count++]), bar1, bar2);
#else
foo_array->array[foo_array->count] = new_foo(bar1, bar2);
if(foo_array->array[foo_array->count] == NULL) {
return -1;
}
foo_array->count++;
#endif
return foo_array->count;
}
int main()
{
int i;
foo_array_t foo_array;
foo_array_init(&foo_array, 20);
for(i = 0; i < MAX_COUNT; i++) {
if(foo_array_add(&foo_array, i, i+1) != (i+1)) {
fprintf(stderr, "Failed to add element %d\n", i);
return EXIT_FAILURE;
}
}
printf("Added all elements\n");
return EXIT_SUCCESS;
}
There is a struct (foo_t) with two members (bar1 and bar2) and another struct that is an array wrapper (foo_array_t). foo_array_t keeps track of the current size of the array and the number of elements in the array. It has an add element function (foo_array_add). Note that there is a foo_init and a new_foo, foo_init takes a pointer to a foo_t and new_foo does not and instead returns a pointer. So foo_init assumes the memory has been allocated in some way, heap, stack or whatever doesn't matter, while new_foo will allocate memory from the heap. There is also a preprocess macro called LESS_MALLOCS. This changes the definition of the array member of foo_array_t, the size of the initial array allocation, the size during reallocation and whether foo_init or new_foo is used. The array and its size have to change to reflect whether a pointer or the actually element is in the array. With LESS_MACRO defined the code is following my suggestion for number 4, when not, it is more similar to your code. Finally, main contains a simple micro-benchmark. The results are the following:
[missimer#asus-laptop tmp]$ gcc temp.c # Compile with LESS_MACROS defined
[missimer#asus-laptop tmp]$ time ./a.out
Added all elements
real 0m1.747s
user 0m1.384s
sys 0m0.357s
[missimer#asus-laptop tmp]$ gcc temp.c #Compile with LESS_MACROS not defined
[missimer#asus-laptop tmp]$ time ./a.out
Added all elements
real 0m9.360s
user 0m4.804s
sys 0m1.968s
Not that time is the best way to measure a benchmark but in this case I think the results speak for themselves. Also, when you allocate an array of elements instead of an array of pointers and then allocate the elements separately you reduce the number of places you have to check for errors. Of course everything has trade-offs, if for example the struct was very large and you wanted to move elements around in the array you would be doing a lot of memcpy-ing as opposed to just moving a pointer around in your approach.
Also, I would recommend against this:
dg_array = realloc(dg_array, sizeof(DatasetGroup) * (groupCount + INCREMENT));
As you lose the value of the original pointer if realloc fails and returns NULL. Also like your previous ret, you should pass a pointer instead of the value as you are not changing the value to the caller, just the callee which then exits so it has no real affect. Finally, I noticed you changed your function definition to have a pointer to ret but you need to dereference that pointer when you use it, you should be getting compiler warnings (perhaps even errors) when you do try what you currently have.
You could do two things, either you dynamically create an array of struct pointers, then call your new function to create N datagroups, or you could dynamically request memory for N structures at once, this would mean your N structures would be contiguously allocated.
Datagroup **parry = malloc(sizeof(datagroup *) * N)
for (int i = 0; i < N; i++){
parry[i] = //yourconstructor
}
Or
//allocate N empty structures
Datagroup *contarr = calloc(N, sizeof(Datagroup))
The second method might need a different initialization routine than your constructor, as the memory is already allocated