0

I am having problems wrapping my brain around proper use of pointers with arrays in C.

The project I am working on is building a simple Dynamic Array and the functions to go along with it. However I do not seem to be able to find the correct syntax/functions to update the array size dynamically. Here is the relevent code:

Array Creation:

    struct DArray
{
    TYPE *data;     /* pointer to the data array */
    int size;       /* Number of elements in the array */
    int capacity;   /* capacity ofthe array */
};


void initDArray(DArray *v, int capacity)
{
    assert(capacity > 0);
    assert(v!= 0);
    v->data = (TYPE *) malloc(sizeof(TYPE) * capacity);
    assert(v->data != 0);
    v->size = 0;
    v->capacity = capacity;
}


DArray* createDArray(int cap)
{
    assert(cap > 0);
    DArray *r = (DArray *)malloc(sizeof( DArray));
    assert(r != 0);
    initDArray(r,cap);
    return r;
}

And the problem bit, in its current non-working form:

    void _DArraySetCapacity(DArray *v, int newCap)
{

    TYPE * newptr = createDArray(newCap);
    newptr = (TYPE *) malloc(sizeof(TYPE) * newCap);
    v->capacity = newCap;
    v->data = newptr;

}

My method being to create a temporary pointer with increased memory allocation then copy the existing date to it and point the data pointer at the new location. I suspect I may be looking at the issue in entirely the wrong way.

Any help tips or pointers (pun intended) would be appreciated. Thanks in advance.

Derkus
  • 15
  • 6
  • 3
    You may want to read about the [`realloc`](http://en.cppreference.com/w/c/memory/realloc) function. Also, you [should not cast the result of `malloc` (or `realloc`)](http://stackoverflow.com/questions/605845/do-i-cast-the-result-of-malloc/605858#605858). – Some programmer dude Apr 15 '15 at 23:55
  • It looks like you are using a C++ compiler to compile C. (struct definitions are not typedefs in C, in C++ they are) – wildplasser Apr 15 '15 at 23:58
  • so you fixed your assert problem then - did you update the question or accept an answer? Just looked - no you didnt – pm100 Apr 16 '15 at 00:06
  • 2
    By the way, don't use `assert` as a method to check for errors from functions, in "release" builds the `assert` *preprocessor macro* does nothing. It might be okay for simple school or book assignments, but using it instead of a proper `if` check in real life is a big no-no. Better learn it early to get good habits going from the start. – Some programmer dude Apr 16 '15 at 00:11

4 Answers4

1

What about using realloc?

The C library function void *realloc(void *ptr, size_t size) attempts to resize the memory block pointed to by ptr that was previously allocated with a call to malloc or calloc.

Paul Nikonowicz
  • 3,883
  • 21
  • 39
1

Fortunately, you don't need to move memory around yourself: realloc does that for you.

In your case a typical usage would be:

v->capacity = newCap;
v->data = realloc(v->data, newCap * sizeof *v->data); /* same as sizeof(TYPE) */

Note that you don't have to (and shouldn't) cast the result of malloc/calloc or realloc.

Kninnug
  • 7,992
  • 1
  • 30
  • 42
  • While you're technically correct, assigning back to the original pointer you reallocate can lead to memory leaks. Think about the case when `realloc` fail, then the original pointer will be lost. – Some programmer dude Apr 16 '15 at 00:01
  • @JoachimPileborg quite right, but as OP is using `assert` to test his `malloc`s he can also just use `assert` on the result of `realloc` and bail (albeit uncleanly) out of the program entirely. – Kninnug Apr 16 '15 at 00:05
0

Use realloc()!

See this question and answer to better understand realloc and how its used.

Community
  • 1
  • 1
alenz316
  • 613
  • 3
  • 10
0

Dynamic generic arrays in C are unexpectedly easy.

Appending/inserting elements:

size_t size = 0, count = 0;
mytype_t *items; // array of mytype_t

mytype_t foo1, foo2, foo3;

carray_grow(items, size, count += 1); // grow to 1
items[0] = foo3;

carray_grow(items, size, count += 2); // grow to 3
carray_insert(items, count, 0, 2); // shift 2 up at 0
items[0] = foo1;
items[1] = foo2;

Removing elements:

carray_remove(items, count, 0, 2); // remove 2 at 0
carray_grow(items, size, count -= 2); // shrink to 1

Free it completely?

carray_grow(items, size, count = 0);

Backend (few defines and single function ahead):

int carray_xgrow(void **datap, size_t *sizep, size_t esize, size_t count);
// 0 or -1/errno(realloc)

#define carray_grow(array, size, count) carray_xgrow((void **)&(array), &(size), sizeof((array)[0]), count)
// 0 or -1/errno(realloc)

#define carray_move(array, from, to, n) memmove(&(array)[to], &(array)[from], sizeof((array)[0]) * (n))
#define carray_insert(array, count, i, n) carray_move(array, i, (i)+(n), (count)-(i)-(n))
#define carray_remove(array, count, i, n) carray_move(array, (i)+(n), i, (count)-(i)-(n))

int
carray_xgrow(void **datap, size_t *sizep, size_t esize, size_t count)
{
    assert(datap != NULL);
    assert(sizep != NULL);
    assert(esize > 0);

    size_t size = *sizep;
    {
        size_t cap = size / esize;

        if (cap >= count * 2) // regrow at 1/2
            cap = 0;

        while (cap < count) // grow by 3/2
            cap = (cap < 2) ? (cap + 1) : (cap * 3 / 2);

        size = cap * esize;
    }

    if (size != *sizep) {
        if (size) {
            void *data = realloc(*datap, size);
            if (data == NULL) return -1;
            *datap = data;
            *sizep = size;
        }
        else {
            free(*datap);
            *datap = NULL;
            *sizep = 0;
        }
    }
    return 0;
}

(Note size in bytes, but cap and count in elements.)

EDIT: removed minor dependencies

user3125367
  • 2,920
  • 1
  • 17
  • 17