Search code examples
cmemoryrealloc

C Array Question - Strange memory issue with realloc'd memory


I don't exactly know how to explain this problem. It's extremely random.

I've got some code posted here: http://pastebin.com/d2vzas5S

#include <unistd.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#include <assert.h>

char * s1 = "hello";

struct gdarray {
    ssize_t idx;
    size_t incr;
    size_t total;
    size_t used;
    size_t size;
    void * flex;
};
typedef struct gdarray gdarray;

gdarray * gdarray_create(size_t size, size_t incr) {
    gdarray * array = calloc(1,sizeof(gdarray));
    array->idx = -1;
    array->used = 0;
    array->total = incr;
    array->incr = incr;
    array->size = size;
    array->flex = calloc(incr,size);
    return array;
}

void gdarray_add_pntr(gdarray * array, void * pntr) {
    if(!array || !array->flex) return;
    size_t total = array->total;
    ssize_t idx = array->idx+1;
    ssize_t idx_1 = idx+1;
    void ** flex = array->flex;
    if(total < idx_1) {
        total = idx+array->incr;
        void * tmp = realloc(array->flex,array->size * total);
        if(!tmp) {
            perror("gdarray_add_pntr.!tmp");
            return;
        }
        char * cmpcm = (char *)&((char **)tmp)[idx];
        memset(cmpcm,0,(total - array->total) - 1);
        array->flex = tmp;
        array->total = total;
        flex = tmp;
    }
    if(array->used < idx_1) array->used = idx_1;
    array->idx = idx;
    if(flex[idx]) {
        printf("this should never get called!\n");
        //free(flex[idx]); - this free needs to happen ONLY if there is
        //indeed a pointer at flex[idx] - but this branch is entered
        //randomly it seems - I can't figure out why this branch is
        //entered
    }
    flex[idx] = pntr;
}

void gdarray_dealloc(gdarray * array, bool free_array,
    bool free_flex, bool free_entries)
{
    if(free_entries) {
        size_t i = 0;
        size_t c = array->used;
        void ** flex = (void **)array->flex;
        for(;i<c;i++) if(flex[i]) free(flex[i]);
    }
    if(free_flex) free(array->flex);
    if(free_array) free(array);
}

int main(int argc, char ** argv) {
    gdarray * array = gdarray_create(sizeof(char *),4);
    gdarray_add_pntr(array,strdup(s1));
    gdarray_add_pntr(array,strdup(s1));
    gdarray_add_pntr(array,strdup(s1));
    gdarray_add_pntr(array,strdup(s1));
    gdarray_add_pntr(array,strdup(s1));
    gdarray_add_pntr(array,strdup(s1));
    gdarray_add_pntr(array,strdup(s1));
    gdarray_add_pntr(array,strdup(s1));
    gdarray_dealloc(array,true,true,true);

    gdarray * array2 = gdarray_create(sizeof(char *),2);
    gdarray_add_pntr(array2,strdup(s1));
    gdarray_add_pntr(array2,strdup(s1));
    gdarray_add_pntr(array2,strdup(s1));
    gdarray_add_pntr(array2,strdup(s1));
    gdarray_dealloc(array2,true,true,true);
    return 0;
}

The problem is at line 54. Technically the if statement shouldn't ever be entered based on the test code in main(). Because the test code is adding pointers to new slots in the array, deallocing, and doing it again.

What's strange is that the if statement is evaluating to true for the second test of the array (array2) in main when it clearly shouldn't be.

I must be missing something completely obvious - or there's some strange stuff going on with the heap / stack that I'm not aware of.

Any ideas?


Solution

  • I believe I got it. my memset after the realloc was only clearing n bytes, when it should of been clearing n*size bytes.