Sean Payne
Sean Payne

Reputation: 1961

C realloc() Invalid Pointer Error even though malloc was used

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

Answers (1)

4386427
4386427

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

Related Questions