Hello i am making a program in C which stores integers on a dynamic array which uses realloc every time it has to add a new element, i declare the array on the main:
int *abundants;
int count = abundant_numbers(&abundants);
once finished, i want to pass the modified array to another function to make other calculations
int abundant_numbers(int *abundants[]){
if (!(*abundants = (int*) malloc(sizeof(int)))){
perror("malloc error!\n");
exit(EXIT_FAILURE);
}
*abundants[0] = 12; //we know the very first abundant number
int count = 1, n = 14;
while (n < MAX_NUM){
if (is_abundant(n)) {
if (!(*abundants = (int*) realloc(*abundants,(count+1) * sizeof(int)))){
perror("Error in realloc\n");
exit(EXIT_FAILURE);
}
*abundants[count] = n;
count++;
}
n += 2; //no odd abundant numbers
}
return count;
}
the first time it enters on the if statement gives no problems, but the second time on the assignment i get a Segmentation Fault: 11, when accesing abundants[2], i dont understand why its not a valid position if it worked fine for abundants[1]
Thanks.
Your problem is a simple one in these lines:
*abundants[0] = 12;
*abundants[count] = n;
The indexing operator [] has higher precedence than the dereference operator *. So here you're treating your abundants as an array pointer directly and try to dereference the element. What you want instead is
(*abundants)[0] = 12;
(*abundants)[count] = n;
This should solve your problem, the remaining code will work correctly.
That being said, I would strongly suggest to use some data structure like this:
struct dynarr
{
size_t count;
size_t capacity;
int entries[];
}
and realloc() in larger chunks, always when your count reaches your capacity. realloc() is costly and you risk fragmenting your heap space in a typical heap-based implementation. Your code could look for example like this:
#include <stdio.h>
#include <stdlib.h>
#define MAX_NUM 1024
int is_abundant(int x) { return x; } // simple fake to make it compile, replace
struct dynarr
{
size_t count;
size_t capacity;
int entries[];
};
struct dynarr *createarr(size_t capacity)
{
struct dynarr *arr = malloc(sizeof(*arr) + capacity * sizeof(int));
if (!arr)
{
perror("malloc error!\n");
exit(EXIT_FAILURE);
}
arr->count = 0;
arr->capacity = capacity;
return arr;
}
struct dynarr *expandarr(struct dynarr *arr)
{
size_t capacity = arr->capacity * 2;
struct dynarr *newarr = realloc(arr,
sizeof(*newarr) + capacity * sizeof(int));
if (!newarr)
{
perror("malloc error!\n");
free(arr);
exit(EXIT_FAILURE);
}
newarr->capacity = capacity;
return newarr;
}
struct dynarr *abundant_numbers(void){
struct dynarr *abundants = createarr(32);
abundants->entries[abundants->count++] = 12; //we know the very first abundant number
int n = 14;
while (n < MAX_NUM){
if (is_abundant(n)) {
if (abundants->count == abundants->capacity)
{
abundants = expandarr(abundants);
}
abundants->entries[abundants->count++] = n;
}
n += 2; //no odd abundant numbers
}
return abundants;
}
int main(void)
{
struct dynarr *abundants = abundant_numbers();
for (size_t i = 0; i < abundants->count; ++i)
{
printf("%d ", abundants->entries[i]);
}
free(abundants);
putchar('\n');
}
the biggest problem is that the code is expecting an array of pointers to int.
But the code is only producing an array of `int`s
And the code contains several 'magic' numbers (2, 12, 14)
int *abundants = NULL;
int count = abundant_numbers(&abundants);
int abundant_numbers(int *abundants[])
{
if (!( abundants = malloc(sizeof(int))))
{
perror("malloc error!\n");
exit(EXIT_FAILURE);
}
abundants[0] = 12; //we know the very first abundant number
int count = 1;
int n = 14;
while (n < MAX_NUM)
{
if (is_abundant(n))
{
void *temp;
if (!( temp = realloc(abundants,(count+1) * sizeof(int))))
{
perror("Error in realloc\n");
free( abundants );
exit(EXIT_FAILURE);
}
// implied else, realloc successful
abundants = temp;
abundants[count] = n;
count++;
}
n += 2; //no odd abundant numbers
}
return count;
}
However, since MAX_NUM is a known value,
it would be better to just allocate that much memory in the beginning.
And strongly suggest to NOT have 'special' code
for special cases of the value of 'n'.
And give 'magic' numbers meaningful names, suggest via #define statements.
sample code follows:
#include <stdlib.h> // malloc(), free()
// use whatever value your program needs in the following statement.
#define MAX_NUM 1024
#define FIRST_ABUNDANT 12
#define STEP_AMOUNT 2
// prototypes
int abundant_numbers( int * );
int main( void )
{
int *abundants = NULL;
if (!( abundants = malloc(sizeof(int) * MAX_NUM)))
{
perror("malloc error!\n");
exit(EXIT_FAILURE);
}
// implied else, malloc successful
int count = abundant_numbers( abundants );
} // end function: main
int abundant_numbers( int *abundants )
{
int count = 0;
for( int n=FIRST_ABUNDANT; n < MAX_NUM; n+=STEP_AMOUNT )
{
if (is_abundant(n))
{
abundants[count] = n;
count++;
}
}
return count;
} // end function: abundant_numbers
Related
In a this code,
#include <stdio.h>
#include <stdlib.h>
typedef struct test
{
int i;
double data;
} test;
void add(ar) struct test *ar;
{
int num = 10;
// *ar = malloc(num * sizeof(struct test)); // Adding this
for (int i = 0; i < num; i++)
{
ar[i].i = i;
ar[i].data = i * i;
}
}
int main(void)
{
test ar[10]; // Removing this
add(&ar);
for (int i = 0; i < 10; i++)
{
printf("%d %f\n", ar[i].i, ar[i].data);
}
return 0;
}
How do we define the struct in main but allocate the memory in the function?
I want to set the number (num) inside the function add, as it is not yet known in main.
There are two values you have to pass back from add() to main(), num and the malloc()ed array itself. As you can return only one value, there are two positilities:
a) return num and have a test ** parameter to pass back the array
int add( struct test **ar )
{
int num = 10;
*ar = malloc( num * sizeof **ar );
// initialize everything, you have to replace ar[i] by (*ar)[i]
return num;
}
call it
struct test *ar;
int num = add( &ar );
b) return the array and have a int * parameter to pass back num
struct test *add( int *num )
{
*num = 10;
struct test *ar = malloc( *num * sizeof *ar );
// initialize everything as you do now
return ar;
}
call it
int num;
struct test *ar = add( &num );
As #Bodo mentioned, either way, you have to call free( ar ); in main() when you don't need ar anymore. You could call it directly or think about having a cleanup function like free_test( struct test *ar, int num ); that does the job. Such a function is especially useful if you have more malloc()s to allocate memory for the single struct elements (eg. if they contained char * elements to store strings).
use the argument(s) to pass argument(s)
use the return value to return stuff to the caller
#include <stdio.h>
#include <stdlib.h>
struct test
{
int i;
double data;
} ;
struct test *test_create(unsigned ntest)
{
struct test *pp;
unsigned idx;
pp = malloc(sizeof *pp * ntest);
for (idx = 0; idx < ntest; idx++)
{
pp[idx].i = idx;
pp[idx].data = idx * idx;
}
return pp;
}
int main(void)
{
struct test *ptr;
ptr = test_create(10);
for (int i = 0; i < 10; i++)
{
printf("%d %f\n", ptr[i].i, ptr[i].data);
}
return 0;
}
Update: if you want to return more than a single item, you could put the items together in a struct:
#include <stdio.h>
#include <stdlib.h>
struct dope {
unsigned size;
struct {
int i;
double data;
} *array;
} ;
struct dope *dope_create(void)
{
struct dope *pp;
unsigned omg =10;
pp = malloc(sizeof *pp );
pp->array = malloc(sizeof *pp->array * omg);
pp->size = omg;
for (omg = 0; omg < pp->size; omg++)
{
pp->array[omg].i = omg;
pp->array[omg].data = omg * omg;
}
return pp;
}
int main(void)
{
struct dope *ptr;
ptr = dope_create();
for (int iii = 0; iii < ptr->size; iii++)
{
printf("%d %f\n", ptr->array[iii].i, ptr->array[iii].data);
}
return 0;
}
You have already good answers as alternatives. Anyway I will show you code for 2 common ways of writing this.
As I see in your code, the factory function will determine the actual number of structs test to allocate. I will in the example:
generate a random number of structs, using rand() since it makes no difference here
fill them in like (1,1.01), (2,2.02) ... just to have a known value for testing
show the structs' contents on screen
free() them at exit
assume that can also be 0 structures created
1: use a NULL-terminated sequence of pointers
As it is used with success in all C strings :) we can use it here. As in strings (C strings), you need to search for the terminator in order to get the array size (Of course you can set the first pointer apart for the size, Pascal-like). It can or can not be of importance to know before-hand the # of structs.
Test** add_vector()
{
// returns a null terminated array of pointers
// to 'Test', pointing to actual instances of
// 'Test', allocated and numbered with 'i' starting
// at 1 and 'data' starting at 1.01
int num = rand() % 10; // 0 to 9 Test
fprintf(stderr,
"add_vector(): creating %d structs\n", num);
// at least one pointer, the terminating one
Test** ar = (Test**)malloc((1 + num) * sizeof(Test*));
int ix = 0;
for (ix = 0; ix < num; ix += 1)
{
ar[ix] = (Test*)malloc(sizeof(Test));
// sets up ar[i] to a known value
ar[ix]->i = 1 + ix;
ar[ix]->data = 1 + ix + (1 + ix) / 100.;
}; // for()
ar[ix] = NULL; // the terminator one, as in strings
return ar;
}
To use it you just call
Test** vector = add_vector();
as you see in the example below. A single pointer is returned. No need for arguments. If the number of structs is zero a single pointer is returned, like it would be with an empty string.
The # of structs is defined inside the function, and all structs instances are allocated and numbered before returning to caller.
2: return a pointer to a struct
typedef struct
{
int i;
double data;
} Test;
typedef struct
{
unsigned size;
Test* test;
} V_Test;
V_Test* add_struct();
The allocation function returns a pointer to a V_Test struct, that contains an array of Test strucs and a size elements, an in main() for every C program
int main(in argc, char** argv)
Here is the code:
V_Test* add_struct()
{
// returns a vector of structs inside V_Test,
// with known 'size', like in main( int,char**)
// The structs are initialized with 'i' starting
// at 1 and 'data' starting at 1.01
unsigned num = rand() % 10; // 0 to 9 Test
fprintf(stderr, "add_struct(): creating %d structs\n", num);
V_Test* v = (V_Test*) malloc(sizeof(V_Test));
v->size = num;
if (num == 0)
{
v->test = NULL;
return v;
};
v->test = (Test*)malloc(num * sizeof(Test));
for (unsigned ix = 0; ix < num; ix += 1)
{
v->test[ix].i = 1 + ix;
v->test[ix].data = 1 + ix + (1 + ix) / 100.;
}; // for()
return v;
}
To used it you just call
V_Test* vector = add_struct();
And here also there are no arguments. A new V_Test is allocated and returned.
Also here the # of structs is defined inside the function, and all structs instances are allocated and numbered before returning to caller.
In the code you will see a way of use both functions, create the structs, fill them in, display the contents and release the allocated memory.
Example output
add_vector(): creating 9 structs
# 1: [1, 1.01]
# 2: [2, 2.02]
# 3: [3, 3.03]
# 4: [4, 4.04]
# 5: [5, 5.05]
# 6: [6, 6.06]
# 7: [7, 7.07]
# 8: [8, 8.08]
# 9: [9, 9.09]
9 records were created
9 records were free()'d
add_struct(): creating 4 structs
# 1: [1, 1.01]
# 2: [2, 2.02]
# 3: [3, 3.03]
# 4: [4, 4.04]
4 records were created
4 records were free()'d
C Code
I compiled just once under MSVC.
#include <stdio.h>
#include <stdlib.h>
typedef struct
{
int i;
double data;
} Test;
typedef struct
{
unsigned size;
Test* test;
} V_Test;
V_Test* add_struct();
Test** add_vector();
void as_vector_of_pointers();
void as_struct_of_struct();
int main(void)
{
srand(210728);
as_vector_of_pointers();
as_struct_of_struct();
return 0;
}
Test** add_vector()
{
// returns a null terminated array of pointers
// to test
int num = rand() % 10; // 0 to 9 Test
fprintf(stderr,
"add_vector(): creating %d structs\n", num);
// at least one pointer, the terminating one
Test** ar = (Test**)malloc((1 + num) * sizeof(Test*));
int ix = 0;
for (ix = 0; ix < num; ix += 1)
{
ar[ix] = (Test*)malloc(sizeof(Test));
// sets up ar[i] to a known value
ar[ix]->i = 1 + ix;
ar[ix]->data = 1 + ix + (1 + ix) / 100.;
}; // for()
ar[ix] = NULL; // the terminator one, as in strings
return ar;
}
V_Test* add_struct()
{
// returns a vector of structs inside V_Test,
// with known 'size', like in
// main( int,char**)
unsigned num = rand() % 10; // 0 to 9 Test
fprintf(stderr, "add_struct(): creating %d structs\n", num);
V_Test* v = (V_Test*) malloc(sizeof(V_Test));
v->size = num;
if (num == 0)
{
v->test = NULL;
return v;
};
v->test = (Test*)malloc(num * sizeof(Test));
for (unsigned ix = 0; ix < num; ix += 1)
{
v->test[ix].i = 1 + ix;
v->test[ix].data = 1 + ix + (1 + ix) / 100.;
}; // for()
return v;
}
void as_struct_of_struct()
{
V_Test* vector = add_struct();
if (vector->size == 0)
{
printf("No records were created!\n");
free(vector);
return;
}
for ( unsigned count = 0; count < vector->size; count+=1)
{
printf("#%3d: [%d, %.2f]\n",
1 + count,
vector->test[count].i,
vector->test[count].data
);
}; // for()
printf("%d records were created\n", vector->size);
// to free() vector:
free(vector->test);
printf("%d records were free()'d\n", vector->size);
free(vector);
return;
};
void as_vector_of_pointers()
{
Test** vector = add_vector();
// now test the vector to count the number of
// records generated in the funcion
if (vector[0] == NULL)
{
printf("No records were created!\n");
free(vector);
return;
}
unsigned count = 0;
while (vector[count] != NULL)
{
printf("#%3d: [%d, %.2f]\n", 1 + count, (vector[count])->i,
(vector[count])->data);
count += 1;
};
printf("%d records were created\n", count);
// to free() vector, same way:
for (unsigned i = 0; i < count; i += 1) free(vector[i]);
free(vector);
printf("%d records were free()'d\n", count);
return;
}
SO vigilants: I always cast malloc() pointers, as I reminder to
myself and others reading the code. No implicit conversions. No need to pointing that.
I'm trying to add new element to dynamic array in C (I know that I must free all memory. I will do it later), but I get this error every time:
But, what is strange, if I compile from terminal, like that, code works properly.
So, where is the error and how i can beat it?
Thank you!
All my code:
main.c
#include <stdio.h>
#include <stdlib.h>
typedef struct vector
{
int size;
int *array;
int alreadyIn;
}vector;
vector *vectorInit(int size)
{
vector *newVec = (vector *)malloc(sizeof(vector));
if(!newVec){printf("No memory!\n"); return NULL;}
newVec->size = size;
newVec->array = (int *)malloc(size * sizeof(int));
return newVec;
}
void allocNewMemory(vector *vect, int howMuch)
{
vect->array = (int *)realloc(vect->array ,(vect->size + howMuch) * sizeof(int));
vect->size += howMuch;
}
void pushBack(vector *vect, int number)
{
int howMuch = 5;
if(vect && vect->alreadyIn < vect->size)
{
vect->array[vect->alreadyIn] = number;
vect->alreadyIn++;
}
else
{
printf("Alloc new memory for %d elements...\n", howMuch);
allocNewMemory(vect, howMuch);
pushBack(vect, number);
}
}
void printVector(vector *vect)
{
for (int i = 0; i < vect->alreadyIn; i++)
{
printf("%d ", vect->array[i]);
}
printf("\n");
}
int main()
{
int startSize = 4;
vector * vec = vectorInit(startSize);
for (int i = 0; i < 6; i++)
{
pushBack(vec, i+1);
}
printVector(vec);
return 0;
}
You never initialize the alreadyIn member in the structure. That means its value will be indeterminate (and seemingly garbage or random).
You need to explicitly initialize it to zero:
vector *vectorInit(int size)
{
vector *newVec = malloc(sizeof(vector));
if(!newVec)
{
printf("No memory!\n");
return NULL;
}
newVec->size = size;
newVec->array = malloc(size * sizeof(int));
newVec->alreadyIn = 0; // Remember to set this to zero
return newVec;
}
This problem should have been easy to detect in the debugger.
Also note that I removed the casts from malloc. One should not cast the result of malloc, or really any function returning void *.
I tried to create dynamically growing array I did with realloc. I have example below but I do not understand how this code works by using malloc.
#include <stdio.h>
#include <stdlib.h>
int main(void) {
int cnt = 0;
double *numbers = NULL;
double newnum;
while (scanf("%lf", &newnum) == 1 && newnum != -1) {
double *newarr = (double*) malloc(sizeof(double) * (cnt+1));
for (int i = 0; i < cnt; ++i)
newarr[i] = numbers[i];
free(numbers);
numbers = newarr;
numbers[cnt] = newarr;
++cnt;
}
for (int i = cnt-1; i >= 0; --i) {
printf("%f\n", numbers[i]);
}
free(numbers);
return 0;
}
realloc() is the same as malloc(), memcpy(), free() all in one function (*).
The loop in your code effectively replaces memcpy()
// memcpy(newarr, numbers, cnt * sizeof *newarr);
for (int i = 0; i < cnt; ++i)
newarr[i] = numbers[i];
(*) realloc() may be smart enough to avoid the memcpy() and free() and reuse memory.
How can I implement this in my code?
It helps to encapsulate the pointer and the total/used elements in a structure.
#include <stdio.h>
#include <stdlib.h>
struct DynArray {
double *data;
size_t m; // total
size_t n; // used
};
void growarray(struct DynArray *x) {
size_t newsize = x->m * 13 / 8 + 1; // use phi aproximation 13/8
double *newarr = realloc(x->data, newsize * sizeof *x->data);
if (!newarr) exit(EXIT_FAILURE);
fprintf(stderr, "INFO: realloc'ed with %d elements.\n", (int)newsize);
x->data = newarr;
x->m = newsize;
}
int main(void) {
struct DynArray numbers = { 0 };
double newnum;
while (scanf("%lf", &newnum) == 1 && newnum != -1) {
if (numbers.n == numbers.m) growarray(&numbers);
numbers.data[numbers.n] = newnum;
numbers.n++;
}
for (int i = numbers.n - 1; i >= 0; --i) {
printf("%f\n", numbers.data[i]);
}
free(numbers.data);
return 0;
}
See code running on ideone or previous version without growarray function or previous version with cnt
I want to append numbers to an empty array and the amount of these numbers is unknown at the start. For example, generating numbers from 1 to 10 and appending one after another.
generateFromOneToTen will save my result in output and count should be 10 after execution. Everything's alright if I print the result in this function.
int generateFromOneToTen(int *output, int count)
{
for (int i = 0; i < 10; i++) {
output = arrayAppendInt(output, i + 1, count);
count++;
}
// Print result of `output` is 1,2,3...10 here
return count;
}
And I implemented arrayAppendInt to dynamic increase the length of an array and append new value after the old ones.
int *arrayAppendInt(int *array, int value, int size)
{
int newSize = size + 1;
int *newArray = (int*) realloc(array, newSize * sizeof(int));
if (newArray == NULL) {
printf("ERROR: unable to realloc memory \n");
return NULL;
}
newArray[size] = value;
return newArray;
}
Here comes the question. When invoking the generation function, numbers will always be NULL. How can I return the generated numbers to the numbers variable?
int *numbers = NULL;
int count = 0;
count = generateFromOneToTen(numbers, 0);
^^^^^^^
You could use a pointer to a pointer of integer (int **):
int generateFromOneToTen(int **output, int count)
{
for (int i = 0; i < 10; i++) {
*output = arrayAppendInt(*output, i + 1, count);
count++;
}
// `*output` is 1,2,3...10 here
return count;
}
You could re-write the arrayAppendInt function like that:
int *arrayAppendInt(int *array, int value, int size)
{
int newSize = size + 1;
int *newArray;
if (array==NULL)
newArray = (int*) malloc ((1+size) * sizeof(int));
else
newArray = (int*) realloc(array, newSize * sizeof(int));
if (newArray == NULL) {
printf("ERROR: unable to realloc memory \n");
return NULL;
}
newArray[size] = value;
return newArray;
}
And call it like that *output = arrayAppendInt(*output, i + 1, i);.
The cleanest solution is (in my opinion) to pack the array+the bookkeeping (size,used) into a structure, and use (a pointer to) this structure as an argument.
#include <stdlib.h>
struct dopedarray {
unsigned size;
unsigned used;
int *array;
};
Now you can put all your allocation and bookkkeeping stuff into a single function (which can be inlined) :
int array_resize(struct dopedarray *ap, unsigned newsize)
{
int *newp;
if(!ap) return -1;
newp = realloc (ap->array, newsize*sizeof*ap->array);
// check return value here...
if (!newp) return -1;
free(ap->array);
ap->array = newp;
ap->size = newsize;
// bookkeeping sanity
if(ap->size > ap->used ) { ap->used > ap->size; }
return 0;
}
The add_element function needs to be changed a bit, too:
int array_add_element(struct dopedarray *ap, int value)
{
if(ap->used >= ap->size){
unsigned newsz;
newsz= ap->used ? 2*ap->used: 4;
array_resize(ap, newsz);
// check return value here...
}
ap->array[ap->used++] = val;
return 0;
}
The complete code to my question:
int generateFromOneToTen(int **output, int count) // +
{
for (int i = 0; i < 10; i++) {
*output = arrayAppendInt(*output, i + 1, count); // ++
count++;
}
return count;
}
int *arrayAppendInt(int *array, int value, int size)
{
int newSize = size + 1;
int *newArray = (int*) realloc(array, newSize * sizeof(int));
if (newArray == NULL) {
printf("ERROR: unable to realloc memory \n");
return NULL;
}
newArray[size] = value;
return newArray;
}
int *numbers = NULL;
int count = 0;
count = generateFromOneToTen(&numbers, 0); // +
This answer also worths reading: https://stackoverflow.com/a/9459803/1951254
As a homework, I'm supposed to create 2 functions that enable you to push and pop elements to an array that acts as a queue. We're supposed to do this dynamically allocating memory. My program is almost working, but sometimes when adding and removing too many elements, I get errors like "realloc(): invalid next size", double free (when I've only called the free function once) and some of the elements in the beginning of the queue are set to 0. For instance, if I first add 100 elements, then remove 90 and try to add another 20, I get "free(): invalid next size (fast): 0x0000000001ea6010".
What am I doing wrong here?
According to suggestions below I changed my functions to take a double pointer as an input for the array. That, however, now gives me a Segmentation fault - which means now I don't know what to look for at all...
#include <stdio.h>
#include <stdlib.h>
void enqueue(int **arr, int* lastElementIdx, size_t* totalElements, int element) {
if (*lastElementIdx >= *totalElements) { // check if memorry is sufficient, otherwise double
*totalElements *= 2;
int* temp = realloc(arr, (*totalElements * sizeof(int)));
if (temp == NULL) { // just in case realloc fails
printf("Allocation error\n");
} else {
*arr = temp;
}
}
if (*lastElementIdx <= *totalElements) {
*lastElementIdx += 1; // once everything is done: add element
*arr[*lastElementIdx] = element;
}
}
int dequeue(int **arr, int* lastElementIdx, size_t* totalElements) {
if (*lastElementIdx > -1) { // if queue is not empty...
int deleted = *arr[0]; // save deleted value first (in case it's still needed)
for (int i = 0; i <= *lastElementIdx; i++) { // shift all elements
*arr[i] = *arr[i + 1];
}
*lastElementIdx -= 1; // index is now decreased by 1
if (((*totalElements / 2) >= 10) && ((*lastElementIdx + 1) < (*totalElements / 2))) { // cut memory in half if not needed
*totalElements /= 2;
*arr = realloc(arr, (*totalElements * sizeof(int)));
int* temp = realloc(arr, (*totalElements * sizeof(int)));
if (temp == NULL) { // in case realloc fails
printf("Allocation error\n");
return 0;
} else {
*arr = temp;
}
}
return deleted;
} else { // if queue is empty, print that there's nothing to dequeue
printf("There are no elements inside the queue\n");
return 0;
}
}
void printQueue(int arr[], int lastElementIdx) {
for (int i = 0; i <= lastElementIdx; i++) { // print entire queue
printf("[%d] = %d\n", i, arr[i]);
}
printf("\n");
}
int main (void) {
size_t totalElements = 10; // number of needed elements at the time
int lastElementIdx = -1; // index of last element in queue at the time
int *arr = calloc(totalElements, sizeof(int));
int **arrpointer = &arr;
for (int i = 1; i < 101; i++) {
enqueue(arrpointer, &lastElementIdx, &totalElements, i);
}
printQueue(arr, lastElementIdx);
for (int i = 0; i < 90; i++) {
dequeue(arrpointer, &lastElementIdx, &totalElements);
}
printQueue(arr, lastElementIdx);
for (int i = 1; i < 21; i++) {
enqueue(arrpointer, &lastElementIdx, &totalElements, i);
}
printQueue(arr, lastElementIdx);
free(arr);
return EXIT_SUCCESS;
}
When you expand or contract the storage for your queue, you need to provide a pointer to the storage back to the caller. This is because realloc() does not necessarily resize a memory block in-place -- it may create a new, differently sized block elsewhere. It is permitted to do so even when it resizes to a smaller block, not only when it resizes to a larger one.
Your usage of variable temp gives the appearance that you are aware of this issue, but as #DerkHermann first observed, you mishandle the resulting pointer. Perhaps you meant to write something along the lines of
arr = temp;
instead. Even that is not sufficient, however. C has only pass-by-value, so if you modify the value of function parameter arr, that modification is visible only in the function (which receives in arr a copy of the value the caller passes). In the event that realloc() allocates a new block, that leaves the caller with an invalid pointer.
If you want your enqueue() and dequeue() functions to be able to resize the storage for the queue, then you must pass the pointer to that storage indirectly. The most straightforward way of doing that, given where you are now, would be to pass a double pointer, so that you can modify its referrent:
void enqueue(int **arr, int* lastElementIdx, size_t* totalElements, int element) {
/* ... */
*arr = temp;
/* ... */
}
Observe, however, that you are passing three separate pointers that among them represent the state of the queue. It would be cleaner to create a struct type that combines those details in one package, and to pass a pointer to an object of that type:
struct queue {
int *arr;
size_t capacity;
size_t last_element_index;
};
void enqueue(struct queue *queue, int element) {
/* ... */
queue->arr = temp;
/* ... */
}
Maybe it's not the only problem, but at least the following line does not what you seem to expect:
*temp = *arr;
It looks as if you want to replace arr with the result of the realloc, delivering it back to the calling function (as with your other inout arguments). But, arr is not an inout argument: It is an array of integers, not a pointer to an array of integers. What you are actually doing with your above line of code is to copy the first element of arr to the newly allocated memory range. That newly allocated memory range temp, then, is nevertheless forgotten, creating a memory leak.
Adding a double pointer to reallocate the space in the right places, changing the comparing function with size_t totalElements and fixing a few additional mistakes finally did the trick.
#include <stdio.h>
#include <stdlib.h>
void enqueue(int **arr, int* lastElementIdx, size_t* totalElements, int element) {
if (*lastElementIdx + 1 >= (int)(*totalElements) - 1) { // check if memorry is sufficient, otherwise double
*totalElements *= 2;
int* temp = realloc(*arr, (*totalElements * sizeof(int)));
if (temp == NULL) { // just in case realloc fails
printf("Allocation error\n");
} else {
*arr = temp;
}
}
if (*lastElementIdx + 1 <= (int)(*totalElements) - 1) {
*lastElementIdx += 1; // once everything is done and if there is now enough space: add element
(*arr)[*lastElementIdx] = element;
}
}
int dequeue(int **arr, int* lastElementIdx, size_t* totalElements) {
if (*lastElementIdx > -1) { // if queue is not empty...
int deleted = (*arr)[0]; // save deleted value first (in case it's still needed)
for (int i = 0; i <= *lastElementIdx; i++) { // shift all elements
(*arr)[i] = (*arr)[i + 1];
}
*lastElementIdx -= 1; // index is now decreased by 1
if (((*totalElements / 2) >= 10) && ((*lastElementIdx + 1) < (*totalElements / 2))) { // cut memory in half if not needed
*totalElements /= 2;
int* temp = realloc(*arr, (*totalElements * sizeof(int)));
if (temp == NULL) { // in case realloc fails
printf("Allocation error\n");
return 0;
} else {
*arr = temp;
}
}
return deleted;
} else { // if queue is empty, print that there's nothing to dequeue
printf("There are no elements inside the queue\n");
return 0;
}
}
void printQueue(int arr[], int lastElementIdx) {
for (int i = 0; i <= lastElementIdx; i++) { // print entire queue
printf("[%d] = %d\n", i, arr[i]);
}
printf("\n");
}
int main (void) {
size_t totalElements = 10; // number of needed elements at the time
int lastElementIdx = -1; // index of last element in queue at the time
int *arr = calloc(totalElements, sizeof(int));
int **arrpointer = &arr;
for (int i = 1; i < 101; i++) {
enqueue(arrpointer, &lastElementIdx, &totalElements, i);
}
printQueue(arr, lastElementIdx);
for (int i = 0; i < 102; i++) {
dequeue(arrpointer, &lastElementIdx, &totalElements);
}
printQueue(arr, lastElementIdx);
for (int i = 1; i < 21; i++) {
enqueue(arrpointer, &lastElementIdx, &totalElements, i);
}
printQueue(arr, lastElementIdx);
free(arr);
return EXIT_SUCCESS;
}