Home > Software design >  Use realloc() in function
Use realloc() in function

Time:11-10

#include <stdio.h>
#include <stdlib.h>

void Increase(int *array1,int *Nums) {
    int*array2 = realloc(array1,(*Nums 1)*sizeof(int));
    array2[*Nums] = 13;
    array2[*Nums-1] = 14;
      (*Nums);
}


int main() {
    int NumOfElements=0,i;
    int*array=(int*)malloc(0*sizeof(int));
    Increase(array,&NumOfElements);
    for(i=0;i<NumOfElements;i  ) {
        printf("%d  ", array[i]);
    }
    free(array);
}

How many elements will be in the array in main() if I run this program?

Does the Increase() function increase the number of memory cells of the array in main(), or will the array in main() still just have 0 memory cells?

CodePudding user response:

From the realloc manual page:

The realloc() function returns a pointer to the newly allocated memory, which is suitably aligned for any kind of variable and may be different from ptr, or NULL if the request fails.

... so the answer to your question will depend on whether the call to realloc() was able to change the memory-allocation's size in-place, or not.

If realloc() was able to do an in-place resize (e.g. because the heap had allocated a larger-than-necessary array for the original malloc() call, allowing realloc() to simply mark some of the extra bytes in the buffer as in-use), then realloc() would return the same pointer that was passed in to it as an argument, which is the same memory-location that main() points to via array.

On the other hand, if realloc() wasn't able to do an in-place resize, then realloc() would be forced to allocate a newer/larger array, copy over the contents of the old array, free() the old array, and return the pointer to the larger array. In that case, array2 would point to a different location in memory than array and array1, and (worse), after Increase() returns, main() would invoke undefined behavior by dereferencing array, which is (at that point) a dangling pointer because realloc() freed the memory it used to point to.

CodePudding user response:

I think one is intending to implement a common container known a dynamic array for use in a stack (or similar structure.)

#include <stddef.h>

struct int_stack { int *data; size_t size, capacity; };

struct int_stack int_stack(void);
void int_stack_(struct int_stack *);
int *int_stack_new(struct int_stack *);

This is what I'd use as int_stack.h. Notice that it's logical size and it's capacity are not necessarily the same, but size <= capacity.

#include "int_stack.h"
#include <stdlib.h>
#include <errno.h>

/** Initialises `s` to idle. */
struct int_stack int_stack(void) {
    struct int_stack s;
    s.data = 0;
    s.capacity = s.size = 0;
    return s;
}

/** Destroys `s`; returns it idle. */
void int_stack_(struct int_stack *const s) {
    free(s->data);
    *s = int_stack();
}

/** Ensures `min_capacity` of `s`. Returns success, otherwise, `errno` will be
 set. */
static int int_stack_reserve(struct int_stack *const s, const size_t min) {
    size_t c0;
    int *data;
    const size_t max_size = (size_t)-1 / sizeof *s->data, min_size = 3;
    if(s->data) {
        if(min <= s->capacity) return 1;
        c0 = s->capacity < min_size ? min_size : s->capacity;
    } else { /* Idle. */
        if(!min) return 1;
        c0 = min_size;
    }
    if(min > max_size) return errno = ERANGE, 0;
    /* `c_n = a1.625^n`, approximation golden ratio `\phi ~ 1.618`. */
    while(c0 < min) {
        size_t c1 = c0   (c0 >> 1)   (c0 >> 3);
        if(c0 > c1) { c0 = max_size; break; }
        c0 = c1;
    }
    if(!(data = realloc(s->data, sizeof *s->data * c0)))
        { if(!errno) errno = ERANGE; return 0; }
    s->data = data, s->capacity = c0;
    return 1;
}

/** Increases the capacity of `s` to at least `n` elements beyond the size.
 Returns the start of the buffered space at the back of the array or null and
 `errno`. */
static int *int_stack_buffer(struct int_stack *const s, const size_t n) {
    if(s->size > (size_t)-1 - n) { errno = ERANGE; return 0; } /* Unlikely. */
    return int_stack_reserve(s, s->size   n) && s->data ? s->data   s->size : 0;
}

/** Adds `n` elements to the back of `s` and returns a pointer to the elements.
 Null indicates an error and `errno` will be set. */
static int *int_stack_append(struct int_stack *const s, const size_t n) {
    int *buffer;
    if(!(buffer = int_stack_buffer(s, n))) return 0;
    return s->size  = n, buffer;
}

/** Adds one new element of `s` and returns it as an uninitialized pointer or
 null and `errno`. */
int *int_stack_new(struct int_stack *const s) { return int_stack_append(s, 1); }

This is an example of what I'd use as the implementation int_stack.c. The function int_stack_reserve is where the realloc is called once the size reaches the capacity. A temporary data is assigned the realloc; this is checked for error, then assigned into s->data. Reserving a geometrically increasing capacity will avoid the cost of expanding each time. Thus, the array will have amortized cost of O(n) to insert n elements.

#include <stdio.h>
#include <stdlib.h>
#include "int_stack.h"

int main(void) {
    int status = EXIT_SUCCESS;
    int *e1, *e2;
    struct int_stack stack = int_stack();
    if(!(e1 = int_stack_new(&stack)) || !(e2 = int_stack_new(&stack))) {
        status = EXIT_FAILURE;
        perror("stack");
    } else {
        *e1 = 13;
        *e2 = 14;
        for(size_t i=0; i<stack.size; i  ) {
            printf("%d  ", stack.data[i]);
        }
        fputc('\n', stdout);
    }
    int_stack_(&stack);
    return status;
}

Instead of a fixed-size, we now have unlimited size, but one has to check for out-of-memory condition.

  • Related