Reputation: 1961
I have a dynamic array implementation I'm working on for generic types. My understanding is that the invalid pointer error for realloc is usually caused by not using malloc to allocate the original pointer, however, I am using malloc.
Here is my array.h code
#include <stdlib.h>
#include <stdint.h>
#include <inttypes.h>
struct array {
void *elems;
size_t obj_size;
size_t size;
size_t capacity;
};
struct array* new_array(size_t objsize);
int push_back(struct array* a, const void* value);
Array.c
#include "array.h"
#include <stdio.h>
#include <string.h>
#define INITIAL_SIZE (1)
#define ARR_AT(a, i) ((void *) ((char *) (a)->elems + (i) * (a)->obj_size))
struct array* new_array(size_t objsize) {
struct array* a;
a = malloc(sizeof a + INITIAL_SIZE * objsize);
if (!a) { return NULL; }
a->elems = malloc(objsize);
a->capacity = 1;
a->size = 0;
return a;
}
int push_back(struct array* a, const void* value) {
if (a->size == a->capacity) {
void* temp = realloc(a->elems, a->obj_size * a->capacity * 2);
a->elems = temp;
if (!a) { return -1; }
a->capacity = a->capacity * 2;
}
memcpy(ARR_AT(a, a->size), value, a->obj_size);
a->size++;
return 0;
}
main.c
#include "array.h"
#include <stdio.h>
int main(void) {
struct array* a = new_array(4);
uint32_t* b = (uint32_t*) 3;
push_back(a, b);
printf("Size: %ld \n", a->size);
for (int i = 0; i < 30; i++) {
push_back(a, b + i);
printf("Size: %ld \n", a->size);
}
return 0;
}
I've been trying to fix this error, but my C skills are subpar. What am I missing here?
Upvotes: 0
Views: 743
Reputation: 44256
Your malloc
is wrong:
a = malloc(sizeof a + INITIAL_SIZE * objsize);
\------/ \----------------------/
sizeof pointer some extra bytes
This does not allocate space for a struct array
. It allocates memory corresponding to the size of a pointer and some extra bytes (here 1*4).
On my system the above allocation is for 12 bytes but struct array
requires 32 bytes.
So the allocated memory can't hold a struct array
and you are accessing memory that isn't assigned to you. Then anything may happen.
It's a bit unclear what you are trying to achieve with this malloc
. The "normal way" is simply:
a = malloc(sizeof *a); // Allocate a single 'struct array'
And you also need to save objsize
like
a->obj_size = objsize;
in the new_array
function. If you don't the realloc
uses an uninitialized variable:
realloc(a->elems, a->obj_size * a->capacity * 2);
\---------/
Currently uninitialized
Further this is very strange:
uint32_t* b = (uint32_t*) 3; // Converting the number 3 to a pointer !?
push_back(a, b); // and then push_back uses that pointer
// in memcpy... that has to fail...
I wonder if you really want something like:
uint32_t b = 3; // Make an ordinary uint variable with value 3
push_back(a, &b); // Pass a pointer to the variable b so that
// the raw data representing the value 3 can be
// copied to "struct array"->elems
As a final note:
Sometimes you check for NULL after malloc
and sometimes you don't. Either do it every time or don't do it at all.
Upvotes: 5